From 87dc95c594a2330b7507a6ab1ce2cf8dff2a8e8c Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Mon, 8 Jul 2024 09:11:47 +0200 Subject: [PATCH] Refactor Calendar Settings into tabs (#6153) This PR migrates Calendar Settings into Tabs: image --- packages/twenty-front/src/App.tsx | 5 - ...SettingsAccountsCalendarChannelDetails.tsx | 118 +++++++++++++++ ...tingsAccountsCalendarChannelsContainer.tsx | 75 +++++++++ ...ettingsAccountsCalendarChannelsGeneral.tsx | 93 ++++++++++++ ...ttingsAccountsCalendarChannelsListCard.tsx | 95 ------------ ...AccountsCalendarChannelDetails.stories.tsx | 30 ++++ ...ccountsCalendarChannelsGeneral.stories.tsx | 18 +++ ...countCalendarChannelsTabListComponentId.ts | 2 + .../accounts/SettingsAccountsCalendars.tsx | 110 +------------- .../SettingsAccountsCalendarsSettings.tsx | 143 ------------------ .../SettingsAccountsCalendars.stories.tsx | 129 ++++++++++++++-- ...tingsAccountsCalendarsSettings.stories.tsx | 61 -------- 12 files changed, 456 insertions(+), 423 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsGeneral.tsx delete mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelDetails.stories.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelsGeneral.stories.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId.ts delete mode 100644 packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendarsSettings.tsx delete mode 100644 packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendarsSettings.stories.tsx diff --git a/packages/twenty-front/src/App.tsx b/packages/twenty-front/src/App.tsx index fff38bef2..4ea9a6ad3 100644 --- a/packages/twenty-front/src/App.tsx +++ b/packages/twenty-front/src/App.tsx @@ -53,7 +53,6 @@ import { PaymentSuccess } from '~/pages/onboarding/PaymentSuccess'; import { SyncEmails } from '~/pages/onboarding/SyncEmails'; import { SettingsAccounts } from '~/pages/settings/accounts/SettingsAccounts'; import { SettingsAccountsCalendars } from '~/pages/settings/accounts/SettingsAccountsCalendars'; -import { SettingsAccountsCalendarsSettings } from '~/pages/settings/accounts/SettingsAccountsCalendarsSettings'; import { SettingsAccountsEmails } from '~/pages/settings/accounts/SettingsAccountsEmails'; import { SettingsNewAccount } from '~/pages/settings/accounts/SettingsNewAccount'; import { SettingsNewObject } from '~/pages/settings/data-model/SettingsNewObject'; @@ -179,10 +178,6 @@ const createRouter = (isBillingEnabled?: boolean) => path={SettingsPath.AccountsCalendars} element={} /> - } - /> } diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx new file mode 100644 index 000000000..5ea723016 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx @@ -0,0 +1,118 @@ +import { CalendarChannel } from '@/accounts/types/CalendarChannel'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { SettingsAccountsEventVisibilitySettingsCard } from '@/settings/accounts/components/SettingsAccountsCalendarVisibilitySettingsCard'; +import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia'; +import { SettingsAccountsToggleSettingCard } from '@/settings/accounts/components/SettingsAccountsToggleSettingCard'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { Section } from '@react-email/components'; +import { H2Title, IconRefresh, IconUser } from 'twenty-ui'; +import { CalendarChannelVisibility } from '~/generated-metadata/graphql'; + +const StyledCardMedia = styled(SettingsAccountsCardMedia)` + height: ${({ theme }) => theme.spacing(6)}; +`; + +const StyledDetailsContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(6)}; + padding-top: ${({ theme }) => theme.spacing(6)}; +`; + +type SettingsAccountsCalendarChannelDetailsProps = { + calendarChannel: Pick< + CalendarChannel, + 'id' | 'visibility' | 'isContactAutoCreationEnabled' | 'isSyncEnabled' + >; +}; + +export const SettingsAccountsCalendarChannelDetails = ({ + calendarChannel, +}: SettingsAccountsCalendarChannelDetailsProps) => { + const theme = useTheme(); + + const { updateOneRecord } = useUpdateOneRecord({ + objectNameSingular: CoreObjectNameSingular.CalendarChannel, + }); + + const handleVisibilityChange = (value: CalendarChannelVisibility) => { + updateOneRecord({ + idToUpdate: calendarChannel.id, + updateOneRecordInput: { + visibility: value, + }, + }); + }; + + const handleContactAutoCreationToggle = (value: boolean) => { + updateOneRecord({ + idToUpdate: calendarChannel.id, + updateOneRecordInput: { + isContactAutoCreationEnabled: value, + }, + }); + }; + + const handleSyncEventsToggle = (value: boolean) => { + updateOneRecord({ + idToUpdate: calendarChannel.id, + updateOneRecordInput: { + isSyncEnabled: value, + }, + }); + }; + return ( + +
+ + +
+
+ + + + + } + title="Auto-creation" + value={!!calendarChannel.isContactAutoCreationEnabled} + onToggle={handleContactAutoCreationToggle} + /> +
+
+ + + + + } + title="Sync events" + value={!!calendarChannel.isSyncEnabled} + onToggle={handleSyncEventsToggle} + /> +
+
+ ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx new file mode 100644 index 000000000..49beddbac --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx @@ -0,0 +1,75 @@ +import { useRecoilValue } from 'recoil'; + +import { CalendarChannel } from '@/accounts/types/CalendarChannel'; +import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; +import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; +import { SettingsAccountsCalendarChannelDetails } from '@/settings/accounts/components/SettingsAccountsCalendarChannelDetails'; +import { SettingsAccountsCalendarChannelsGeneral } from '@/settings/accounts/components/SettingsAccountsCalendarChannelsGeneral'; +import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; +import { SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID } from '@/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId'; +import { TabList } from '@/ui/layout/tab/components/TabList'; +import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; + +export const SettingsAccountsCalendarChannelsContainer = () => { + const { activeTabIdState } = useTabList( + SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID, + ); + const activeTabId = useRecoilValue(activeTabIdState); + const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); + + const { records: accounts } = useFindManyRecords({ + objectNameSingular: CoreObjectNameSingular.ConnectedAccount, + filter: { + accountOwnerId: { + eq: currentWorkspaceMember?.id, + }, + }, + }); + + const { records: calendarChannels } = useFindManyRecords< + CalendarChannel & { + connectedAccount: ConnectedAccount; + } + >({ + objectNameSingular: CoreObjectNameSingular.CalendarChannel, + filter: { + connectedAccountId: { + in: accounts.map((account) => account.id), + }, + }, + }); + + const tabs = [ + ...calendarChannels.map((calendarChannel) => ({ + id: calendarChannel.id, + title: calendarChannel.handle, + })), + ]; + + if (!calendarChannels.length) { + return ; + } + + return ( + <> + + {calendarChannels.map((calendarChannel) => ( + <> + {calendarChannel.id === activeTabId && ( + + )} + + ))} + {false && activeTabId === 'general' && ( + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsGeneral.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsGeneral.tsx new file mode 100644 index 000000000..14ffba784 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsGeneral.tsx @@ -0,0 +1,93 @@ +import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard'; +import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext'; +import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; +import { SettingsAccountsCalendarDisplaySettings } from '@/settings/accounts/components/SettingsAccountsCalendarDisplaySettings'; +import styled from '@emotion/styled'; +import { Section } from '@react-email/components'; +import { addMinutes, endOfDay, min, startOfDay } from 'date-fns'; +import { useRecoilValue } from 'recoil'; +import { H2Title } from 'twenty-ui'; +import { + CalendarChannelVisibility, + TimelineCalendarEvent, +} from '~/generated-metadata/graphql'; + +const StyledGeneralContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(6)}; + padding-top: ${({ theme }) => theme.spacing(6)}; +`; + +export const SettingsAccountsCalendarChannelsGeneral = () => { + const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); + + const exampleStartDate = new Date(); + const exampleEndDate = min([ + addMinutes(exampleStartDate, 30), + endOfDay(exampleStartDate), + ]); + const exampleDayTime = startOfDay(exampleStartDate).getTime(); + const exampleCalendarEvent: TimelineCalendarEvent = { + id: '', + participants: [ + { + firstName: currentWorkspaceMember?.name.firstName || '', + lastName: currentWorkspaceMember?.name.lastName || '', + displayName: currentWorkspaceMember + ? [ + currentWorkspaceMember.name.firstName, + currentWorkspaceMember.name.lastName, + ].join(' ') + : '', + avatarUrl: currentWorkspaceMember?.avatarUrl || '', + handle: '', + personId: '', + workspaceMemberId: currentWorkspaceMember?.id || '', + }, + ], + endsAt: exampleEndDate.toISOString(), + isFullDay: false, + startsAt: exampleStartDate.toISOString(), + conferenceSolution: '', + conferenceLink: { + label: '', + url: '', + }, + description: '', + isCanceled: false, + location: '', + title: 'Onboarding call', + visibility: CalendarChannelVisibility.ShareEverything, + }; + + return ( + +
+ + +
+
+ + undefined, + updateCurrentCalendarEvent: () => {}, + }} + > + + +
+
+ ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx deleted file mode 100644 index e00f32398..000000000 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import styled from '@emotion/styled'; -import { useRecoilValue } from 'recoil'; -import { IconChevronRight, IconGoogleCalendar } from 'twenty-ui'; - -import { CalendarChannel } from '@/accounts/types/CalendarChannel'; -import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; -import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; -import { - SettingsAccountsSynchronizationStatus, - SettingsAccountsSynchronizationStatusProps, -} from '@/settings/accounts/components/SettingsAccountsSynchronizationStatus'; -import { SettingsListCard } from '@/settings/components/SettingsListCard'; -import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; - -const StyledRowRightContainer = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; -`; - -export const SettingsAccountsCalendarChannelsListCard = () => { - const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); - const navigate = useNavigate(); - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.CalendarChannel, - }); - - const { records: accounts, loading: accountsLoading } = - useFindManyRecords({ - objectNameSingular: CoreObjectNameSingular.ConnectedAccount, - filter: { - accountOwnerId: { - eq: currentWorkspaceMember?.id, - }, - }, - }); - - const { records: calendarChannels, loading: calendarChannelsLoading } = - useFindManyRecords< - CalendarChannel & { - connectedAccount: ConnectedAccount; - } - >({ - objectNameSingular: CoreObjectNameSingular.CalendarChannel, - skip: !accounts.length, - filter: { - connectedAccountId: { - in: accounts.map((account) => account.id), - }, - }, - recordGqlFields: generateDepthOneRecordGqlFields({ objectMetadataItem }), - }); - - if (!calendarChannels.length) { - return ; - } - - const calendarChannelsWithSyncStatus: (CalendarChannel & { - connectedAccount: ConnectedAccount; - } & SettingsAccountsSynchronizationStatusProps)[] = calendarChannels.map( - (calendarChannel) => ({ - ...calendarChannel, - syncStatus: calendarChannel.connectedAccount?.authFailedAt - ? 'FAILED' - : 'SUCCEEDED', - }), - ); - - return ( - calendarChannel.handle} - isLoading={accountsLoading || calendarChannelsLoading} - onRowClick={(calendarChannel) => - navigate(`/settings/accounts/calendars/${calendarChannel.id}`) - } - RowIcon={IconGoogleCalendar} - RowRightComponent={({ item: calendarChannel }) => ( - - - - - )} - /> - ); -}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelDetails.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelDetails.stories.tsx new file mode 100644 index 000000000..5ef756baf --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelDetails.stories.tsx @@ -0,0 +1,30 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { ComponentDecorator } from 'twenty-ui'; + +import { SettingsAccountsCalendarChannelDetails } from '@/settings/accounts/components/SettingsAccountsCalendarChannelDetails'; +import { CalendarChannelVisibility } from '~/generated/graphql'; + +const meta: Meta = { + title: + 'Modules/Settings/Accounts/CalendarChannels/SettingsAccountsCalendarChannelDetails', + component: SettingsAccountsCalendarChannelDetails, + decorators: [ComponentDecorator], + args: { + calendarChannel: { + id: '20202020-ef5a-4822-9e08-ce6e6a4dcb6a', + isContactAutoCreationEnabled: true, + isSyncEnabled: true, + visibility: CalendarChannelVisibility.ShareEverything, + }, + }, + argTypes: { + calendarChannel: { control: false }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + play: async () => {}, +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelsGeneral.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelsGeneral.stories.tsx new file mode 100644 index 000000000..49f279316 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsCalendarChannelsGeneral.stories.tsx @@ -0,0 +1,18 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { ComponentDecorator } from 'twenty-ui'; + +import { SettingsAccountsCalendarChannelsGeneral } from '@/settings/accounts/components/SettingsAccountsCalendarChannelsGeneral'; + +const meta: Meta = { + title: + 'Modules/Settings/Accounts/CalendarChannels/SettingsAccountsCalendarChannelsGeneral', + component: SettingsAccountsCalendarChannelsGeneral, + decorators: [ComponentDecorator], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + play: async () => {}, +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId.ts b/packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId.ts new file mode 100644 index 000000000..5d414496d --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId.ts @@ -0,0 +1,2 @@ +export const SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID = + 'settings-account-calendar-channels-tab-list'; diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx index 6ad0070f3..6d3d1c357 100644 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx @@ -1,85 +1,14 @@ -import { addMinutes, endOfDay, min, startOfDay } from 'date-fns'; -import { useRecoilValue } from 'recoil'; -import { H2Title, IconSettings } from 'twenty-ui'; +import { IconSettings } from 'twenty-ui'; -import { CalendarChannel } from '@/accounts/types/CalendarChannel'; -import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard'; -import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext'; -import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; -import { SettingsAccountsCalendarChannelsListCard } from '@/settings/accounts/components/SettingsAccountsCalendarChannelsListCard'; -import { SettingsAccountsCalendarDisplaySettings } from '@/settings/accounts/components/SettingsAccountsCalendarDisplaySettings'; +import { SettingsAccountsCalendarChannelsContainer } from '@/settings/accounts/components/SettingsAccountsCalendarChannelsContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; -import { CalendarChannelVisibility } from '~/generated/graphql'; -import { TimelineCalendarEvent } from '~/generated-metadata/graphql'; export const SettingsAccountsCalendars = () => { - const calendarSettingsEnabled = false; - const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); - const { records: accounts } = useFindManyRecords({ - objectNameSingular: CoreObjectNameSingular.ConnectedAccount, - filter: { - accountOwnerId: { - eq: currentWorkspaceMember?.id, - }, - }, - }); - - const { records: calendarChannels } = useFindManyRecords({ - objectNameSingular: CoreObjectNameSingular.CalendarChannel, - filter: { - connectedAccountId: { - in: accounts.map((account) => account.id), - }, - }, - }); - - const exampleStartDate = new Date(); - const exampleEndDate = min([ - addMinutes(exampleStartDate, 30), - endOfDay(exampleStartDate), - ]); - const exampleDayTime = startOfDay(exampleStartDate).getTime(); - const exampleCalendarEvent: TimelineCalendarEvent = { - id: '', - participants: [ - { - firstName: currentWorkspaceMember?.name.firstName || '', - lastName: currentWorkspaceMember?.name.lastName || '', - displayName: currentWorkspaceMember - ? [ - currentWorkspaceMember.name.firstName, - currentWorkspaceMember.name.lastName, - ].join(' ') - : '', - avatarUrl: currentWorkspaceMember?.avatarUrl || '', - handle: '', - personId: '', - workspaceMemberId: currentWorkspaceMember?.id || '', - }, - ], - endsAt: exampleEndDate.toISOString(), - isFullDay: false, - startsAt: exampleStartDate.toISOString(), - conferenceSolution: '', - conferenceLink: { - label: '', - url: '', - }, - description: '', - isCanceled: false, - location: '', - title: 'Onboarding call', - visibility: CalendarChannelVisibility.ShareEverything, - }; - return ( @@ -93,41 +22,8 @@ export const SettingsAccountsCalendars = () => { ]} />
- - +
- {!!calendarChannels.length && calendarSettingsEnabled && ( - <> -
- - -
-
- - undefined, - updateCurrentCalendarEvent: () => {}, - }} - > - - -
- - )}
); diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendarsSettings.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendarsSettings.tsx deleted file mode 100644 index 589b89acc..000000000 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendarsSettings.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useEffect } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; -import { H2Title, IconRefresh, IconSettings, IconUser } from 'twenty-ui'; - -import { CalendarChannel } from '@/accounts/types/CalendarChannel'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; -import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; -import { SettingsAccountsEventVisibilitySettingsCard } from '@/settings/accounts/components/SettingsAccountsCalendarVisibilitySettingsCard'; -import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia'; -import { SettingsAccountsToggleSettingCard } from '@/settings/accounts/components/SettingsAccountsToggleSettingCard'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; -import { AppPath } from '@/types/AppPath'; -import { SettingsPath } from '@/types/SettingsPath'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; -import { Section } from '@/ui/layout/section/components/Section'; -import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; -import { CalendarChannelVisibility } from '~/generated/graphql'; - -const StyledCardMedia = styled(SettingsAccountsCardMedia)` - height: ${({ theme }) => theme.spacing(6)}; -`; - -export const SettingsAccountsCalendarsSettings = () => { - const theme = useTheme(); - const navigate = useNavigate(); - - const { accountUuid: calendarChannelId = '' } = useParams(); - - const { record: calendarChannel, loading } = - useFindOneRecord({ - objectNameSingular: CoreObjectNameSingular.CalendarChannel, - objectRecordId: calendarChannelId, - }); - - const { updateOneRecord } = useUpdateOneRecord({ - objectNameSingular: CoreObjectNameSingular.CalendarChannel, - }); - - const handleVisibilityChange = (value: CalendarChannelVisibility) => { - updateOneRecord({ - idToUpdate: calendarChannelId, - updateOneRecordInput: { - visibility: value, - }, - }); - }; - - const handleContactAutoCreationToggle = (value: boolean) => { - updateOneRecord({ - idToUpdate: calendarChannelId, - updateOneRecordInput: { - isContactAutoCreationEnabled: value, - }, - }); - }; - - const handleSyncEventsToggle = (value: boolean) => { - updateOneRecord({ - idToUpdate: calendarChannelId, - updateOneRecordInput: { - isSyncEnabled: value, - }, - }); - }; - - useEffect(() => { - if (!loading && !calendarChannel) navigate(AppPath.NotFound); - }, [loading, calendarChannel, navigate]); - - if (!calendarChannel) return null; - - return ( - - - -
- - -
-
- - - - - } - title="Auto-creation" - value={!!calendarChannel.isContactAutoCreationEnabled} - onToggle={handleContactAutoCreationToggle} - /> -
-
- - - - - } - title="Sync events" - value={!!calendarChannel.isSyncEnabled} - onToggle={handleSyncEventsToggle} - /> -
-
-
- ); -}; diff --git a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx index d2134d2f4..81468b782 100644 --- a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx @@ -1,23 +1,19 @@ import { Meta, StoryObj } from '@storybook/react'; -import { within } from '@storybook/test'; +import { HttpResponse, graphql } from 'msw'; +import { SettingsAccountsCalendars } from '~/pages/settings/accounts/SettingsAccountsCalendars'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; -import { SettingsPath } from '@/types/SettingsPath'; import { PageDecorator, PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { sleep } from '~/utils/sleep'; - -import { SettingsAccountsCalendars } from '../SettingsAccountsCalendars'; const meta: Meta = { title: 'Pages/Settings/Accounts/SettingsAccountsCalendars', component: SettingsAccountsCalendars, decorators: [PageDecorator], args: { - routePath: getSettingsPagePath(SettingsPath.AccountsCalendars), + routePath: '/settings/accounts/calendars', }, parameters: { layout: 'fullscreen', @@ -29,11 +25,120 @@ export default meta; export type Story = StoryObj; -export const Default: Story = { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - sleep(100); +export const NoConnectedAccount: Story = {}; - await canvas.findByText('Calendar settings'); +export const TwoConnectedAccounts: Story = { + parameters: { + msw: { + handlers: [ + ...graphqlMocks.handlers, + graphql.query('FindManyConnectedAccounts', () => { + return HttpResponse.json({ + data: { + connectedAccounts: { + __typename: 'ConnectedAccountConnection', + totalCount: 1, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + startCursor: '', + endCursor: '', + }, + edges: [ + { + __typename: 'ConnectedAccountEdge', + cursor: '', + node: { + __typename: 'ConnectedAccount', + accessToken: '', + refreshToken: '', + updatedAt: '2024-07-03T20:03:35.064Z', + createdAt: '2024-07-03T20:03:35.064Z', + id: '20202020-954c-4d76-9a87-e5f072d4b7ef', + provider: 'google', + accountOwnerId: '20202020-03f2-4d83-b0d5-2ec2bcee72d4', + lastSyncHistoryId: '', + emailAliases: '', + handle: 'test.test@gmail.com', + authFailedAt: null, + }, + }, + ], + }, + }, + }); + }), + graphql.query('FindManyCalendarChannels', () => { + return HttpResponse.json({ + data: { + calendarChannels: { + __typename: 'CalendarChannelConnection', + totalCount: 2, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + startCursor: '', + endCursor: '', + }, + edges: [ + { + __typename: 'CalendarChannelEdge', + cursor: '', + node: { + __typename: 'CalendarChannel', + handle: 'test.test@gmail.com', + excludeNonProfessionalEmails: true, + syncStageStartedAt: null, + id: '20202020-ef5a-4822-9e08-ce6e6a4dcb6f', + updatedAt: '2024-07-03T20:03:11.903Z', + createdAt: '2024-07-03T20:03:11.903Z', + connectedAccountId: + '20202020-954c-4d76-9a87-e5f072d4b7ef', + contactAutoCreationPolicy: 'SENT', + syncStage: 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING', + type: 'email', + isContactAutoCreationEnabled: true, + syncCursor: '1562764', + excludeGroupEmails: true, + throttleFailureCount: 0, + isSyncEnabled: true, + visibility: 'SHARE_EVERYTHING', + syncStatus: 'COMPLETED', + syncedAt: '2024-07-04T16:25:04.960Z', + }, + }, + { + __typename: 'CalendarChannelEdge', + cursor: '', + node: { + __typename: 'CalendarChannel', + handle: 'test.test2@gmail.com', + excludeNonProfessionalEmails: true, + syncStageStartedAt: null, + id: '20202020-ef5a-4822-9e08-ce6e6a4dcb6a', + updatedAt: '2024-07-03T20:03:11.903Z', + createdAt: '2024-07-03T20:03:11.903Z', + connectedAccountId: + '20202020-954c-4d76-9a87-e5f072d4b7ef', + contactAutoCreationPolicy: 'SENT', + syncStage: 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING', + type: 'email', + isContactAutoCreationEnabled: true, + syncCursor: '1562764', + excludeGroupEmails: true, + throttleFailureCount: 0, + isSyncEnabled: true, + visibility: 'SHARE_EVERYTHING', + syncStatus: 'COMPLETED', + syncedAt: '2024-07-04T16:25:04.960Z', + }, + }, + ], + }, + }, + }); + }), + ], + }, }, }; diff --git a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendarsSettings.stories.tsx b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendarsSettings.stories.tsx deleted file mode 100644 index 1e3ee5934..000000000 --- a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendarsSettings.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { within } from '@storybook/test'; -import { graphql, HttpResponse } from 'msw'; - -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; -import { SettingsPath } from '@/types/SettingsPath'; -import { - PageDecorator, - PageDecoratorArgs, -} from '~/testing/decorators/PageDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedConnectedAccounts } from '~/testing/mock-data/accounts'; -import { sleep } from '~/utils/sleep'; - -import { SettingsAccountsCalendarsSettings } from '../SettingsAccountsCalendarsSettings'; - -const meta: Meta = { - title: 'Pages/Settings/Accounts/SettingsAccountsCalendarsSettings', - component: SettingsAccountsCalendarsSettings, - decorators: [PageDecorator], - args: { - routePath: getSettingsPagePath(SettingsPath.AccountsCalendarsSettings), - routeParams: { ':accountUuid': mockedConnectedAccounts[0].id }, - }, - parameters: { - layout: 'fullscreen', - msw: { - handlers: [ - ...graphqlMocks.handlers, - graphql.query('FindOneCalendarChannel', () => { - return HttpResponse.json({ - data: { - calendarChannel: { - edges: [], - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: null, - endCursor: null, - }, - }, - }, - }); - }), - ], - }, - }, -}; - -export default meta; - -export type Story = StoryObj; - -export const Default: Story = { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - sleep(100); - - await canvas.findByText('Event visibility'); - }, -};