diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx index e59476ce0..623040fb3 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsListCard.tsx @@ -7,8 +7,9 @@ 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 { SettingsAccountsListCard } from '@/settings/accounts/components/SettingsAccountsListCard'; +import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; import { SettingsAccountsSynchronizationStatus } from '@/settings/accounts/components/SettingsAccountsSynchronizationStatus'; +import { SettingsListCard } from '@/settings/components/SettingsListCard'; import { IconChevronRight } from '@/ui/display/icon'; import { IconGoogleCalendar } from '@/ui/display/icon/components/IconGoogleCalendar'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; @@ -36,6 +37,7 @@ export const SettingsAccountsCalendarChannelsListCard = () => { const { records: calendarChannels, loading: calendarChannelsLoading } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.CalendarChannel, + skip: !accounts.length, filter: { connectedAccountId: { in: accounts.map((account) => account.id), @@ -43,9 +45,14 @@ export const SettingsAccountsCalendarChannelsListCard = () => { }, }); + if (!calendarChannels.length) { + return ; + } + return ( - calendarChannel.handle} isLoading={accountsLoading || calendarChannelsLoading} onRowClick={(calendarChannel) => navigate(`/settings/accounts/calendars/${calendarChannel.id}`) diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx new file mode 100644 index 000000000..e2a6fbaac --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx @@ -0,0 +1,41 @@ +import { useNavigate } from 'react-router-dom'; + +import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; +import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; +import { SettingsAccountsRowDropdownMenu } from '@/settings/accounts/components/SettingsAccountsRowDropdownMenu'; +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; +import { IconGoogle } from '@/ui/display/icon/components/IconGoogle'; + +import { SettingsListCard } from '../../components/SettingsListCard'; + +export const SettingsAccountsConnectedAccountsListCard = ({ + accounts, + loading, +}: { + accounts: ConnectedAccount[]; + loading?: boolean; +}) => { + const navigate = useNavigate(); + + if (!accounts.length) { + return ; + } + + return ( + account.handle} + isLoading={loading} + RowIcon={IconGoogle} + RowRightComponent={({ item: account }) => ( + + )} + hasFooter + footerButtonLabel="Add account" + onFooterButtonClick={() => + navigate(getSettingsPagePath(SettingsPath.NewAccount)) + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx deleted file mode 100644 index 7ffb55c4d..000000000 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsSection.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; -import { SettingsAccountsRowDropdownMenu } from '@/settings/accounts/components/SettingsAccountsRowDropdownMenu'; -import { H2Title } from '@/ui/display/typography/components/H2Title'; -import { Section } from '@/ui/layout/section/components/Section'; - -import { SettingsAccountsListCard } from './SettingsAccountsListCard'; - -export const SettingsAccountsConnectedAccountsSection = ({ - accounts, - loading, -}: { - accounts: ConnectedAccount[]; - loading?: boolean; -}) => ( -
- - -
-); diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx index 103bd86fd..7b0979cb8 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsListCard.tsx @@ -8,8 +8,9 @@ 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 { SettingsAccountsListCard } from '@/settings/accounts/components/SettingsAccountsListCard'; +import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; import { SettingsAccountsSynchronizationStatus } from '@/settings/accounts/components/SettingsAccountsSynchronizationStatus'; +import { SettingsListCard } from '@/settings/components/SettingsListCard'; import { IconChevronRight } from '@/ui/display/icon'; import { IconGmail } from '@/ui/display/icon/components/IconGmail'; @@ -50,9 +51,14 @@ export const SettingsAccountsMessageChannelsListCard = () => { }), ); + if (!messageChannelsWithSyncedEmails.length) { + return ; + } + return ( - messageChannel.handle} isLoading={accountsLoading || messageChannelsLoading} onRowClick={(messageChannel) => navigate(`/settings/accounts/emails/${messageChannel.id}`) diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx b/packages/twenty-front/src/modules/settings/components/SettingsListCard.tsx similarity index 54% rename from packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx rename to packages/twenty-front/src/modules/settings/components/SettingsListCard.tsx index f8b04da35..4cd2499c0 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListCard.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsListCard.tsx @@ -1,19 +1,14 @@ import { ComponentType } from 'react'; -import { useNavigate } from 'react-router-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -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 { SettingsListSkeletonCard } from '@/settings/components/SettingsListSkeletonCard'; import { IconPlus } from '@/ui/display/icon'; -import { IconGoogle } from '@/ui/display/icon/components/IconGoogle'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { Card } from '@/ui/layout/card/components/Card'; import { CardFooter } from '@/ui/layout/card/components/CardFooter'; -import { SettingsAccountRow } from './SettingsAccountsRow'; +import { SettingsListItemCardContent } from './SettingsListItemCardContent'; const StyledFooter = styled(CardFooter)` align-items: center; @@ -41,58 +36,54 @@ const StyledButton = styled.button` } `; -type SettingsAccountsListCardItem = { - handle: string; - id: string; -}; - -type SettingsAccountsListCardProps = { - items: T[]; +type SettingsListCardProps = { + items: ListItem[]; + getItemLabel: (item: ListItem) => string; hasFooter?: boolean; isLoading?: boolean; - onRowClick?: (item: T) => void; - RowIcon?: IconComponent; - RowRightComponent: ComponentType<{ item: T }>; + onRowClick?: (item: ListItem) => void; + RowIcon: IconComponent; + RowRightComponent: ComponentType<{ item: ListItem }>; + footerButtonLabel?: string; + onFooterButtonClick?: () => void; }; -export const SettingsAccountsListCard = < - T extends SettingsAccountsListCardItem, +export const SettingsListCard = < + ListItem extends { id: string } = { + id: string; + }, >({ items, + getItemLabel, hasFooter, isLoading, onRowClick, - RowIcon = IconGoogle, + RowIcon, RowRightComponent, -}: SettingsAccountsListCardProps) => { + onFooterButtonClick, + footerButtonLabel, +}: SettingsListCardProps) => { const theme = useTheme(); - const navigate = useNavigate(); - if (isLoading === true) return ; - - if (!items.length) return ; + if (isLoading === true) return ; return ( {items.map((item, index) => ( - } divider={index < items.length - 1} onClick={() => onRowClick?.(item)} /> ))} {hasFooter && ( - - - navigate(getSettingsPagePath(SettingsPath.NewAccount)) - } - > + + - Add account + {footerButtonLabel} )} diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx b/packages/twenty-front/src/modules/settings/components/SettingsListItemCardContent.tsx similarity index 78% rename from packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx rename to packages/twenty-front/src/modules/settings/components/SettingsListItemCardContent.tsx index 8029242ba..206951eb6 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRow.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsListItemCardContent.tsx @@ -2,7 +2,6 @@ import { ReactNode } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { CardContent } from '@/ui/layout/card/components/CardContent'; @@ -21,27 +20,27 @@ const StyledAccountHandle = styled.span` flex: 1 0 auto; `; -type SettingsAccountRowProps = { - account: Pick; +type SettingsListItemCardContentProps = { + label: string; divider?: boolean; LeftIcon: IconComponent; onClick?: () => void; rightComponent: ReactNode; }; -export const SettingsAccountRow = ({ - account, +export const SettingsListItemCardContent = ({ + label, divider, LeftIcon, onClick, rightComponent, -}: SettingsAccountRowProps) => { +}: SettingsListItemCardContentProps) => { const theme = useTheme(); return ( - {account.handle} + {label} {rightComponent} ); diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListSkeletonCard.tsx b/packages/twenty-front/src/modules/settings/components/SettingsListSkeletonCard.tsx similarity index 78% rename from packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListSkeletonCard.tsx rename to packages/twenty-front/src/modules/settings/components/SettingsListSkeletonCard.tsx index 2044d629a..1a4732fed 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListSkeletonCard.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsListSkeletonCard.tsx @@ -7,4 +7,4 @@ const StyledCard = styled(Card)` height: 40px; `; -export { StyledCard as SettingsAccountsListSkeletonCard }; +export { StyledCard as SettingsListSkeletonCard }; diff --git a/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationDatabasesListCard.tsx b/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationDatabasesListCard.tsx new file mode 100644 index 000000000..c9991b9c0 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/integrations/components/SettingsIntegrationDatabasesListCard.tsx @@ -0,0 +1,73 @@ +import { useNavigate } from 'react-router-dom'; +import styled from '@emotion/styled'; + +import { SettingsListCard } from '@/settings/components/SettingsListCard'; +import { IconChevronRight } from '@/ui/display/icon'; +import { Status } from '@/ui/display/status/components/Status'; +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; + +type SettingsIntegrationDatabasesListCardProps = { + integrationLogoUrl: string; + databases: { + id: string; + key: string; + name: string; + tables: { + name: string; + }[]; + }[]; +}; + +const StyledIntegrationLogoContainer = styled.div` + align-items: center; + display: flex; + height: ${({ theme }) => theme.spacing(4)}; + justify-content: center; + width: ${({ theme }) => theme.spacing(4)}; +`; + +const StyledIntegrationLogo = styled.img` + height: 100%; +`; + +const StyledRowRightContainer = styled.div` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; +`; + +export const SettingsIntegrationDatabasesListCard = ({ + integrationLogoUrl, + databases, +}: SettingsIntegrationDatabasesListCardProps) => { + const navigate = useNavigate(); + + return ( + ( + + + + )} + RowRightComponent={({ item: database }) => ( + + + + + )} + onRowClick={(database) => navigate(`./${database.key}`)} + getItemLabel={(database) => database.name} + hasFooter + footerButtonLabel="Add connection" + onFooterButtonClick={() => navigate('./new')} + /> + ); +}; diff --git a/packages/twenty-front/src/modules/ui/layout/card/components/CardFooter.tsx b/packages/twenty-front/src/modules/ui/layout/card/components/CardFooter.tsx index 1cc953db3..e1813bf98 100644 --- a/packages/twenty-front/src/modules/ui/layout/card/components/CardFooter.tsx +++ b/packages/twenty-front/src/modules/ui/layout/card/components/CardFooter.tsx @@ -1,8 +1,10 @@ +import { css } from '@emotion/react'; import styled from '@emotion/styled'; -const StyledCardFooter = styled.div` +const StyledCardFooter = styled.div<{ divider?: boolean }>` background-color: ${({ theme }) => theme.background.primary}; - border-top: 1px solid ${({ theme }) => theme.border.color.medium}; + border-top: ${({ divider = true, theme }) => + divider ? css`1px solid ${theme.border.color.medium}` : 0}; font-size: ${({ theme }) => theme.font.size.sm}; padding: ${({ theme }) => theme.spacing(2, 4)}; `; diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index d92d71b93..fb77dfacc 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -6,24 +6,24 @@ import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus.ts'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus.ts'; -import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage.tsx'; +import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SupportChat } from '@/support/components/SupportChat.tsx'; +import { SupportChat } from '@/support/components/SupportChat'; import { AppPath } from '@/types/AppPath.ts'; import { IconCalendarEvent, IconCircleX } from '@/ui/display/icon'; import { IconCreditCard, IconCurrencyDollar } from '@/ui/display/icon'; -import { Info } from '@/ui/display/info/components/Info.tsx'; -import { H1Title } from '@/ui/display/typography/components/H1Title.tsx'; -import { H2Title } from '@/ui/display/typography/components/H2Title.tsx'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar.ts'; -import { Button } from '@/ui/input/button/components/Button.tsx'; -import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal.tsx'; +import { Info } from '@/ui/display/info/components/Info'; +import { H1Title } from '@/ui/display/typography/components/H1Title'; +import { H2Title } from '@/ui/display/typography/components/H2Title'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { Button } from '@/ui/input/button/components/Button'; +import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; -import { Section } from '@/ui/layout/section/components/Section.tsx'; +import { Section } from '@/ui/layout/section/components/Section'; import { useBillingPortalSessionQuery, useUpdateBillingSubscriptionMutation, -} from '~/generated/graphql.tsx'; +} from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; const StyledH1Title = styled(H1Title)` diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx index 0a250ae90..0beefb76a 100644 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx @@ -4,12 +4,14 @@ import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { SettingsAccountLoader } from '@/settings/accounts/components/SettingsAccountLoader'; -import { SettingsAccountsConnectedAccountsSection } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsSection'; +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 { IconSettings } from '@/ui/display/icon'; +import { H2Title } from '@/ui/display/typography/components/H2Title'; 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 { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; @@ -42,7 +44,16 @@ export const SettingsAccounts = () => { ) : ( <> - +
+ + +
{isBlocklistEnabled && } diff --git a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDetail.tsx b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDetail.tsx index 823012fed..4df171ffa 100644 --- a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDetail.tsx +++ b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDetail.tsx @@ -2,13 +2,19 @@ import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { SettingsIntegrationDatabasesListCard } from '@/settings/integrations/components/SettingsIntegrationDatabasesListCard'; import { SettingsIntegrationPreview } from '@/settings/integrations/components/SettingsIntegrationPreview'; import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { AppPath } from '@/types/AppPath'; +import { SettingsPath } from '@/types/SettingsPath'; import { IconSettings } from '@/ui/display/icon'; +import { H2Title } from '@/ui/display/typography/components/H2Title'; 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 { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; +import { mockedRemoteObjectIntegrations } from '~/testing/mock-data/remoteObjectDatabases'; export const SettingsIntegrationDetail = () => { const { integrationKey = '' } = useParams(); @@ -38,18 +44,36 @@ export const SettingsIntegrationDetail = () => { if (!isIntegrationAvailable) return null; + const databases = + mockedRemoteObjectIntegrations.find( + ({ key }) => key === integration.from.key, + )?.databases || []; + return ( +
+ + +
); diff --git a/packages/twenty-front/src/testing/mock-data/remoteObjectDatabases.ts b/packages/twenty-front/src/testing/mock-data/remoteObjectDatabases.ts new file mode 100644 index 000000000..cb235a99f --- /dev/null +++ b/packages/twenty-front/src/testing/mock-data/remoteObjectDatabases.ts @@ -0,0 +1,20 @@ +export const mockedRemoteObjectIntegrations = [ + { + id: '5b717911-dc75-4876-bf33-dfdc994c88cd', + key: 'postgresql', + databases: [ + { + id: '67cbfd35-8dd4-4591-b9d4-c1906281a5da', + key: 'twenty_postgres', + name: 'Twenty_postgres', + tables: [{ name: '1' }], + }, + { + id: '3740cd85-7a1e-45b5-8b0d-47e1921d01f3', + key: 'image_postgres', + name: 'Image_postgres', + tables: [{ name: '2' }, { name: '3' }], + }, + ], + }, +];