feat: add Tables settings to Settings/Integrations/Database/Connectio… (#4851)

…n page

Closes #4560
This commit is contained in:
Thaïs 2024-04-05 18:12:54 +02:00 committed by GitHub
parent f4017119ab
commit bbdb926687
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 112 additions and 31 deletions

View File

@ -42,7 +42,7 @@ type SettingsListCardProps<ListItem extends { id: string }> = {
hasFooter?: boolean; hasFooter?: boolean;
isLoading?: boolean; isLoading?: boolean;
onRowClick?: (item: ListItem) => void; onRowClick?: (item: ListItem) => void;
RowIcon: IconComponent; RowIcon?: IconComponent;
RowRightComponent: ComponentType<{ item: ListItem }>; RowRightComponent: ComponentType<{ item: ListItem }>;
footerButtonLabel?: string; footerButtonLabel?: string;
onFooterButtonClick?: () => void; onFooterButtonClick?: () => void;

View File

@ -14,16 +14,17 @@ const StyledRow = styled(CardContent)`
gap: ${({ theme }) => theme.spacing(2)}; gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)}; padding: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(3)}; padding-left: ${({ theme }) => theme.spacing(3)};
min-height: ${({ theme }) => theme.spacing(6)};
`; `;
const StyledAccountHandle = styled.span` const StyledLabel = styled.span`
flex: 1 0 auto; flex: 1 0 auto;
`; `;
type SettingsListItemCardContentProps = { type SettingsListItemCardContentProps = {
label: string; label: string;
divider?: boolean; divider?: boolean;
LeftIcon: IconComponent; LeftIcon?: IconComponent;
onClick?: () => void; onClick?: () => void;
rightComponent: ReactNode; rightComponent: ReactNode;
}; };
@ -39,8 +40,8 @@ export const SettingsListItemCardContent = ({
return ( return (
<StyledRow onClick={onClick} divider={divider}> <StyledRow onClick={onClick} divider={divider}>
<LeftIcon size={theme.icon.size.md} /> {!!LeftIcon && <LeftIcon size={theme.icon.size.md} />}
<StyledAccountHandle>{label}</StyledAccountHandle> <StyledLabel>{label}</StyledLabel>
{rightComponent} {rightComponent}
</StyledRow> </StyledRow>
); );

View File

@ -0,0 +1,50 @@
import { Controller, useFormContext } from 'react-hook-form';
import styled from '@emotion/styled';
import { z } from 'zod';
import { SettingsListCard } from '@/settings/components/SettingsListCard';
import { Toggle } from '@/ui/input/components/Toggle';
export const settingsIntegrationsDatabaseTablesSchema = z.object({
syncedTablesById: z.record(z.boolean()),
});
export type SettingsIntegrationsDatabaseTablesFormValues = z.infer<
typeof settingsIntegrationsDatabaseTablesSchema
>;
type SettingsIntegrationDatabaseTablesListCardProps = {
tables: { id: string; name: string; isSynced?: boolean }[];
};
const StyledRowRightContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
`;
export const SettingsIntegrationDatabaseTablesListCard = ({
tables,
}: SettingsIntegrationDatabaseTablesListCardProps) => {
const { control } =
useFormContext<SettingsIntegrationsDatabaseTablesFormValues>();
return (
<SettingsListCard
items={tables}
RowRightComponent={({ item: table }) => (
<StyledRowRightContainer>
<Controller
name={`syncedTablesById.${table.id}`}
control={control}
defaultValue={!!table.isSynced}
render={({ field: { onChange, value } }) => (
<Toggle value={value} onChange={onChange} />
)}
/>
</StyledRowRightContainer>
)}
getItemLabel={(table) => table.name}
/>
);
};

View File

@ -1,8 +1,15 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { zodResolver } from '@hookform/resolvers/zod';
import { IconSettings } from 'twenty-ui'; import { IconSettings } from 'twenty-ui';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import {
SettingsIntegrationDatabaseTablesListCard,
SettingsIntegrationsDatabaseTablesFormValues,
settingsIntegrationsDatabaseTablesSchema,
} from '@/settings/integrations/components/SettingsIntegrationDatabaseTablesListCard';
import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
@ -47,37 +54,54 @@ export const SettingsIntegrationDatabaseConnection = () => {
} }
}, [integration, databaseKey, navigate, isIntegrationAvailable, connection]); }, [integration, databaseKey, navigate, isIntegrationAvailable, connection]);
const formConfig = useForm<SettingsIntegrationsDatabaseTablesFormValues>({
mode: 'onTouched',
resolver: zodResolver(settingsIntegrationsDatabaseTablesSchema),
});
if (!isIntegrationAvailable || !connection) return null; if (!isIntegrationAvailable || !connection) return null;
const settingsIntegrationsPagePath = getSettingsPagePath( const settingsIntegrationsPagePath = getSettingsPagePath(
SettingsPath.Integrations, SettingsPath.Integrations,
); );
const tables = mockedRemoteObjectIntegrations[0].connections[0].tables;
return ( return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings"> // eslint-disable-next-line react/jsx-props-no-spreading
<SettingsPageContainer> <FormProvider {...formConfig}>
<Breadcrumb <SubMenuTopBarContainer Icon={IconSettings} title="Settings">
links={[ <SettingsPageContainer>
{ <Breadcrumb
children: 'Integrations', links={[
href: settingsIntegrationsPagePath, {
}, children: 'Integrations',
{ href: settingsIntegrationsPagePath,
children: integration.text, },
href: `${settingsIntegrationsPagePath}/${databaseKey}`, {
}, children: integration.text,
{ children: connection.name }, href: `${settingsIntegrationsPagePath}/${databaseKey}`,
]} },
/> { children: connection.name },
<Section> ]}
<H2Title title="About" description="About this remote object" />
<SettingsIntegrationDatabaseConnectionSummaryCard
databaseLogoUrl={integration.from.image}
connectionName={connection.name}
connectedTablesNb={connection.tables.length}
/> />
</Section> <Section>
</SettingsPageContainer> <H2Title title="About" description="About this remote object" />
</SubMenuTopBarContainer> <SettingsIntegrationDatabaseConnectionSummaryCard
databaseLogoUrl={integration.from.image}
connectionName={connection.name}
connectedTablesNb={tables.length}
/>
</Section>
<Section>
<H2Title
title="Tables"
description="Select the tables that should be tracked"
/>
<SettingsIntegrationDatabaseTablesListCard tables={tables} />
</Section>
</SettingsPageContainer>
</SubMenuTopBarContainer>
</FormProvider>
); );
}; };

View File

@ -7,13 +7,19 @@ export const mockedRemoteObjectIntegrations = [
id: '67cbfd35-8dd4-4591-b9d4-c1906281a5da', id: '67cbfd35-8dd4-4591-b9d4-c1906281a5da',
key: 'twenty_postgres', key: 'twenty_postgres',
name: 'Twenty_postgres', name: 'Twenty_postgres',
tables: [{ name: '1' }], tables: [
{ id: 'invoices', name: 'Invoices' },
{ id: 'quotes', name: 'Quotes', isSynced: true },
{ id: 'customers', name: 'Customers', isSynced: false },
{ id: 'subscriptions', name: 'Subscriptions', isSynced: true },
{ id: 'payments', name: 'Payments' },
],
}, },
{ {
id: '3740cd85-7a1e-45b5-8b0d-47e1921d01f3', id: '3740cd85-7a1e-45b5-8b0d-47e1921d01f3',
key: 'image_postgres', key: 'image_postgres',
name: 'Image_postgres', name: 'Image_postgres',
tables: [{ name: '2' }, { name: '3' }], tables: [],
}, },
], ],
}, },