Date formatting per workspace member settings (#6408)

Implement date formatting per workspace member settings

We'll need another round to maybe initialize all workspaces on the
default settings.

For now the default behavior is to take system settings if nothing is
found in DB.

---------

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Lucas Bordeau
2024-07-30 14:52:10 +02:00
committed by GitHub
parent 45ebb0b824
commit ccf4d1eeec
64 changed files with 1176 additions and 165 deletions

View File

@ -1,4 +1,3 @@
import { useContext, useMemo, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
@ -8,13 +7,14 @@ import {
startOfMonth,
} from 'date-fns';
import { AnimatePresence, motion } from 'framer-motion';
import { useContext, useMemo, useState } from 'react';
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEventEnded';
import { hasCalendarEventStarted } from '@/activities/calendar/utils/hasCalendarEventStarted';
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
import { TimelineCalendarEvent } from '~/generated/graphql';
type CalendarCurrentEventCursorProps = {
calendarEvent: TimelineCalendarEvent;

View File

@ -5,7 +5,7 @@ import { differenceInSeconds, endOfDay, format } from 'date-fns';
import { CalendarEventRow } from '@/activities/calendar/components/CalendarEventRow';
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
import { TimelineCalendarEvent } from '~/generated/graphql';
type CalendarDayCardContentProps = {
calendarEvents: TimelineCalendarEvent[];

View File

@ -3,7 +3,13 @@ import styled from '@emotion/styled';
import { format } from 'date-fns';
import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { Avatar, AvatarGroup, IconArrowRight, IconLock } from 'twenty-ui';
import {
Avatar,
AvatarGroup,
IconArrowRight,
IconLock,
isDefined,
} from 'twenty-ui';
import { CalendarCurrentEventCursor } from '@/activities/calendar/components/CalendarCurrentEventCursor';
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
@ -14,9 +20,10 @@ import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEv
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { Card } from '@/ui/layout/card/components/Card';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
import { CalendarChannelVisibility } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import {
CalendarChannelVisibility,
TimelineCalendarEvent,
} from '~/generated-metadata/graphql';
type CalendarEventRowProps = {
calendarEvent: TimelineCalendarEvent;

View File

@ -1,6 +1,6 @@
import { createContext } from 'react';
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
import { TimelineCalendarEvent } from '~/generated/graphql';
type CalendarContextValue = {
calendarEventsByDayTime: Record<number, TimelineCalendarEvent[] | undefined>;

View File

@ -1,41 +1,87 @@
import { act, renderHook } from '@testing-library/react';
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { CalendarChannelVisibility } from '~/generated/graphql';
import {
CalendarChannelVisibility,
TimelineCalendarEvent,
} from '~/generated/graphql';
const calendarEvents: CalendarEvent[] = [
const calendarEvents: TimelineCalendarEvent[] = [
{
id: '1234',
externalCreatedAt: '2024-02-17T20:45:43.854Z',
isFullDay: false,
startsAt: '2024-02-17T21:45:27.822Z',
visibility: CalendarChannelVisibility.Metadata,
__typename: 'CalendarEvent',
conferenceLink: {
primaryLinkUrl: 'https://meet.google.com/abc-def-ghi',
primaryLinkLabel: 'Google Meet',
__typename: 'LinksMetadata',
},
conferenceSolution: 'GoogleMeet',
description: 'Description',
endsAt: '2024-02-17T22:45:27.822Z',
isCanceled: false,
location: 'Location',
participants: [],
title: 'Title',
__typename: 'TimelineCalendarEvent',
},
{
id: '5678',
externalCreatedAt: '2024-02-18T19:43:37.854Z',
isFullDay: false,
startsAt: '2024-02-18T21:43:27.754Z',
visibility: CalendarChannelVisibility.ShareEverything,
__typename: 'CalendarEvent',
conferenceLink: {
primaryLinkUrl: 'https://meet.google.com/abc-def-ghi',
primaryLinkLabel: 'Google Meet',
__typename: 'LinksMetadata',
},
conferenceSolution: 'GoogleMeet',
description: 'Description',
endsAt: '2024-02-17T22:45:27.822Z',
isCanceled: false,
location: 'Location',
participants: [],
title: 'Title',
__typename: 'TimelineCalendarEvent',
},
{
id: '91011',
externalCreatedAt: '2024-02-19T20:45:20.854Z',
isFullDay: true,
startsAt: '2024-02-19T22:05:27.653Z',
visibility: CalendarChannelVisibility.Metadata,
__typename: 'CalendarEvent',
conferenceLink: {
primaryLinkUrl: 'https://meet.google.com/abc-def-ghi',
primaryLinkLabel: 'Google Meet',
__typename: 'LinksMetadata',
},
conferenceSolution: 'GoogleMeet',
description: 'Description',
endsAt: '2024-02-17T22:45:27.822Z',
isCanceled: false,
location: 'Location',
participants: [],
title: 'Title',
__typename: 'TimelineCalendarEvent',
},
{
id: '121314',
externalCreatedAt: '2024-02-20T20:45:12.854Z',
isFullDay: true,
startsAt: '2024-02-20T23:15:23.150Z',
visibility: CalendarChannelVisibility.ShareEverything,
__typename: 'CalendarEvent',
conferenceLink: {
primaryLinkUrl: 'https://meet.google.com/abc-def-ghi',
primaryLinkLabel: 'Google Meet',
__typename: 'LinksMetadata',
},
conferenceSolution: 'GoogleMeet',
description: 'Description',
endsAt: '2024-02-17T22:45:27.822Z',
isCanceled: false,
location: 'Location',
participants: [],
title: 'Title',
__typename: 'TimelineCalendarEvent',
},
];

View File

@ -1,21 +1,14 @@
import { useMemo, useState } from 'react';
import { getYear, isThisMonth, startOfDay, startOfMonth } from 'date-fns';
import { useMemo, useState } from 'react';
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { findUpcomingCalendarEvent } from '@/activities/calendar/utils/findUpcomingCalendarEvent';
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
import { TimelineCalendarEvent } from '~/generated/graphql';
import { groupArrayItemsBy } from '~/utils/array/groupArrayItemsBy';
import { isDefined } from '~/utils/isDefined';
import { sortDesc } from '~/utils/sort';
type CalendarEventGeneric = Omit<
CalendarEvent,
'participants' | 'externalCreatedAt' | '__typename'
>;
export const useCalendarEvents = <T extends CalendarEventGeneric>(
calendarEvents: T[],
) => {
export const useCalendarEvents = (calendarEvents: TimelineCalendarEvent[]) => {
const calendarEventsByDayTime = groupArrayItemsBy(
calendarEvents,
(calendarEvent) =>
@ -36,14 +29,14 @@ export const useCalendarEvents = <T extends CalendarEventGeneric>(
const monthTimesByYear = groupArrayItemsBy(sortedMonthTimes, getYear);
const getPreviousCalendarEvent = (calendarEvent: T) => {
const getPreviousCalendarEvent = (calendarEvent: TimelineCalendarEvent) => {
const calendarEventIndex = calendarEvents.indexOf(calendarEvent);
return calendarEventIndex < calendarEvents.length - 1
? calendarEvents[calendarEventIndex + 1]
: undefined;
};
const getNextCalendarEvent = (calendarEvent: T) => {
const getNextCalendarEvent = (calendarEvent: TimelineCalendarEvent) => {
const calendarEventIndex = calendarEvents.indexOf(calendarEvent);
return calendarEventIndex > 0
? calendarEvents[calendarEventIndex - 1]

View File

@ -6,6 +6,8 @@ import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { UserContext } from '@/users/contexts/UserContext';
import { useContext } from 'react';
import {
formatToHumanReadableDay,
formatToHumanReadableMonth,
@ -107,6 +109,8 @@ export const EventCardCalendarEvent = ({
const { openCalendarEventRightDrawer } = useOpenCalendarEventRightDrawer();
const { timeZone } = useContext(UserContext);
if (isDefined(error)) {
const shouldHideMessageContent = error.graphQLErrors.some(
(e) => e.extensions?.code === 'FORBIDDEN',
@ -138,12 +142,14 @@ export const EventCardCalendarEvent = ({
throw new Error("Can't render a calendarEvent without a start date");
}
const startsAtMonth = formatToHumanReadableMonth(startsAtDate);
const startsAtMonth = formatToHumanReadableMonth(startsAtDate, timeZone);
const startsAtDay = formatToHumanReadableDay(startsAtDate);
const startsAtDay = formatToHumanReadableDay(startsAtDate, timeZone);
const startsAtHour = formatToHumanReadableTime(startsAtDate);
const endsAtHour = endsAtDate ? formatToHumanReadableTime(endsAtDate) : null;
const startsAtHour = formatToHumanReadableTime(startsAtDate, timeZone);
const endsAtHour = endsAtDate
? formatToHumanReadableTime(endsAtDate, timeZone)
: null;
return (
<StyledEventCardCalendarEventContainer