Update Connected Accounts Design (#13332)

/closes #13328

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
neo773
2025-07-25 16:48:02 +05:30
committed by GitHub
parent 79992b53d1
commit d4ca63dbb7
8 changed files with 153 additions and 70 deletions

View File

@ -1,40 +1,65 @@
import { ConnectedAccount } from '@/accounts/types/ConnectedAccount';
import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard';
import { SettingsConnectedAccountsTableHeader } from '@/settings/accounts/components/SettingsConnectedAccountsTableHeader';
import { SettingsConnectedAccountsTableRow } from '@/settings/components/SettingsConnectedAccountsTableRow';
import { SettingsPath } from '@/types/SettingsPath';
import { Table } from '@/ui/layout/table/components/Table';
import styled from '@emotion/styled';
import { SettingsAccountsConnectedAccountsRowRightContainer } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer';
import { useLingui } from '@lingui/react/macro';
import { IconPlus } from 'twenty-ui/display';
import { SettingsConnectedAccountIcon } from '@/settings/accounts/components/SettingsConnectedAccountIcon';
import { Button } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { SettingsListCard } from '../../components/SettingsListCard';
const StyledTableRows = styled.div`
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-top: ${({ theme }) => theme.spacing(2)};
`;
const StyledAddAccountSection = styled(Section)`
border-top: 1px solid ${({ theme }) => theme.border.color.light};
display: flex;
justify-content: flex-end;
padding-top: ${({ theme }) => theme.spacing(2)};
padding-bottom: ${({ theme }) => theme.spacing(2)};
`;
export const SettingsAccountsConnectedAccountsListCard = ({
accounts,
loading,
}: {
accounts: ConnectedAccount[];
loading?: boolean;
}) => {
const navigate = useNavigateSettings();
const { t } = useLingui();
const navigateSettings = useNavigateSettings();
if (!accounts.length) {
return <SettingsAccountsListEmptyStateCard />;
}
return (
<SettingsListCard
items={accounts}
getItemLabel={(account) => account.handle}
isLoading={loading}
RowIconFn={(row) => SettingsConnectedAccountIcon({ account: row })}
RowRightComponent={({ item: account }) => (
<SettingsAccountsConnectedAccountsRowRightContainer account={account} />
)}
hasFooter={true}
footerButtonLabel={t`Add account`}
onFooterButtonClick={() => navigate(SettingsPath.NewAccount)}
<Section>
<Table>
<SettingsConnectedAccountsTableHeader />
<StyledTableRows>
{accounts.map((account) => (
<SettingsConnectedAccountsTableRow
key={account.id}
account={account}
/>
))}
</StyledTableRows>
</Table>
<StyledAddAccountSection>
<Button
Icon={IconPlus}
title={t`Add account`}
variant="secondary"
size="small"
onClick={() => navigateSettings(SettingsPath.NewAccount)}
/>
</StyledAddAccountSection>
</Section>
);
};

View File

@ -9,7 +9,7 @@ import { Status } from 'twenty-ui/display';
const StyledRowRightContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${({ theme }) => theme.spacing(4)};
`;
export const SettingsAccountsConnectedAccountsRowRightContainer = ({

View File

@ -3,40 +3,30 @@ import { isGoogleMessagingEnabledState } from '@/client-config/states/isGoogleMe
import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState';
import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState';
import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth';
import { SettingsCard } from '@/settings/components/SettingsCard';
import { SettingsPath } from '@/types/SettingsPath';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { IconAt, IconGoogle, IconMicrosoft } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { Card, CardContent, CardHeader } from 'twenty-ui/layout';
import { UndecoratedLink } from 'twenty-ui/navigation';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
const StyledHeader = styled(CardHeader)`
align-items: center;
const StyledCardsContainer = styled.div`
display: flex;
height: ${({ theme }) => theme.spacing(6)};
`;
const StyledBody = styled(CardContent)`
display: flex;
justify-content: center;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
`;
type SettingsAccountsListEmptyStateCardProps = {
label?: string;
};
export const SettingsAccountsListEmptyStateCard = ({
label,
}: SettingsAccountsListEmptyStateCardProps) => {
export const SettingsAccountsListEmptyStateCard = () => {
const { triggerApisOAuth } = useTriggerApisOAuth();
const { t } = useLingui();
const theme = useTheme();
const isGoogleMessagingEnabled = useRecoilValue(
isGoogleMessagingEnabledState,
@ -56,36 +46,33 @@ export const SettingsAccountsListEmptyStateCard = ({
);
return (
<Card>
<StyledHeader>{label || t`No connected account`}</StyledHeader>
<StyledBody>
<StyledCardsContainer>
{(isGoogleMessagingEnabled || isGoogleCalendarEnabled) && (
<Button
Icon={IconGoogle}
<SettingsCard
Icon={<IconGoogle size={theme.icon.size.md} />}
title={t`Connect with Google`}
variant="secondary"
onClick={() => triggerApisOAuth(ConnectedAccountProvider.GOOGLE)}
/>
)}
{(isMicrosoftMessagingEnabled || isMicrosoftCalendarEnabled) && (
<Button
Icon={IconMicrosoft}
<SettingsCard
Icon={<IconMicrosoft size={theme.icon.size.md} />}
title={t`Connect with Microsoft`}
variant="secondary"
onClick={() => triggerApisOAuth(ConnectedAccountProvider.MICROSOFT)}
/>
)}
{isImapSmtpCaldavFeatureFlagEnabled && (
<Button
Icon={IconAt}
title={t`Connect Email Account`}
variant="secondary"
<UndecoratedLink
to={getSettingsPath(SettingsPath.NewImapSmtpCaldavConnection)}
>
<SettingsCard
Icon={<IconAt size={theme.icon.size.md} />}
title={t`Connect Email Account`}
/>
</UndecoratedLink>
)}
</StyledBody>
</Card>
</StyledCardsContainer>
);
};

View File

@ -12,11 +12,11 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal';
import { Trans, useLingui } from '@lingui/react/macro';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import {
IconAt,
IconCalendarEvent,
IconDotsVertical,
IconMail,
IconRefresh,
IconSettings,
IconTrash,
} from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
@ -63,7 +63,7 @@ export const SettingsAccountsRowDropdownMenu = ({
ConnectedAccountProvider.IMAP_SMTP_CALDAV && (
<MenuItem
text={t`Connection settings`}
LeftIcon={IconSettings}
LeftIcon={IconAt}
onClick={() => {
navigate(SettingsPath.EditImapSmtpCaldavConnection, {
connectedAccountId: account.id,

View File

@ -0,0 +1,24 @@
import { Table } from '@/ui/layout/table/components/Table';
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import styled from '@emotion/styled';
import { Trans } from '@lingui/react/macro';
const StyledTableHeader = styled(TableHeader)`
padding-right: ${({ theme }) => theme.spacing(14)};
`;
export const SettingsConnectedAccountsTableHeader = () => {
return (
<Table>
<TableRow gridAutoColumns="332px 1fr">
<StyledTableHeader>
<Trans>Account</Trans>
</StyledTableHeader>
<StyledTableHeader align={'right'}>
<Trans>Status</Trans>
</StyledTableHeader>
</TableRow>
</Table>
);
};

View File

@ -10,7 +10,7 @@ export const SettingsNewAccountSection = () => {
title={t`New account`}
description={t`Connect a new account to your workspace`}
/>
<SettingsAccountsListEmptyStateCard label={t`Choose your provider`} />
<SettingsAccountsListEmptyStateCard />
</Section>
);
};

View File

@ -0,0 +1,50 @@
import { ConnectedAccount } from '@/accounts/types/ConnectedAccount';
import { SettingsAccountsConnectedAccountsRowRightContainer } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer';
import { SettingsConnectedAccountIcon } from '@/settings/accounts/components/SettingsConnectedAccountIcon';
import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
const StyledNameCell = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
`;
const StyledTableRow = styled(TableRow)`
&:hover {
background: ${({ theme }) => theme.background.transparent.light};
cursor: pointer;
}
`;
type SettingsConnectedAccountsTableRowProps = {
account: ConnectedAccount;
};
export const SettingsConnectedAccountsTableRow = ({
account,
}: SettingsConnectedAccountsTableRowProps) => {
const theme = useTheme();
const IconComponent = SettingsConnectedAccountIcon({ account });
return (
<StyledTableRow key={account.id} gridAutoColumns="332px 1fr">
<TableCell>
<StyledNameCell>
<IconComponent
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
{account.handle}
</StyledNameCell>
</TableCell>
<TableCell align={'right'}>
<SettingsAccountsConnectedAccountsRowRightContainer account={account} />
</TableCell>
</StyledTableRow>
);
};

View File

@ -56,10 +56,7 @@ export const SettingsAccounts = () => {
title={t`Connected accounts`}
description={t`Manage your internet accounts.`}
/>
<SettingsAccountsConnectedAccountsListCard
accounts={accounts}
loading={loading}
/>
<SettingsAccountsConnectedAccountsListCard accounts={accounts} />
</Section>
<SettingsAccountsBlocklistSection />
<SettingsAccountsSettingsSection />