From f847e1270907c44a76427d39e83d494d829900ea Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Thu, 4 Jul 2024 19:05:33 +0200 Subject: [PATCH] Implement Settings Tabs (#6136) In this PR: - Renaming SettingsAccountsEmailBlocklist to SettingsAccountsEmailBlocklist as the blocklist is not tied to emails/messaging but is user level - Changing the UI settings UI by removing /emails/{id} page and adding tabs on /emails page image --- packages/twenty-front/src/App.tsx | 5 - ...tsx => SettingsAccountsBlocklistInput.tsx} | 6 +- ...x => SettingsAccountsBlocklistSection.tsx} | 10 +- ...tsx => SettingsAccountsBlocklistTable.tsx} | 10 +- ... => SettingsAccountsBlocklistTableRow.tsx} | 6 +- .../SettingsAccountsMessageChannelDetails.tsx | 116 ++++++++++++++++ ...ttingsAccountsMessageChannelsContainer.tsx | 71 ++++++++++ ...ettingsAccountsMessageChannelsListCard.tsx | 86 ------------ ...ettingsAccountsBlocklistInput.stories.tsx} | 11 +- ...ttingsAccountsBlocklistSection.stories.tsx | 16 +++ ...ettingsAccountsBlocklistTable.stories.tsx} | 11 +- ...ingsAccountsBlocklistTableRow.stories.tsx} | 10 +- ...AccountsEmailsBlocklistSection.stories.tsx | 17 --- ...sAccountsMessageChannelDetails.stories.tsx | 30 ++++ ...ccountMessageChannelsTabListComponentId.ts | 2 + .../settings/accounts/SettingsAccounts.tsx | 4 +- .../accounts/SettingsAccountsEmails.tsx | 10 +- .../SettingsAccountsEmailsInboxSettings.tsx | 128 ------------------ .../SettingsAccountsEmails.stories.tsx | 119 +++++++++++++++- ...ngsAccountsEmailsInboxSettings.stories.tsx | 79 ----------- 20 files changed, 389 insertions(+), 358 deletions(-) rename packages/twenty-front/src/modules/settings/accounts/components/{SettingsAccountsEmailsBlocklistInput.tsx => SettingsAccountsBlocklistInput.tsx} (94%) rename packages/twenty-front/src/modules/settings/accounts/components/{SettingsAccountsEmailsBlocklistSection.tsx => SettingsAccountsBlocklistSection.tsx} (81%) rename packages/twenty-front/src/modules/settings/accounts/components/{SettingsAccountsEmailsBlocklistTable.tsx => SettingsAccountsBlocklistTable.tsx} (79%) rename packages/twenty-front/src/modules/settings/accounts/components/{SettingsAccountsEmailsBlocklistTableRow.tsx => SettingsAccountsBlocklistTableRow.tsx} (85%) create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelDetails.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx delete mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx rename packages/twenty-front/src/modules/settings/accounts/components/__stories__/{SettingsAccountsEmailsBlocklistInput.stories.tsx => SettingsAccountsBlocklistInput.stories.tsx} (77%) create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistSection.stories.tsx rename packages/twenty-front/src/modules/settings/accounts/components/__stories__/{SettingsAccountsEmailsBlocklistTable.stories.tsx => SettingsAccountsBlocklistTable.stories.tsx} (81%) rename packages/twenty-front/src/modules/settings/accounts/components/__stories__/{SettingsAccountsEmailsBlocklistTableRow.stories.tsx => SettingsAccountsBlocklistTableRow.stories.tsx} (80%) delete mode 100644 packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistSection.stories.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsMessageChannelDetails.stories.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountMessageChannelsTabListComponentId.ts delete mode 100644 packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmailsInboxSettings.tsx delete mode 100644 packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmailsInboxSettings.stories.tsx diff --git a/packages/twenty-front/src/App.tsx b/packages/twenty-front/src/App.tsx index 956a6c59e..fff38bef2 100644 --- a/packages/twenty-front/src/App.tsx +++ b/packages/twenty-front/src/App.tsx @@ -55,7 +55,6 @@ 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 { SettingsAccountsEmailsInboxSettings } from '~/pages/settings/accounts/SettingsAccountsEmailsInboxSettings'; import { SettingsNewAccount } from '~/pages/settings/accounts/SettingsNewAccount'; import { SettingsNewObject } from '~/pages/settings/data-model/SettingsNewObject'; import { SettingsObjectDetail } from '~/pages/settings/data-model/SettingsObjectDetail'; @@ -188,10 +187,6 @@ const createRouter = (isBillingEnabled?: boolean) => path={SettingsPath.AccountsEmails} element={} /> - } - /> } diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistInput.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistInput.tsx similarity index 94% rename from packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistInput.tsx rename to packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistInput.tsx index 1794eb1d4..9bb4fdd98 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistInput.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistInput.tsx @@ -19,7 +19,7 @@ const StyledLinkContainer = styled.div` margin-right: ${({ theme }) => theme.spacing(2)}; `; -type SettingsAccountsEmailsBlocklistInputProps = { +type SettingsAccountsBlocklistInputProps = { updateBlockedEmailList: (email: string) => void; blockedEmailOrDomainList: string[]; }; @@ -50,10 +50,10 @@ type FormInput = { emailOrDomain: string; }; -export const SettingsAccountsEmailsBlocklistInput = ({ +export const SettingsAccountsBlocklistInput = ({ updateBlockedEmailList, blockedEmailOrDomainList, -}: SettingsAccountsEmailsBlocklistInputProps) => { +}: SettingsAccountsBlocklistInputProps) => { const { reset, handleSubmit, control, formState } = useForm({ mode: 'onSubmit', resolver: zodResolver(validationSchema(blockedEmailOrDomainList)), diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistSection.tsx similarity index 81% rename from packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistSection.tsx rename to packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistSection.tsx index 39b789fae..51ff642b1 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistSection.tsx @@ -7,11 +7,11 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; -import { SettingsAccountsEmailsBlocklistInput } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistInput'; -import { SettingsAccountsEmailsBlocklistTable } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistTable'; +import { SettingsAccountsBlocklistInput } from '@/settings/accounts/components/SettingsAccountsBlocklistInput'; +import { SettingsAccountsBlocklistTable } from '@/settings/accounts/components/SettingsAccountsBlocklistTable'; import { Section } from '@/ui/layout/section/components/Section'; -export const SettingsAccountsEmailsBlocklistSection = () => { +export const SettingsAccountsBlocklistSection = () => { const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const { records: blocklist } = useFindManyRecords({ @@ -44,11 +44,11 @@ export const SettingsAccountsEmailsBlocklistSection = () => { title="Blocklist" description="Exclude the following people and domains from my email sync" /> - item.handle)} updateBlockedEmailList={updateBlockedEmailList} /> - diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistTable.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistTable.tsx similarity index 79% rename from packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistTable.tsx rename to packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistTable.tsx index a238ef6e5..a4c9f5306 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsBlocklistTable.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsBlocklistTable.tsx @@ -1,13 +1,13 @@ import styled from '@emotion/styled'; import { BlocklistItem } from '@/accounts/types/BlocklistItem'; -import { SettingsAccountsEmailsBlocklistTableRow } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistTableRow'; +import { SettingsAccountsBlocklistTableRow } from '@/settings/accounts/components/SettingsAccountsBlocklistTableRow'; import { Table } from '@/ui/layout/table/components/Table'; import { TableBody } from '@/ui/layout/table/components/TableBody'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableRow } from '@/ui/layout/table/components/TableRow'; -type SettingsAccountsEmailsBlocklistTableProps = { +type SettingsAccountsBlocklistTableProps = { blocklist: BlocklistItem[]; handleBlockedEmailRemove: (id: string) => void; }; @@ -20,10 +20,10 @@ const StyledTableBody = styled(TableBody)` border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; `; -export const SettingsAccountsEmailsBlocklistTable = ({ +export const SettingsAccountsBlocklistTable = ({ blocklist, handleBlockedEmailRemove, -}: SettingsAccountsEmailsBlocklistTableProps) => { +}: SettingsAccountsBlocklistTableProps) => { return ( <> {blocklist.length > 0 && ( @@ -35,7 +35,7 @@ export const SettingsAccountsEmailsBlocklistTable = ({ {blocklist.map((blocklistItem) => ( - void; }; -export const SettingsAccountsEmailsBlocklistTableRow = ({ +export const SettingsAccountsBlocklistTableRow = ({ blocklistItem, onRemove, -}: SettingsAccountsEmailsBlocklistTableRowProps) => { +}: SettingsAccountsBlocklistTableRowProps) => { return ( {blocklistItem.handle} diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelDetails.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelDetails.tsx new file mode 100644 index 000000000..ad3b912db --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelDetails.tsx @@ -0,0 +1,116 @@ +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { H2Title, IconRefresh, IconUser } from 'twenty-ui'; + +import { MessageChannel } from '@/accounts/types/MessageChannel'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia'; +import { SettingsAccountsInboxVisibilitySettingsCard } from '@/settings/accounts/components/SettingsAccountsInboxVisibilitySettingsCard'; +import { SettingsAccountsToggleSettingCard } from '@/settings/accounts/components/SettingsAccountsToggleSettingCard'; +import { Section } from '@/ui/layout/section/components/Section'; +import { MessageChannelVisibility } from '~/generated-metadata/graphql'; + +type SettingsAccountsMessageChannelDetailsProps = { + messageChannel: Pick< + MessageChannel, + 'id' | 'visibility' | 'isContactAutoCreationEnabled' | 'isSyncEnabled' + >; +}; + +const StyledDetailsContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(6)}; + padding-top: ${({ theme }) => theme.spacing(6)}; +`; + +export const SettingsAccountsMessageChannelDetails = ({ + messageChannel, +}: SettingsAccountsMessageChannelDetailsProps) => { + const theme = useTheme(); + + const { updateOneRecord } = useUpdateOneRecord({ + objectNameSingular: CoreObjectNameSingular.MessageChannel, + }); + + const handleVisibilityChange = (value: MessageChannelVisibility) => { + updateOneRecord({ + idToUpdate: messageChannel.id, + updateOneRecordInput: { + visibility: value, + }, + }); + }; + + const handleContactAutoCreationToggle = (value: boolean) => { + updateOneRecord({ + idToUpdate: messageChannel.id, + updateOneRecordInput: { + isContactAutoCreationEnabled: value, + }, + }); + }; + + const handleIsSyncEnabledToggle = (value: boolean) => { + updateOneRecord({ + idToUpdate: messageChannel.id, + updateOneRecordInput: { + isSyncEnabled: value, + }, + }); + }; + + return ( + +
+ + +
+
+ + + + + } + title="Auto-creation" + value={!!messageChannel.isContactAutoCreationEnabled} + onToggle={handleContactAutoCreationToggle} + /> +
+
+ + + + + } + title="Sync emails" + value={!!messageChannel.isSyncEnabled} + onToggle={handleIsSyncEnabledToggle} + /> +
+
+ ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx new file mode 100644 index 000000000..95f462e06 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx @@ -0,0 +1,71 @@ +import { useRecoilValue } from 'recoil'; + +import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; +import { MessageChannel } from '@/accounts/types/MessageChannel'; +import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; +import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; +import { SettingsAccountsMessageChannelDetails } from '@/settings/accounts/components/SettingsAccountsMessageChannelDetails'; +import { SETTINGS_ACCOUNT_MESSAGE_CHANNELS_TAB_LIST_COMPONENT_ID } from '@/settings/accounts/constants/SettingsAccountMessageChannelsTabListComponentId'; +import { TabList } from '@/ui/layout/tab/components/TabList'; +import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; + +export const SettingsAccountsMessageChannelsContainer = () => { + const { activeTabIdState } = useTabList( + SETTINGS_ACCOUNT_MESSAGE_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: messageChannels } = useFindManyRecords< + MessageChannel & { + connectedAccount: ConnectedAccount; + } + >({ + objectNameSingular: CoreObjectNameSingular.MessageChannel, + filter: { + connectedAccountId: { + in: accounts.map((account) => account.id), + }, + }, + }); + + const tabs = [ + ...messageChannels.map((messageChannel) => ({ + id: messageChannel.id, + title: messageChannel.handle, + })), + ]; + + if (!messageChannels.length) { + return ; + } + + return ( + <> + + {messageChannels.map((messageChannel) => ( + <> + {messageChannel.id === activeTabId && ( + + )} + + ))} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx deleted file mode 100644 index addc376b4..000000000 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import styled from '@emotion/styled'; -import { useRecoilValue } from 'recoil'; -import { IconChevronRight, IconGmail } from 'twenty-ui'; - -import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { MessageChannel } from '@/accounts/types/MessageChannel'; -import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -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 SettingsAccountsMessageChannelsListCard = () => { - const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); - const navigate = useNavigate(); - - const { records: accounts, loading: accountsLoading } = - useFindManyRecords({ - objectNameSingular: CoreObjectNameSingular.ConnectedAccount, - filter: { - accountOwnerId: { - eq: currentWorkspaceMember?.id, - }, - }, - }); - - const { records: messageChannels, loading: messageChannelsLoading } = - useFindManyRecords< - MessageChannel & { - connectedAccount: ConnectedAccount; - } - >({ - objectNameSingular: CoreObjectNameSingular.MessageChannel, - filter: { - connectedAccountId: { - in: accounts.map((account) => account.id), - }, - }, - }); - - const messageChannelsWithSyncedEmails: (MessageChannel & { - connectedAccount: ConnectedAccount; - } & SettingsAccountsSynchronizationStatusProps)[] = messageChannels.map( - (messageChannel) => ({ - ...messageChannel, - syncStatus: messageChannel.syncStatus, - }), - ); - - if (!messageChannelsWithSyncedEmails.length) { - return ; - } - - return ( - messageChannel.handle} - isLoading={accountsLoading || messageChannelsLoading} - onRowClick={(messageChannel) => - navigate(`/settings/accounts/emails/${messageChannel.id}`) - } - RowIcon={IconGmail} - RowRightComponent={({ item: messageChannel }) => ( - - - - - )} - /> - ); -}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistInput.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistInput.stories.tsx similarity index 77% rename from packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistInput.stories.tsx rename to packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistInput.stories.tsx index 3ceef7405..48a52896b 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistInput.stories.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistInput.stories.tsx @@ -2,7 +2,7 @@ import { Decorator, Meta, StoryObj } from '@storybook/react'; import { expect, fn, userEvent, within } from '@storybook/test'; import { ComponentDecorator } from 'twenty-ui'; -import { SettingsAccountsEmailsBlocklistInput } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistInput'; +import { SettingsAccountsBlocklistInput } from '@/settings/accounts/components/SettingsAccountsBlocklistInput'; const updateBlockedEmailListJestFn = fn(); @@ -13,10 +13,9 @@ const ClearMocksDecorator: Decorator = (Story, context) => { return ; }; -const meta: Meta = { - title: - 'Modules/Settings/Accounts/Blocklist/SettingsAccountsEmailsBlocklistInput', - component: SettingsAccountsEmailsBlocklistInput, +const meta: Meta = { + title: 'Modules/Settings/Accounts/Blocklist/SettingsAccountsBlocklistInput', + component: SettingsAccountsBlocklistInput, decorators: [ComponentDecorator, ClearMocksDecorator], args: { updateBlockedEmailList: updateBlockedEmailListJestFn, @@ -31,7 +30,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = {}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistSection.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistSection.stories.tsx new file mode 100644 index 000000000..bc3f9cb72 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistSection.stories.tsx @@ -0,0 +1,16 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { ComponentDecorator } from 'twenty-ui'; + +import { SettingsAccountsBlocklistInput } from '@/settings/accounts/components/SettingsAccountsBlocklistInput'; +import { SettingsAccountsBlocklistSection } from '@/settings/accounts/components/SettingsAccountsBlocklistSection'; + +const meta: Meta = { + title: 'Modules/Settings/Accounts/Blocklist/SettingsAccountsBlocklistSection', + component: SettingsAccountsBlocklistInput, + decorators: [ComponentDecorator], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistTable.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistTable.stories.tsx similarity index 81% rename from packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistTable.stories.tsx rename to packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistTable.stories.tsx index 7ab7f1c9b..71126ba4e 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistTable.stories.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistTable.stories.tsx @@ -3,7 +3,7 @@ import { expect, fn, userEvent, within } from '@storybook/test'; import { ComponentDecorator } from 'twenty-ui'; import { mockedBlocklist } from '@/settings/accounts/components/__stories__/mockedBlocklist'; -import { SettingsAccountsEmailsBlocklistTable } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistTable'; +import { SettingsAccountsBlocklistTable } from '@/settings/accounts/components/SettingsAccountsBlocklistTable'; import { formatToHumanReadableDate } from '~/utils/date-utils'; const handleBlockedEmailRemoveJestFn = fn(); @@ -15,10 +15,9 @@ const ClearMocksDecorator: Decorator = (Story, context) => { return ; }; -const meta: Meta = { - title: - 'Modules/Settings/Accounts/Blocklist/SettingsAccountsEmailsBlocklistTable', - component: SettingsAccountsEmailsBlocklistTable, +const meta: Meta = { + title: 'Modules/Settings/Accounts/Blocklist/SettingsAccountsBlocklistTable', + component: SettingsAccountsBlocklistTable, decorators: [ComponentDecorator, ClearMocksDecorator], args: { blocklist: mockedBlocklist, @@ -34,7 +33,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { play: async ({ canvasElement }) => { diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistTableRow.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistTableRow.stories.tsx similarity index 80% rename from packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistTableRow.stories.tsx rename to packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistTableRow.stories.tsx index f24b0033f..b4a1991e6 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistTableRow.stories.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsBlocklistTableRow.stories.tsx @@ -3,7 +3,7 @@ import { expect, fn, userEvent, within } from '@storybook/test'; import { ComponentDecorator } from 'twenty-ui'; import { mockedBlocklist } from '@/settings/accounts/components/__stories__/mockedBlocklist'; -import { SettingsAccountsEmailsBlocklistTableRow } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistTableRow'; +import { SettingsAccountsBlocklistTableRow } from '@/settings/accounts/components/SettingsAccountsBlocklistTableRow'; import { formatToHumanReadableDate } from '~/utils/date-utils'; const onRemoveJestFn = fn(); @@ -15,10 +15,10 @@ const ClearMocksDecorator: Decorator = (Story, context) => { return ; }; -const meta: Meta = { +const meta: Meta = { title: - 'Modules/Settings/Accounts/Blocklist/SettingsAccountsEmailsBlocklistTableRow', - component: SettingsAccountsEmailsBlocklistTableRow, + 'Modules/Settings/Accounts/Blocklist/SettingsAccountsBlocklistTableRow', + component: SettingsAccountsBlocklistTableRow, decorators: [ComponentDecorator, ClearMocksDecorator], args: { blocklistItem: mockedBlocklist[0], @@ -34,7 +34,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { play: async ({ canvasElement }) => { diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistSection.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistSection.stories.tsx deleted file mode 100644 index 7fa2183aa..000000000 --- a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistSection.stories.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { ComponentDecorator } from 'twenty-ui'; - -import { SettingsAccountsEmailsBlocklistInput } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistInput'; -import { SettingsAccountsEmailsBlocklistSection } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistSection'; - -const meta: Meta = { - title: - 'Modules/Settings/Accounts/Blocklist/SettingsAccountsEmailsBlocklistSection', - component: SettingsAccountsEmailsBlocklistInput, - decorators: [ComponentDecorator], -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsMessageChannelDetails.stories.tsx b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsMessageChannelDetails.stories.tsx new file mode 100644 index 000000000..e1472836e --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsMessageChannelDetails.stories.tsx @@ -0,0 +1,30 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { ComponentDecorator } from 'twenty-ui'; + +import { SettingsAccountsMessageChannelDetails } from '@/settings/accounts/components/SettingsAccountsMessageChannelDetails'; +import { MessageChannelVisibility } from '~/generated/graphql'; + +const meta: Meta = { + title: + 'Modules/Settings/Accounts/MessageChannels/SettingsAccountsMessageChannelDetails', + component: SettingsAccountsMessageChannelDetails, + decorators: [ComponentDecorator], + args: { + messageChannel: { + id: '20202020-ef5a-4822-9e08-ce6e6a4dcb6a', + isContactAutoCreationEnabled: true, + isSyncEnabled: true, + visibility: MessageChannelVisibility.ShareEverything, + }, + }, + argTypes: { + messageChannel: { control: false }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + play: async () => {}, +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountMessageChannelsTabListComponentId.ts b/packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountMessageChannelsTabListComponentId.ts new file mode 100644 index 000000000..31f4638d7 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/constants/SettingsAccountMessageChannelsTabListComponentId.ts @@ -0,0 +1,2 @@ +export const SETTINGS_ACCOUNT_MESSAGE_CHANNELS_TAB_LIST_COMPONENT_ID = + 'settings-account-message-channels-tab-list'; diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx index 484a0a681..0a46dfbe9 100644 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx @@ -8,8 +8,8 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { SettingsAccountLoader } from '@/settings/accounts/components/SettingsAccountLoader'; +import { SettingsAccountsBlocklistSection } from '@/settings/accounts/components/SettingsAccountsBlocklistSection'; import { SettingsAccountsConnectedAccountsListCard } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsListCard'; -import { SettingsAccountsEmailsBlocklistSection } from '@/settings/accounts/components/SettingsAccountsEmailsBlocklistSection'; import { SettingsAccountsSettingsSection } from '@/settings/accounts/components/SettingsAccountsSettingsSection'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; @@ -55,7 +55,7 @@ export const SettingsAccounts = () => { loading={loading} /> - {isBlocklistEnabled && } + {isBlocklistEnabled && } )} diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmails.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmails.tsx index d2827187f..b972ad215 100644 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmails.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmails.tsx @@ -1,6 +1,6 @@ -import { H2Title, IconSettings } from 'twenty-ui'; +import { IconSettings } from 'twenty-ui'; -import { SettingsAccountsMessageChannelsListCard } from '@/settings/accounts/components/SettingsAccountsMessageChannelsListCard'; +import { SettingsAccountsMessageChannelsContainer } from '@/settings/accounts/components/SettingsAccountsMessageChannelsContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; @@ -16,11 +16,7 @@ export const SettingsAccountsEmails = () => ( ]} />
- - +
diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmailsInboxSettings.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmailsInboxSettings.tsx deleted file mode 100644 index afef0c498..000000000 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsEmailsInboxSettings.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useEffect } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useTheme } from '@emotion/react'; -import { H2Title, IconRefresh, IconSettings, IconUser } from 'twenty-ui'; - -import { MessageChannel } from '@/accounts/types/MessageChannel'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; -import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; -import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia'; -import { SettingsAccountsInboxVisibilitySettingsCard } from '@/settings/accounts/components/SettingsAccountsInboxVisibilitySettingsCard'; -import { SettingsAccountsToggleSettingCard } from '@/settings/accounts/components/SettingsAccountsToggleSettingCard'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { AppPath } from '@/types/AppPath'; -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 { MessageChannelVisibility } from '~/generated/graphql'; - -export const SettingsAccountsEmailsInboxSettings = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const { accountUuid: messageChannelId = '' } = useParams(); - - const { record: messageChannel, loading } = useFindOneRecord({ - objectNameSingular: CoreObjectNameSingular.MessageChannel, - objectRecordId: messageChannelId, - }); - - const { updateOneRecord } = useUpdateOneRecord({ - objectNameSingular: CoreObjectNameSingular.MessageChannel, - }); - - const handleVisibilityChange = (value: MessageChannelVisibility) => { - updateOneRecord({ - idToUpdate: messageChannelId, - updateOneRecordInput: { - visibility: value, - }, - }); - }; - - const handleContactAutoCreationToggle = (value: boolean) => { - updateOneRecord({ - idToUpdate: messageChannelId, - updateOneRecordInput: { - isContactAutoCreationEnabled: value, - }, - }); - }; - - const handleIsSyncEnabledToggle = (value: boolean) => { - updateOneRecord({ - idToUpdate: messageChannelId, - updateOneRecordInput: { - isSyncEnabled: value, - }, - }); - }; - - useEffect(() => { - if (!loading && !messageChannel) navigate(AppPath.NotFound); - }, [loading, messageChannel, navigate]); - - if (!messageChannel) return null; - - return ( - - - -
- - -
-
- - - - - } - title="Auto-creation" - value={!!messageChannel.isContactAutoCreationEnabled} - onToggle={handleContactAutoCreationToggle} - /> -
-
- - - - - } - title="Sync emails" - value={!!messageChannel.isSyncEnabled} - onToggle={handleIsSyncEnabledToggle} - /> -
-
-
- ); -}; diff --git a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmails.stories.tsx b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmails.stories.tsx index 21bede0eb..bde4ce31d 100644 --- a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmails.stories.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmails.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryObj } from '@storybook/react'; +import { graphql, HttpResponse } from 'msw'; import { PageDecorator, @@ -25,4 +26,120 @@ export default meta; export type Story = StoryObj; -export const Default: Story = {}; +export const NoConnectedAccount: Story = {}; + +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('FindManyMessageChannels', () => { + return HttpResponse.json({ + data: { + messageChannels: { + __typename: 'MessageChannelConnection', + totalCount: 2, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + startCursor: '', + endCursor: '', + }, + edges: [ + { + __typename: 'MessageChannelEdge', + cursor: '', + node: { + __typename: 'MessageChannel', + 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_MESSAGE_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: 'MessageChannelEdge', + cursor: '', + node: { + __typename: 'MessageChannel', + 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_MESSAGE_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__/SettingsAccountsEmailsInboxSettings.stories.tsx b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmailsInboxSettings.stories.tsx deleted file mode 100644 index 6c23dd71c..000000000 --- a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsEmailsInboxSettings.stories.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { within } from '@storybook/test'; -import { graphql, HttpResponse } from 'msw'; - -import { MessageChannelVisibility } from '~/generated/graphql'; -import { SettingsAccountsEmailsInboxSettings } from '~/pages/settings/accounts/SettingsAccountsEmailsInboxSettings'; -import { - PageDecorator, - PageDecoratorArgs, -} from '~/testing/decorators/PageDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; - -const meta: Meta = { - title: 'Pages/Settings/Accounts/SettingsAccountsEmailsInboxSettings', - component: SettingsAccountsEmailsInboxSettings, - decorators: [PageDecorator], - args: { - routePath: '/settings/accounts/emails/:accountUuid', - routeParams: { ':accountUuid': '123' }, - }, - parameters: { - layout: 'fullscreen', - msw: { - handlers: [ - graphql.query('FindOneMessageChannel', () => { - return HttpResponse.json({ - data: { - messageChannel: { - id: '1', - visibility: MessageChannelVisibility.ShareEverything, - messageThreads: { edges: [] }, - createdAt: '2021-08-27T12:00:00Z', - type: 'email', - updatedAt: '2021-08-27T12:00:00Z', - targetUrl: 'https://example.com/webhook', - connectedAccountId: '1', - handle: 'handle', - connectedAccount: { - id: '1', - handle: 'handle', - updatedAt: '2021-08-27T12:00:00Z', - accessToken: 'accessToken', - messageChannels: { edges: [] }, - refreshToken: 'refreshToken', - __typename: 'ConnectedAccount', - accountOwner: { id: '1', __typename: 'WorkspaceMember' }, - provider: 'provider', - createdAt: '2021-08-27T12:00:00Z', - accountOwnerId: '1', - }, - __typename: 'MessageChannel', - }, - }, - }); - }), - graphqlMocks.handlers, - ], - }, - }, -}; - -export default meta; - -export type Story = StoryObj; - -export const Default: Story = { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByText('Email visibility'); - await canvas.findByText( - 'Define what will be visible to other users in your workspace', - ); - await canvas.findByText('Contact auto-creation'); - await canvas.findByText( - 'Automatically create contacts for people you’ve sent emails to', - ); - }, -};