From 5a692fbaebf31f0e965adfb1d91d9ea535351e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tha=C3=AFs?= Date: Thu, 22 Feb 2024 07:22:49 -0300 Subject: [PATCH] feat: add Accounts List Card to Calendar Settings (#4129) Closes #4061 --- ...ettingsAccountsCalendarSettingsSection.tsx | 39 ++++++-- .../components/SettingsAccountsCard.tsx | 69 -------------- ...ttingsAccountsConnectedAccountsSection.tsx | 46 ++++------ .../components/SettingsAccountsEmailsCard.tsx | 54 ----------- .../SettingsAccountsEmailsSyncSection.tsx | 47 ++++++---- .../components/SettingsAccountsListCard.tsx | 89 +++++++++++++++++++ .../components/SettingsAccountsRow.tsx | 11 ++- .../SettingsAccountsRowDropdownMenu.tsx | 30 ++++--- .../SettingsAccountsSynchronizationStatus.tsx | 15 ++++ .../display/icon/assets/google-calendar.svg | 10 +++ .../icon/components/IconGoogleCalendar.tsx | 14 +++ 11 files changed, 230 insertions(+), 194 deletions(-) delete mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCard.tsx delete mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsCard.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSynchronizationStatus.tsx create mode 100644 packages/twenty-front/src/modules/ui/display/icon/assets/google-calendar.svg create mode 100644 packages/twenty-front/src/modules/ui/display/icon/components/IconGoogleCalendar.tsx diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarSettingsSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarSettingsSection.tsx index fa2165822..b06954f3c 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarSettingsSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarSettingsSection.tsx @@ -1,18 +1,31 @@ +import { useNavigate } from 'react-router-dom'; +import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; 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 { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; -import { SettingsAccountsListSkeletonCard } from '@/settings/accounts/components/SettingsAccountsListSkeletonCard'; +import { SettingsAccountsListCard } from '@/settings/accounts/components/SettingsAccountsListCard'; +import { SettingsAccountsSynchronizationStatus } from '@/settings/accounts/components/SettingsAccountsSynchronizationStatus'; +import { IconChevronRight } from '@/ui/display/icon'; +import { IconGoogleCalendar } from '@/ui/display/icon/components/IconGoogleCalendar'; import { H2Title } from '@/ui/display/typography/components/H2Title'; +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { Section } from '@/ui/layout/section/components/Section'; +import { mockedConnectedAccounts } from '~/testing/mock-data/accounts'; + +const StyledRowRightContainer = styled.div` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; +`; export const SettingsAccountsCalendarSettingsSection = () => { const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); + const navigate = useNavigate(); - const { records: accounts, loading } = useFindManyRecords({ + const { records: _accounts, loading } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.ConnectedAccount, filter: { accountOwnerId: { @@ -27,12 +40,20 @@ export const SettingsAccountsCalendarSettingsSection = () => { title="Calendar settings" description="Sync your calendars and set your preferences" /> - - {loading ? ( - - ) : !accounts.length ? ( - - ) : null} + + navigate(`/settings/accounts/calendars/${account.id}`) + } + RowIcon={IconGoogleCalendar} + RowRightComponent={({ account: _account }) => ( + + + + + )} + /> ); }; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCard.tsx deleted file mode 100644 index f9a17e9bb..000000000 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCard.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; - -import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { SettingsAccountsRowDropdownMenu } from '@/settings/accounts/components/SettingsAccountsRowDropdownMenu'; -import { IconAt, IconPlus } from '@/ui/display/icon'; -import { IconGoogle } from '@/ui/display/icon/components/IconGoogle'; -import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; -import { Card } from '@/ui/layout/card/components/Card'; -import { CardFooter } from '@/ui/layout/card/components/CardFooter'; - -import { SettingsAccountRow } from './SettingsAccountsRow'; - -const StyledFooter = styled(CardFooter)` - align-items: center; - color: ${({ theme }) => theme.font.color.tertiary}; - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; - height: ${({ theme }) => theme.spacing(6)}; - padding: ${({ theme }) => theme.spacing(2)}; - padding-left: ${({ theme }) => theme.spacing(4)}; -`; - -const StyledIconButton = styled(LightIconButton)` - margin-left: auto; -`; - -const StyledDropdown = styled(SettingsAccountsRowDropdownMenu)` - margin-left: auto; -`; - -type SettingsAccountsCardProps = { - accounts: ConnectedAccount[]; - onAccountRemove?: (uuid: string) => void; -}; - -export const SettingsAccountsCard = ({ - accounts, - onAccountRemove, -}: SettingsAccountsCardProps) => { - const theme = useTheme(); - const navigate = useNavigate(); - - return ( - - {accounts.map((account, index) => ( - - } - divider={index < accounts.length - 1} - /> - ))} - - - Add account - navigate('/settings/accounts/new')} - /> - - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx index 50db7192f..10ca86b3c 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx @@ -1,37 +1,27 @@ import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; +import { SettingsAccountsRowDropdownMenu } from '@/settings/accounts/components/SettingsAccountsRowDropdownMenu'; import { H2Title } from '@/ui/display/typography/components/H2Title'; import { Section } from '@/ui/layout/section/components/Section'; -import { SettingsAccountsCard } from './SettingsAccountsCard'; -import { SettingsAccountsListEmptyStateCard } from './SettingsAccountsListEmptyStateCard'; +import { SettingsAccountsListCard } from './SettingsAccountsListCard'; export const SettingsAccountsConnectedAccountsSection = ({ accounts, + loading, }: { accounts: ConnectedAccount[]; -}) => { - const { deleteOneRecord } = useDeleteOneRecord({ - objectNameSingular: 'connectedAccount', - }); - - const handleAccountRemove = (idToRemove: string) => - deleteOneRecord(idToRemove); - - return ( -
- - {accounts?.length ? ( - - ) : ( - - )} -
- ); -}; + loading?: boolean; +}) => ( +
+ + +
+); diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsCard.tsx deleted file mode 100644 index 25cc99d88..000000000 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsCard.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import styled from '@emotion/styled'; - -import { MessageChannel } from '@/accounts/types/MessageChannel'; -import { IconChevronRight } from '@/ui/display/icon'; -import { IconGmail } from '@/ui/display/icon/components/IconGmail'; -import { Status } from '@/ui/display/status/components/Status'; -import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; -import { Card } from '@/ui/layout/card/components/Card'; - -import { SettingsAccountRow } from './SettingsAccountsRow'; - -const StyledRightContainer = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; - margin-left: auto; -`; - -type SettingsAccountsEmailsCardProps = { - messageChannels: MessageChannel[]; -}; - -export const SettingsAccountsEmailsCard = ({ - messageChannels, -}: SettingsAccountsEmailsCardProps) => { - const navigate = useNavigate(); - - return ( - - {messageChannels.map((messageChannel, index) => ( - - {messageChannel.isSynced ? ( - - ) : ( - - )} - - - } - onClick={() => - navigate(`/settings/accounts/emails/${messageChannel.id}`) - } - divider={index < messageChannels.length - 1} - /> - ))} - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsSyncSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsSyncSection.tsx index f5a4aa93a..e6d3cfa86 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsSyncSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEmailsSyncSection.tsx @@ -1,18 +1,29 @@ +import { useNavigate } from 'react-router-dom'; +import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; +import { LightIconButton } from 'tsup.ui.index'; 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 { SettingsAccountsEmailsCard } from '@/settings/accounts/components/SettingsAccountsEmailsCard'; -import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; -import { SettingsAccountsListSkeletonCard } from '@/settings/accounts/components/SettingsAccountsListSkeletonCard'; +import { SettingsAccountsListCard } from '@/settings/accounts/components/SettingsAccountsListCard'; +import { SettingsAccountsSynchronizationStatus } from '@/settings/accounts/components/SettingsAccountsSynchronizationStatus'; +import { IconChevronRight } from '@/ui/display/icon'; +import { IconGmail } from '@/ui/display/icon/components/IconGmail'; import { H2Title } from '@/ui/display/typography/components/H2Title'; import { Section } from '@/ui/layout/section/components/Section'; +const StyledRowRightContainer = styled.div` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; +`; + export const SettingsAccountsEmailsSyncSection = () => { const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); + const navigate = useNavigate(); const { records: accounts, loading: accountsLoading } = useFindManyRecords({ @@ -26,7 +37,7 @@ export const SettingsAccountsEmailsSyncSection = () => { const { records: messageChannels, loading: messageChannelsLoading } = useFindManyRecords({ - objectNameSingular: 'messageChannel', + objectNameSingular: CoreObjectNameSingular.MessageChannel, filter: { connectedAccountId: { in: accounts.map((account) => account.id), @@ -41,24 +52,28 @@ export const SettingsAccountsEmailsSyncSection = () => { }), ); - const loading = accountsLoading || messageChannelsLoading; - return (
- - {loading ? ( - - ) : accounts.length ? ( - - ) : ( - - )} + + navigate(`/settings/accounts/emails/${messageChannel.id}`) + } + RowIcon={IconGmail} + RowRightComponent={({ account: messageChannel }) => ( + + + + + )} + />
); }; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx new file mode 100644 index 000000000..dbdcdc456 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx @@ -0,0 +1,89 @@ +import { ReactNode } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; + +import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; +import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; +import { SettingsAccountsListSkeletonCard } from '@/settings/accounts/components/SettingsAccountsListSkeletonCard'; +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; +import { IconAt, IconPlus } from '@/ui/display/icon'; +import { IconGoogle } from '@/ui/display/icon/components/IconGoogle'; +import { IconComponent } from '@/ui/display/icon/types/IconComponent'; +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; +import { Card } from '@/ui/layout/card/components/Card'; +import { CardFooter } from '@/ui/layout/card/components/CardFooter'; + +import { SettingsAccountRow } from './SettingsAccountsRow'; + +const StyledFooter = styled(CardFooter)` + align-items: center; + color: ${({ theme }) => theme.font.color.tertiary}; + display: flex; + gap: ${({ theme }) => theme.spacing(2)}; + height: ${({ theme }) => theme.spacing(6)}; + padding: ${({ theme }) => theme.spacing(2)}; + padding-left: ${({ theme }) => theme.spacing(4)}; +`; + +const StyledIconButton = styled(LightIconButton)` + margin-left: auto; +`; + +type SettingsAccountsListCardProps< + Account extends Pick, +> = { + accounts: Account[]; + hasFooter?: boolean; + isLoading?: boolean; + onRowClick?: (account: Account) => void; + RowIcon?: IconComponent; + RowRightComponent: ({ account }: { account: Account }) => ReactNode; +}; + +export const SettingsAccountsListCard = < + Account extends Pick = ConnectedAccount, +>({ + accounts, + hasFooter, + isLoading, + onRowClick, + RowIcon = IconGoogle, + RowRightComponent, +}: SettingsAccountsListCardProps) => { + const theme = useTheme(); + const navigate = useNavigate(); + + if (isLoading) return ; + + if (!accounts.length) return ; + + return ( + + {accounts.map((account, index) => ( + } + divider={index < accounts.length - 1} + onClick={() => onRowClick?.(account)} + /> + ))} + {hasFooter && ( + + + Add account + + navigate(getSettingsPagePath(SettingsPath.NewAccount)) + } + /> + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx index a62beea23..2c2660ddd 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx @@ -3,7 +3,6 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { MessageChannel } from '@/accounts/types/MessageChannel'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { CardContent } from '@/ui/layout/card/components/CardContent'; @@ -18,8 +17,12 @@ const StyledRow = styled(CardContent)` padding-left: ${({ theme }) => theme.spacing(4)}; `; +const StyledAccountHandle = styled.span` + flex: 1 0 auto; +`; + type SettingsAccountRowProps = { - account: ConnectedAccount | MessageChannel; + account: Pick; divider?: boolean; LeftIcon: IconComponent; onClick?: () => void; @@ -37,8 +40,8 @@ export const SettingsAccountRow = ({ return ( - - {account.handle} + + {account.handle} {rightComponent} ); diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx index 0119f51ed..e112ccd94 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx @@ -1,6 +1,8 @@ import { useNavigate } from 'react-router-dom'; import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; import { IconDotsVertical, IconMail, IconTrash } from '@/ui/display/icon'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; @@ -10,21 +12,23 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type SettingsAccountsRowDropdownMenuProps = { - account: ConnectedAccount; + account: Pick; className?: string; - onRemove?: (uuid: string) => void; }; export const SettingsAccountsRowDropdownMenu = ({ account, className, - onRemove, }: SettingsAccountsRowDropdownMenuProps) => { const dropdownId = `settings-account-row-${account.id}`; const navigate = useNavigate(); const { closeDropdown } = useDropdown(dropdownId); + const { deleteOneRecord } = useDeleteOneRecord({ + objectNameSingular: CoreObjectNameSingular.ConnectedAccount, + }); + return ( - {!!onRemove && ( - { - onRemove(account.id); - closeDropdown(); - }} - /> - )} + { + deleteOneRecord(account.id); + closeDropdown(); + }} + /> } diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSynchronizationStatus.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSynchronizationStatus.tsx new file mode 100644 index 000000000..4744007ee --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSynchronizationStatus.tsx @@ -0,0 +1,15 @@ +import { Status } from '@/ui/display/status/components/Status'; + +type SettingsAccountsSynchronizationStatusProps = { + synced: boolean; +}; + +export const SettingsAccountsSynchronizationStatus = ({ + synced, +}: SettingsAccountsSynchronizationStatusProps) => ( + +); diff --git a/packages/twenty-front/src/modules/ui/display/icon/assets/google-calendar.svg b/packages/twenty-front/src/modules/ui/display/icon/assets/google-calendar.svg new file mode 100644 index 000000000..9c149ef02 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/display/icon/assets/google-calendar.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/twenty-front/src/modules/ui/display/icon/components/IconGoogleCalendar.tsx b/packages/twenty-front/src/modules/ui/display/icon/components/IconGoogleCalendar.tsx new file mode 100644 index 000000000..49ee58c95 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/display/icon/components/IconGoogleCalendar.tsx @@ -0,0 +1,14 @@ +import { useTheme } from '@emotion/react'; + +import IconGoogleCalendarRaw from '../assets/google-calendar.svg?react'; + +type IconGoogleCalendarProps = { + size?: number; +}; + +export const IconGoogleCalendar = (props: IconGoogleCalendarProps) => { + const theme = useTheme(); + const size = props.size ?? theme.icon.size.lg; + + return ; +};