diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index 986e25aa6af..d4bfe5b3624 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -235,38 +235,6 @@ const SettingsDevelopersWebhookDetail = lazy(() => })), ); -const SettingsIntegrationDatabase = lazy(() => - import('~/pages/settings/integrations/SettingsIntegrationDatabase').then( - (module) => ({ - default: module.SettingsIntegrationDatabase, - }), - ), -); - -const SettingsIntegrationNewDatabaseConnection = lazy(() => - import( - '~/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection' - ).then((module) => ({ - default: module.SettingsIntegrationNewDatabaseConnection, - })), -); - -const SettingsIntegrationEditDatabaseConnection = lazy(() => - import( - '~/pages/settings/integrations/SettingsIntegrationEditDatabaseConnection' - ).then((module) => ({ - default: module.SettingsIntegrationEditDatabaseConnection, - })), -); - -const SettingsIntegrationShowDatabaseConnection = lazy(() => - import( - '~/pages/settings/integrations/SettingsIntegrationShowDatabaseConnection' - ).then((module) => ({ - default: module.SettingsIntegrationShowDatabaseConnection, - })), -); - const SettingsObjectNewFieldSelect = lazy(() => import( '~/pages/settings/data-model/new-field/SettingsObjectNewFieldSelect' @@ -568,22 +536,6 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => ( path={SettingsPath.Integrations} element={} /> - } - /> - } - /> - } - /> - } - /> { - const apolloMetadataClient = useApolloCoreClient(); - const apolloClient = useApolloClient(); - - const { refetch: refetchObjectMetadataItems } = - useFindManyObjectMetadataItems(); - - const { findManyRecordsQuery: findManyViewsQuery } = useFindManyRecordsQuery({ - objectNameSingular: CoreObjectNameSingular.View, - }); - const [mutate] = useMutation< - SyncRemoteTableMutation, - SyncRemoteTableMutationVariables - >(SYNC_REMOTE_TABLE, { - client: apolloMetadataClient, - }); - - const syncRemoteTable = useCallback( - async (input: RemoteTableInput) => { - const remoteTable = await mutate({ - variables: { - input, - }, - update: (cache, { data }) => { - if (isDefined(data)) { - modifyRemoteTableFromCache({ - cache: cache, - remoteTableName: input.name, - fieldModifiers: { - status: () => data.syncRemoteTable.status, - }, - }); - } - }, - }); - - await refetchObjectMetadataItems(); - await apolloClient.query({ - query: findManyViewsQuery, - fetchPolicy: 'network-only', - }); - - return remoteTable; - }, - [apolloClient, findManyViewsQuery, mutate, refetchObjectMetadataItems], - ); - - return { - syncRemoteTable, - }; -}; diff --git a/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts b/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts deleted file mode 100644 index 15541d86523..00000000000 --- a/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { useApolloClient, useMutation } from '@apollo/client'; -import { useCallback } from 'react'; - -import { SYNC_REMOTE_TABLE_SCHEMA_CHANGES } from '@/databases/graphql/mutations/syncRemoteTableSchemaChanges'; -import { modifyRemoteTableFromCache } from '@/databases/utils/modifyRemoteTableFromCache'; -import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; -import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; -import { - type RemoteTableInput, - type SyncRemoteTableSchemaChangesMutation, - type SyncRemoteTableSchemaChangesMutationVariables, -} from '~/generated-metadata/graphql'; -import { isDefined } from 'twenty-shared/utils'; - -export const useSyncRemoteTableSchemaChanges = () => { - const apolloMetadataClient = useApolloCoreClient(); - const apolloClient = useApolloClient(); - - const { refetch: refetchObjectMetadataItems } = - useFindManyObjectMetadataItems(); - - const { findManyRecordsQuery: findManyViewsQuery } = useFindManyRecordsQuery({ - objectNameSingular: CoreObjectNameSingular.View, - }); - - const [mutate, mutationInformation] = useMutation< - SyncRemoteTableSchemaChangesMutation, - SyncRemoteTableSchemaChangesMutationVariables - >(SYNC_REMOTE_TABLE_SCHEMA_CHANGES, { - client: apolloMetadataClient, - }); - - const syncRemoteTableSchemaChanges = useCallback( - async (input: RemoteTableInput) => { - const remoteTable = await mutate({ - variables: { - input, - }, - update: (cache, { data }) => { - if (isDefined(data)) { - modifyRemoteTableFromCache({ - cache: cache, - remoteTableName: input.name, - fieldModifiers: { - schemaPendingUpdates: () => - data.syncRemoteTableSchemaChanges.schemaPendingUpdates || [], - status: () => data.syncRemoteTableSchemaChanges.status, - }, - }); - } - }, - }); - - await refetchObjectMetadataItems(); - - await apolloClient.query({ - query: findManyViewsQuery, - fetchPolicy: 'network-only', - }); - - return remoteTable; - }, - [mutate, refetchObjectMetadataItems, findManyViewsQuery, apolloClient], - ); - - return { - syncRemoteTableSchemaChanges, - isLoading: mutationInformation.loading, - }; -}; diff --git a/packages/twenty-front/src/modules/object-metadata/types/CoreObjectNameSingular.ts b/packages/twenty-front/src/modules/object-metadata/types/CoreObjectNameSingular.ts index 1738d1c6c14..f1b479d8941 100644 --- a/packages/twenty-front/src/modules/object-metadata/types/CoreObjectNameSingular.ts +++ b/packages/twenty-front/src/modules/object-metadata/types/CoreObjectNameSingular.ts @@ -24,12 +24,6 @@ export enum CoreObjectNameSingular { Person = 'person', Task = 'task', TaskTarget = 'taskTarget', - View = 'view', - ViewField = 'viewField', - ViewFilter = 'viewFilter', - ViewFilterGroup = 'viewFilterGroup', - ViewSort = 'viewSort', - ViewGroup = 'viewGroup', Webhook = 'webhook', WorkspaceMember = 'workspaceMember', MessageThreadSubscriber = 'messageThreadSubscriber', diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionForm.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionForm.tsx deleted file mode 100644 index 397f5459d75..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionForm.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import styled from '@emotion/styled'; -import { Controller, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; - -import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput'; - -export const settingsIntegrationPostgreSQLConnectionFormSchema = z.object({ - dbname: z.string().min(1), - host: z.string().min(1), - port: z.preprocess((val) => parseInt(val as string), z.number().positive()), - user: z.string().min(1), - password: z.string().min(1), - schema: z.string().min(1), - label: z.string().min(1), -}); - -type SettingsIntegrationPostgreSQLConnectionFormValues = z.infer< - typeof settingsIntegrationPostgreSQLConnectionFormSchema ->; - -export const settingsIntegrationStripeConnectionFormSchema = z.object({ - api_key: z.string().min(1), - label: z.string().min(1), -}); - -type SettingsIntegrationStripeConnectionFormValues = z.infer< - typeof settingsIntegrationStripeConnectionFormSchema ->; - -const StyledInputsContainer = styled.div` - display: grid; - gap: ${({ theme }) => theme.spacing(2, 4)}; - grid-template-columns: 1fr 1fr; - grid-template-areas: - 'input-1 input-1' - 'input-2 input-3' - 'input-4 input-5'; - - & :first-of-type { - grid-area: input-1; - } -`; - -type SettingsIntegrationDatabaseConnectionFormProps = { - databaseKey: string; - disabled?: boolean; -}; - -type SettingsIntegrationConnectionFormValues = - | SettingsIntegrationPostgreSQLConnectionFormValues - | SettingsIntegrationStripeConnectionFormValues; - -const getFormFields = ( - databaseKey: string, -): - | { - name: - | 'dbname' - | 'host' - | 'port' - | 'user' - | 'password' - | 'schema' - | 'api_key' - | 'label'; - label: string; - type?: string; - placeholder: string; - }[] - | null => { - switch (databaseKey) { - case 'postgresql': - return [ - { - name: 'dbname' as const, - label: 'Database Name', - placeholder: 'default', - }, - { name: 'host' as const, label: 'Host', placeholder: 'host' }, - { name: 'port' as const, label: 'Port', placeholder: '5432' }, - { - name: 'user' as const, - label: 'User', - placeholder: 'user', - }, - { - name: 'password' as const, - label: 'Password', - type: 'password', - placeholder: '••••••', - }, - { name: 'schema' as const, label: 'Schema', placeholder: 'public' }, - { - name: 'label' as const, - label: 'Label', - placeholder: 'My database', - }, - ]; - case 'stripe': - return [ - { name: 'api_key' as const, label: 'API Key', placeholder: 'API key' }, - { - name: 'label' as const, - label: 'Label', - placeholder: 'My database', - }, - ]; - default: - return null; - } -}; - -export const SettingsIntegrationDatabaseConnectionForm = ({ - databaseKey, - disabled, -}: SettingsIntegrationDatabaseConnectionFormProps) => { - const { control } = useFormContext(); - const formFields = getFormFields(databaseKey); - - if (!formFields) return null; - - return ( - - {formFields.map(({ name, label, type, placeholder }) => ( - { - return ( - - ); - }} - /> - ))} - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx deleted file mode 100644 index 3c262fd125a..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { useDeleteOneDatabaseConnection } from '@/databases/hooks/useDeleteOneDatabaseConnection'; -import { SettingsIntegrationDatabaseConnectionSummaryCard } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard'; -import { SettingsIntegrationDatabaseTablesListCard } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard'; -import { useDatabaseConnection } from '@/settings/integrations/database-connection/hooks/useDatabaseConnection'; -import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; -import { Section } from '@react-email/components'; -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; -import { H2Title } from 'twenty-ui/display'; -import { useNavigateSettings } from '~/hooks/useNavigateSettings'; - -export const SettingsIntegrationDatabaseConnectionShowContainer = () => { - const navigate = useNavigateSettings(); - const { connection, integration, databaseKey, tables } = - useDatabaseConnection({ fetchPolicy: 'network-only' }); - - const { deleteOneDatabaseConnection } = useDeleteOneDatabaseConnection(); - - if (!connection || !integration) { - return null; - } - - const deleteConnection = async () => { - await deleteOneDatabaseConnection({ id: connection.id }); - - navigate(SettingsPath.IntegrationDatabase, { - databaseKey, - }); - }; - - const settingsIntegrationsPagePath = getSettingsPath( - SettingsPath.Integrations, - ); - - // TODO: move breadcrumb to header? - return ( - <> - -
- - -
-
- - {!!tables?.length && ( - - )} -
- - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx deleted file mode 100644 index d5313b83335..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { SettingsSummaryCard } from '@/settings/components/SettingsSummaryCard'; -import { SettingsIntegrationDatabaseConnectionSyncStatus } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import styled from '@emotion/styled'; -import { IconDotsVertical, IconPencil, IconTrash } from 'twenty-ui/display'; -import { LightIconButton } from 'twenty-ui/input'; -import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation'; - -type SettingsIntegrationDatabaseConnectionSummaryCardProps = { - databaseLogoUrl: string; - connectionId: string; - connectionLabel: string; - onRemove: () => void; -}; - -const StyledDatabaseLogoContainer = styled.div` - align-items: center; - display: flex; - height: ${({ theme }) => theme.spacing(4)}; - justify-content: center; - width: ${({ theme }) => theme.spacing(4)}; -`; - -const StyledDatabaseLogo = styled.img` - height: 100%; -`; - -export const SettingsIntegrationDatabaseConnectionSummaryCard = ({ - databaseLogoUrl, - connectionId, - connectionLabel, - onRemove, -}: SettingsIntegrationDatabaseConnectionSummaryCardProps) => { - const dropdownId = - 'settings-integration-database-connection-summary-card-dropdown'; - - return ( - - - - - {connectionLabel} - - } - rightComponent={ - <> - - - } - dropdownComponents={ - - - - - - - - - } - /> - - } - /> - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus.tsx deleted file mode 100644 index 0ac9de36935..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables'; -import { RemoteTableStatus } from '~/generated-metadata/graphql'; -import { isDefined } from 'twenty-shared/utils'; -import { Status } from 'twenty-ui/display'; - -type SettingsIntegrationDatabaseConnectionSyncStatusProps = { - connectionId: string; - skip?: boolean; - shouldFetchPendingSchemaUpdates?: boolean; -}; - -export const SettingsIntegrationDatabaseConnectionSyncStatus = ({ - connectionId, - skip, - shouldFetchPendingSchemaUpdates, -}: SettingsIntegrationDatabaseConnectionSyncStatusProps) => { - const { tables, error } = useGetDatabaseConnectionTables({ - connectionId, - skip, - shouldFetchPendingSchemaUpdates, - }); - - if (isDefined(error)) { - return ; - } - - const syncedTables = tables.filter( - (table) => table.status === RemoteTableStatus.SYNCED, - ); - - const updatesAvailable = tables.some( - (table) => - table.schemaPendingUpdates?.length && - table.schemaPendingUpdates.length > 0, - ); - - return ( - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx deleted file mode 100644 index 1968ec42886..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import styled from '@emotion/styled'; - -import { SettingsListCard } from '@/settings/components/SettingsListCard'; -import { SettingsIntegrationDatabaseConnectionSyncStatus } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus'; -import { type SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration'; -import { SettingsPath } from 'twenty-shared/types'; -import { IconChevronRight } from 'twenty-ui/display'; -import { LightIconButton } from 'twenty-ui/input'; -import { type RemoteServer } from '~/generated-metadata/graphql'; -import { useNavigateSettings } from '~/hooks/useNavigateSettings'; - -type SettingsIntegrationDatabaseConnectionsListCardProps = { - integration: SettingsIntegration; - connections: RemoteServer[]; -}; - -const StyledDatabaseLogoContainer = styled.div` - align-items: center; - display: flex; - height: ${({ theme }) => theme.spacing(4)}; - justify-content: center; - width: ${({ theme }) => theme.spacing(4)}; -`; - -const StyledDatabaseLogo = styled.img` - height: 100%; -`; - -const StyledRowRightContainer = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; -`; - -export const SettingsIntegrationDatabaseConnectionsListCard = ({ - integration, - connections, -}: SettingsIntegrationDatabaseConnectionsListCardProps) => { - const navigate = useNavigateSettings(); - - return ( - ( - - - - )} - RowRightComponent={({ item: connection }) => ( - - - - - )} - onRowClick={(connection) => - navigate(SettingsPath.IntegrationDatabaseConnection, { - databaseKey: integration.from.key, - connectionId: connection.id, - }) - } - getItemLabel={(connection) => connection.label} - hasFooter - footerButtonLabel="Add connection" - onFooterButtonClick={() => - navigate(SettingsPath.IntegrationNewDatabaseConnection, { - databaseKey: integration.from.key, - }) - } - /> - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx deleted file mode 100644 index de815aa58f4..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import styled from '@emotion/styled'; -import { useCallback } from 'react'; -import { z } from 'zod'; - -import { useSyncRemoteTable } from '@/databases/hooks/useSyncRemoteTable'; -import { useSyncRemoteTableSchemaChanges } from '@/databases/hooks/useSyncRemoteTableSchemaChanges'; -import { useUnsyncRemoteTable } from '@/databases/hooks/useUnsyncRemoteTable'; -import { SettingsListCard } from '@/settings/components/SettingsListCard'; -import { SettingsIntegrationRemoteTableSchemaUpdate } from '@/settings/integrations/components/SettingsIntegrationRemoteTableSchemaUpdate'; -import { SettingsIntegrationRemoteTableSyncStatusToggle } from '@/settings/integrations/components/SettingsIntegrationRemoteTableSyncStatusToggle'; -import { - DistantTableUpdate, - type RemoteTable, - type RemoteTableStatus, -} from '~/generated-metadata/graphql'; - -export const settingsIntegrationsDatabaseTablesSchema = z.object({ - syncedTablesByName: z.record(z.string(), z.boolean()), -}); - -export type SettingsIntegrationsDatabaseTablesFormValues = z.infer< - typeof settingsIntegrationsDatabaseTablesSchema ->; - -type SettingsIntegrationDatabaseTablesListCardProps = { - connectionId: string; - tables: RemoteTable[]; -}; - -const StyledRowRightContainer = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; -`; - -const getDistantTableUpdatesText = ( - schemaPendingUpdates: DistantTableUpdate[], -) => { - if (schemaPendingUpdates.includes(DistantTableUpdate.TABLE_DELETED)) { - return 'Table has been deleted'; - } - if ( - schemaPendingUpdates.includes(DistantTableUpdate.COLUMNS_ADDED) && - schemaPendingUpdates.includes(DistantTableUpdate.COLUMNS_DELETED) - ) { - return 'Columns have been added and other deleted'; - } - if (schemaPendingUpdates.includes(DistantTableUpdate.COLUMNS_ADDED)) { - return 'Columns have been added'; - } - if (schemaPendingUpdates.includes(DistantTableUpdate.COLUMNS_DELETED)) { - return 'Columns have been deleted'; - } - return null; -}; - -export const SettingsIntegrationDatabaseTablesListCard = ({ - connectionId, - tables, -}: SettingsIntegrationDatabaseTablesListCardProps) => { - const { syncRemoteTable } = useSyncRemoteTable(); - const { unsyncRemoteTable } = useUnsyncRemoteTable(); - const { syncRemoteTableSchemaChanges } = useSyncRemoteTableSchemaChanges(); - - const items = tables.map((table) => ({ - ...table, - id: table.name, - updatesText: table.schemaPendingUpdates - ? getDistantTableUpdatesText(table.schemaPendingUpdates) - : null, - })); - - const onSyncUpdate = useCallback( - async (isSynced: boolean, tableName: string) => { - if (isSynced) { - await syncRemoteTable({ - remoteServerId: connectionId, - name: tableName, - }); - } else { - await unsyncRemoteTable({ - remoteServerId: connectionId, - name: tableName, - }); - } - }, - [syncRemoteTable, connectionId, unsyncRemoteTable], - ); - - const onSyncSchemaUpdate = useCallback( - async (tableName: string) => - syncRemoteTableSchemaChanges({ - remoteServerId: connectionId, - name: tableName, - }), - [syncRemoteTableSchemaChanges, connectionId], - ); - - const rowRightComponent = useCallback( - ({ - item, - }: { - item: { - id: string; - name: string; - status: RemoteTableStatus; - updatesText?: string | null; - }; - }) => ( - - {item.updatesText && ( - onSyncSchemaUpdate(item.name)} - /> - )} - - - ), - [onSyncSchemaUpdate, onSyncUpdate], - ); - return ( - table.id} - /> - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx deleted file mode 100644 index 85c78392699..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { SettingsIntegrationEditDatabaseConnectionContent } from '@/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent'; -import { useDatabaseConnection } from '@/settings/integrations/database-connection/hooks/useDatabaseConnection'; - -export const SettingsIntegrationEditDatabaseConnectionContainer = () => { - const { connection, integration, databaseKey, tables } = - useDatabaseConnection({}); - - if (!connection || !integration) return null; - - return ( - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx deleted file mode 100644 index 971f0ee6d34..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useUpdateOneDatabaseConnection } from '@/databases/hooks/useUpdateOneDatabaseConnection'; -import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; -import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer'; -import { SettingsIntegrationDatabaseConnectionForm } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionForm'; -import { - formatValuesForUpdate, - getEditionSchemaForForm, - getFormDefaultValuesFromConnection, -} from '@/settings/integrations/database-connection/utils/editDatabaseConnection'; -import { type SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; -import { ApolloError } from '@apollo/client'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { useLingui } from '@lingui/react/macro'; -import { Section } from '@react-email/components'; -import { FormProvider, useForm } from 'react-hook-form'; -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; -import { H2Title, Info } from 'twenty-ui/display'; -import { type z } from 'zod'; -import { - type RemoteServer, - type RemoteTable, - RemoteTableStatus, -} from '~/generated-metadata/graphql'; -import { useNavigateSettings } from '~/hooks/useNavigateSettings'; - -export const SettingsIntegrationEditDatabaseConnectionContent = ({ - connection, - integration, - databaseKey, - tables, -}: { - connection: RemoteServer; - integration: SettingsIntegration; - databaseKey: string; - tables: RemoteTable[]; -}) => { - const { enqueueErrorSnackBar } = useSnackBar(); - const navigate = useNavigateSettings(); - - const editConnectionSchema = getEditionSchemaForForm(databaseKey); - type SettingsIntegrationEditConnectionFormValues = z.infer< - typeof editConnectionSchema - >; - - const formConfig = useForm({ - mode: 'onTouched', - resolver: zodResolver(editConnectionSchema), - defaultValues: getFormDefaultValuesFromConnection({ - databaseKey, - connection, - }), - }); - const { t } = useLingui(); - - const { updateOneDatabaseConnection } = useUpdateOneDatabaseConnection(); - - const settingsIntegrationsPagePath = getSettingsPath( - SettingsPath.Integrations, - ); - - const hasSyncedTables = tables?.some( - (table) => table?.status === RemoteTableStatus.SYNCED, - ); - - const { isDirty, isValid } = formConfig.formState; - const canSave = isDirty && isValid && !hasSyncedTables; // order matters here - - const handleSave = async () => { - const formValues = formConfig.getValues(); - const dirtyFieldKeys = Object.keys( - formConfig.formState.dirtyFields, - ) as (keyof SettingsIntegrationEditConnectionFormValues)[]; - - const dirtyFormValues = Object.fromEntries( - Object.entries(formValues).filter(([key]) => - dirtyFieldKeys.includes(key as keyof typeof formValues), - ), - ); - - try { - await updateOneDatabaseConnection({ - ...formatValuesForUpdate({ - databaseKey, - formValues: dirtyFormValues, - }), - id: connection?.id ?? '', - }); - - navigate(SettingsPath.IntegrationDatabaseConnection, { - databaseKey, - connectionId: connection?.id, - }); - } catch (error) { - enqueueErrorSnackBar({ - apolloError: error instanceof ApolloError ? error : undefined, - }); - } - }; - - // TODO: move breadcrumb to header? - return ( - <> - - - - - navigate(SettingsPath.IntegrationDatabase, { - databaseKey, - }) - } - onSave={handleSave} - /> - - {hasSyncedTables && ( - - )} -
- - - -
-
- - ); -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts b/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts deleted file mode 100644 index 33245605125..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { type WatchQueryFetchPolicy } from '@apollo/client'; -import { useEffect } from 'react'; -import { useParams } from 'react-router-dom'; - -import { useGetDatabaseConnection } from '@/databases/hooks/useGetDatabaseConnection'; -import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables'; -import { useIsSettingsIntegrationEnabled } from '@/settings/integrations/hooks/useIsSettingsIntegrationEnabled'; -import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; -import { AppPath } from 'twenty-shared/types'; -import { useNavigateApp } from '~/hooks/useNavigateApp'; - -export const useDatabaseConnection = ({ - fetchPolicy, -}: { - fetchPolicy?: WatchQueryFetchPolicy; -}) => { - const { databaseKey = '', connectionId = '' } = useParams(); - const navigateApp = useNavigateApp(); - - const [integrationCategoryAll] = useSettingsIntegrationCategories(); - const integration = integrationCategoryAll.integrations.find( - ({ from: { key } }) => key === databaseKey, - ); - - const isIntegrationEnabled = useIsSettingsIntegrationEnabled(databaseKey); - - const isIntegrationAvailable = !!integration && isIntegrationEnabled; - - const { connection, loading } = useGetDatabaseConnection({ - databaseKey, - connectionId, - skip: !isIntegrationAvailable, - fetchPolicy, - }); - - useEffect(() => { - if (!isIntegrationAvailable || (!loading && !connection)) { - navigateApp(AppPath.NotFound); - } - }, [ - integration, - databaseKey, - navigateApp, - isIntegrationAvailable, - connection, - loading, - ]); - - const { tables } = useGetDatabaseConnectionTables({ - connectionId, - skip: !connection, - shouldFetchPendingSchemaUpdates: true, - fetchPolicy, - }); - - return { connection, integration, databaseKey, tables }; -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/utils/editDatabaseConnection.ts b/packages/twenty-front/src/modules/settings/integrations/database-connection/utils/editDatabaseConnection.ts deleted file mode 100644 index af5e1b67eb3..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/utils/editDatabaseConnection.ts +++ /dev/null @@ -1,103 +0,0 @@ -import identity from 'lodash.identity'; -import isEmpty from 'lodash.isempty'; -import pickBy from 'lodash.pickby'; -import { z } from 'zod'; - -import { - settingsIntegrationPostgreSQLConnectionFormSchema, - settingsIntegrationStripeConnectionFormSchema, -} from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionForm'; -import { CustomError } from 'twenty-shared/utils'; -import { type RemoteServer } from '~/generated-metadata/graphql'; - -export const getEditionSchemaForForm = (databaseKey: string) => { - switch (databaseKey) { - case 'postgresql': - return settingsIntegrationPostgreSQLConnectionFormSchema.extend({ - password: z.string().optional(), - }); - case 'stripe': - return settingsIntegrationStripeConnectionFormSchema; - default: - throw new CustomError( - `No schema found for database key: ${databaseKey}`, - 'NO_SCHEMA_FOUND', - ); - } -}; - -export const getFormDefaultValuesFromConnection = ({ - databaseKey, - connection, -}: { - databaseKey: string; - connection: RemoteServer; -}) => { - switch (databaseKey) { - case 'postgresql': - return { - dbname: connection.foreignDataWrapperOptions.dbname, - host: connection.foreignDataWrapperOptions.host, - port: connection.foreignDataWrapperOptions.port, - user: connection.userMappingOptions?.user || undefined, - schema: connection.schema || undefined, - label: connection.label, - password: '', - }; - case 'stripe': - return { - api_key: connection.foreignDataWrapperOptions.api_key, - label: connection.label, - }; - default: - throw new Error( - `No default form values for database key: ${databaseKey}`, - ); - } -}; - -export const formatValuesForUpdate = ({ - databaseKey, - formValues, -}: { - databaseKey: string; - formValues: any; -}) => { - switch (databaseKey) { - case 'postgresql': { - const formattedValues = { - userMappingOptions: pickBy( - { - user: formValues.user, - password: formValues.password, - }, - identity, - ), - foreignDataWrapperOptions: pickBy( - { - dbname: formValues.dbname, - host: formValues.host, - port: formValues.port, - }, - identity, - ), - schema: formValues.schema, - label: formValues.label, - }; - - return pickBy(formattedValues, (obj) => !isEmpty(obj)); - } - case 'stripe': - return { - foreignDataWrapperOptions: { - api_key: formValues.api_key, - }, - label: formValues.label, - }; - default: - throw new CustomError( - `Cannot format values for database key: ${databaseKey}`, - 'CANNOT_FORMAT_VALUES', - ); - } -}; diff --git a/packages/twenty-front/src/modules/settings/integrations/hooks/useSettingsIntegrationCategories.ts b/packages/twenty-front/src/modules/settings/integrations/hooks/useSettingsIntegrationCategories.ts index 9fab3ca0218..7a5018e9a53 100644 --- a/packages/twenty-front/src/modules/settings/integrations/hooks/useSettingsIntegrationCategories.ts +++ b/packages/twenty-front/src/modules/settings/integrations/hooks/useSettingsIntegrationCategories.ts @@ -1,45 +1,10 @@ -import { MOCK_REMOTE_DATABASES } from '@/settings/integrations/constants/MockRemoteDatabases'; import { SETTINGS_INTEGRATION_REQUEST_CATEGORY } from '@/settings/integrations/constants/SettingsIntegrationRequest'; import { SETTINGS_INTEGRATION_ZAPIER_CATEGORY } from '@/settings/integrations/constants/SettingsIntegrationZapier'; import { type SettingsIntegrationCategory } from '@/settings/integrations/types/SettingsIntegrationCategory'; -import { getSettingsIntegrationAll } from '@/settings/integrations/utils/getSettingsIntegrationAll'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useSettingsIntegrationCategories = (): SettingsIntegrationCategory[] => { - const isAirtableIntegrationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_AIRTABLE_INTEGRATION_ENABLED, - ); - const isAirtableIntegrationActive = !!MOCK_REMOTE_DATABASES.find( - ({ name }) => name === 'airtable', - )?.isActive; - - const isPostgresqlIntegrationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_POSTGRESQL_INTEGRATION_ENABLED, - ); - const isPostgresqlIntegrationActive = !!MOCK_REMOTE_DATABASES.find( - ({ name }) => name === 'postgresql', - )?.isActive; - - const isStripeIntegrationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_STRIPE_INTEGRATION_ENABLED, - ); - const isStripeIntegrationActive = !!MOCK_REMOTE_DATABASES.find( - ({ name }) => name === 'stripe', - )?.isActive; - - const allIntegrations = getSettingsIntegrationAll({ - isAirtableIntegrationEnabled, - isAirtableIntegrationActive, - isPostgresqlIntegrationEnabled, - isPostgresqlIntegrationActive, - isStripeIntegrationEnabled, - isStripeIntegrationActive, - }); - return [ - ...(allIntegrations.integrations.length > 0 ? [allIntegrations] : []), SETTINGS_INTEGRATION_ZAPIER_CATEGORY, SETTINGS_INTEGRATION_REQUEST_CATEGORY, ]; diff --git a/packages/twenty-front/src/modules/settings/integrations/utils/__tests__/getSettingsIntegrationAll.test.ts b/packages/twenty-front/src/modules/settings/integrations/utils/__tests__/getSettingsIntegrationAll.test.ts deleted file mode 100644 index f8815dab20f..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/utils/__tests__/getSettingsIntegrationAll.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { getSettingsIntegrationAll } from '../getSettingsIntegrationAll'; - -describe('getSettingsIntegrationAll', () => { - it('should return null if imageUrl is null', () => { - expect( - getSettingsIntegrationAll({ - isAirtableIntegrationActive: true, - isAirtableIntegrationEnabled: true, - isPostgresqlIntegrationActive: true, - isPostgresqlIntegrationEnabled: true, - isStripeIntegrationActive: true, - isStripeIntegrationEnabled: true, - }), - ).toStrictEqual({ - integrations: [ - { - from: { - image: '/images/integrations/airtable-logo.png', - key: 'airtable', - }, - link: '/settings/integrations/airtable', - text: 'Airtable', - type: 'Active', - }, - { - from: { - image: '/images/integrations/postgresql-logo.png', - key: 'postgresql', - }, - link: '/settings/integrations/postgresql', - text: 'PostgreSQL', - type: 'Active', - }, - { - from: { - image: '/images/integrations/stripe-logo.png', - key: 'stripe', - }, - link: '/settings/integrations/stripe', - text: 'Stripe', - type: 'Active', - }, - ], - key: 'all', - title: 'All', - }); - }); -}); diff --git a/packages/twenty-front/src/modules/settings/integrations/utils/getSettingsIntegrationAll.ts b/packages/twenty-front/src/modules/settings/integrations/utils/getSettingsIntegrationAll.ts deleted file mode 100644 index b0c607b823b..00000000000 --- a/packages/twenty-front/src/modules/settings/integrations/utils/getSettingsIntegrationAll.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { type SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration'; -import { type SettingsIntegrationCategory } from '@/settings/integrations/types/SettingsIntegrationCategory'; -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; - -export const getSettingsIntegrationAll = ({ - isAirtableIntegrationEnabled, - isAirtableIntegrationActive, - isPostgresqlIntegrationEnabled, - isPostgresqlIntegrationActive, - isStripeIntegrationEnabled, - isStripeIntegrationActive, -}: { - isAirtableIntegrationEnabled: boolean; - isAirtableIntegrationActive: boolean; - isPostgresqlIntegrationEnabled: boolean; - isPostgresqlIntegrationActive: boolean; - isStripeIntegrationEnabled: boolean; - isStripeIntegrationActive: boolean; -}): SettingsIntegrationCategory => ({ - key: 'all', - title: 'All', - integrations: [ - isAirtableIntegrationEnabled && { - from: { - key: 'airtable', - image: '/images/integrations/airtable-logo.png', - }, - type: isAirtableIntegrationActive ? 'Active' : 'Add', - text: 'Airtable', - link: getSettingsPath(SettingsPath.IntegrationDatabase, { - databaseKey: 'airtable', - }), - }, - isPostgresqlIntegrationEnabled && { - from: { - key: 'postgresql', - image: '/images/integrations/postgresql-logo.png', - }, - type: isPostgresqlIntegrationActive ? 'Active' : 'Add', - text: 'PostgreSQL', - link: getSettingsPath(SettingsPath.IntegrationDatabase, { - databaseKey: 'postgresql', - }), - }, - isStripeIntegrationEnabled && { - from: { - key: 'stripe', - image: '/images/integrations/stripe-logo.png', - }, - type: isStripeIntegrationActive ? 'Active' : 'Add', - text: 'Stripe', - link: getSettingsPath(SettingsPath.IntegrationDatabase, { - databaseKey: 'stripe', - }), - }, - ].filter(Boolean) as SettingsIntegration[], -}); diff --git a/packages/twenty-front/src/pages/settings/data-model/new-field/SettingsObjectNewFieldConfigure.tsx b/packages/twenty-front/src/pages/settings/data-model/new-field/SettingsObjectNewFieldConfigure.tsx index 573426cebf5..0d165d45a38 100644 --- a/packages/twenty-front/src/pages/settings/data-model/new-field/SettingsObjectNewFieldConfigure.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/new-field/SettingsObjectNewFieldConfigure.tsx @@ -1,7 +1,5 @@ import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown'; @@ -12,8 +10,6 @@ import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fi import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { type View } from '@/views/types/View'; -import { ViewType } from '@/views/types/ViewType'; import { zodResolver } from '@hookform/resolvers/zod'; import { useLingui } from '@lingui/react/macro'; import { useEffect, useState } from 'react'; @@ -85,27 +81,6 @@ export const SettingsObjectNewFieldConfigure = () => { const [isSaving, setIsSaving] = useState(false); - useFindManyRecords({ - objectNameSingular: CoreObjectNameSingular.View, - filter: { - type: { eq: ViewType.Table }, - objectMetadataId: { eq: activeObjectMetadataItem?.id }, - }, - }); - - const relationObjectMetadataId = formConfig.watch( - 'relation.objectMetadataId', - ); - - useFindManyRecords({ - objectNameSingular: CoreObjectNameSingular.View, - skip: !relationObjectMetadataId, - filter: { - type: { eq: ViewType.Table }, - objectMetadataId: { eq: relationObjectMetadataId }, - }, - }); - useEffect(() => { if (!activeObjectMetadataItem) { navigateApp(AppPath.NotFound); diff --git a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDatabase.tsx b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDatabase.tsx deleted file mode 100644 index 12280fd2f41..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationDatabase.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useEffect } from 'react'; -import { useParams } from 'react-router-dom'; - -import { useGetDatabaseConnections } from '@/databases/hooks/useGetDatabaseConnections'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SettingsIntegrationPreview } from '@/settings/integrations/components/SettingsIntegrationPreview'; -import { SettingsIntegrationDatabaseConnectionsListCard } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard'; -import { useIsSettingsIntegrationEnabled } from '@/settings/integrations/hooks/useIsSettingsIntegrationEnabled'; -import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { AppPath, SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; -import { H2Title } from 'twenty-ui/display'; -import { Section } from 'twenty-ui/layout'; -import { useNavigateApp } from '~/hooks/useNavigateApp'; - -export const SettingsIntegrationDatabase = () => { - const { databaseKey = '' } = useParams(); - const navigateApp = useNavigateApp(); - - const [integrationCategoryAll] = useSettingsIntegrationCategories(); - const integration = integrationCategoryAll.integrations.find( - ({ from: { key } }) => key === databaseKey, - ); - - const isIntegrationEnabled = useIsSettingsIntegrationEnabled(databaseKey); - - const isIntegrationAvailable = !!integration && isIntegrationEnabled; - - useEffect(() => { - if (!isIntegrationAvailable) { - navigateApp(AppPath.NotFound); - } - }, [integration, databaseKey, navigateApp, isIntegrationAvailable]); - - const { connections } = useGetDatabaseConnections({ - databaseKey, - skip: !isIntegrationAvailable, - }); - - if (!isIntegrationAvailable) return null; - - return ( - - - -
- - -
-
-
- ); -}; diff --git a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationEditDatabaseConnection.tsx b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationEditDatabaseConnection.tsx deleted file mode 100644 index f3a20aec39f..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationEditDatabaseConnection.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SettingsIntegrationEditDatabaseConnectionContainer } from '@/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; - -export const SettingsIntegrationEditDatabaseConnection = () => { - return ( - - - - - - ); -}; diff --git a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx deleted file mode 100644 index e9dc37ad8c3..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { useEffect } from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; -import { useParams } from 'react-router-dom'; -import { type z } from 'zod'; - -import { useCreateOneDatabaseConnection } from '@/databases/hooks/useCreateOneDatabaseConnection'; -import { getForeignDataWrapperType } from '@/databases/utils/getForeignDataWrapperType'; -import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { - SettingsIntegrationDatabaseConnectionForm, - settingsIntegrationPostgreSQLConnectionFormSchema, - settingsIntegrationStripeConnectionFormSchema, -} from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionForm'; -import { useIsSettingsIntegrationEnabled } from '@/settings/integrations/hooks/useIsSettingsIntegrationEnabled'; -import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; - -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { ApolloError } from '@apollo/client'; -import { AppPath, SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; -import { H2Title } from 'twenty-ui/display'; -import { Section } from 'twenty-ui/layout'; -import { type CreateRemoteServerInput } from '~/generated-metadata/graphql'; -import { useNavigateApp } from '~/hooks/useNavigateApp'; -import { useNavigateSettings } from '~/hooks/useNavigateSettings'; - -const createRemoteServerInputPostgresSchema = - settingsIntegrationPostgreSQLConnectionFormSchema.transform( - (values) => ({ - foreignDataWrapperType: 'postgres_fdw', - foreignDataWrapperOptions: { - dbname: values.dbname, - host: values.host, - port: values.port, - }, - userMappingOptions: { - password: values.password, - user: values.user, - }, - schema: values.schema, - label: values.label, - }), - ); - -type SettingsIntegrationNewConnectionPostgresFormValues = z.infer< - typeof createRemoteServerInputPostgresSchema ->; - -const createRemoteServerInputStripeSchema = - settingsIntegrationStripeConnectionFormSchema.transform( - (values) => ({ - foreignDataWrapperType: 'stripe_fdw', - foreignDataWrapperOptions: { - api_key: values.api_key, - }, - label: values.label, - }), - ); - -type SettingsIntegrationNewConnectionStripeFormValues = z.infer< - typeof createRemoteServerInputStripeSchema ->; - -type SettingsIntegrationNewConnectionFormValues = - | SettingsIntegrationNewConnectionPostgresFormValues - | SettingsIntegrationNewConnectionStripeFormValues; - -export const SettingsIntegrationNewDatabaseConnection = () => { - const { databaseKey = '' } = useParams(); - const navigate = useNavigateSettings(); - const navigateApp = useNavigateApp(); - - const [integrationCategoryAll] = useSettingsIntegrationCategories(); - const integration = integrationCategoryAll.integrations.find( - ({ from: { key } }) => key === databaseKey, - ); - - const { createOneDatabaseConnection } = useCreateOneDatabaseConnection(); - const { enqueueErrorSnackBar } = useSnackBar(); - - const isIntegrationEnabled = useIsSettingsIntegrationEnabled(databaseKey); - - const isIntegrationAvailable = !!integration && isIntegrationEnabled; - - useEffect(() => { - if (!isIntegrationAvailable) { - navigateApp(AppPath.NotFound); - } - }, [integration, databaseKey, navigateApp, isIntegrationAvailable]); - - const newConnectionSchema = - databaseKey === 'postgresql' - ? createRemoteServerInputPostgresSchema - : createRemoteServerInputStripeSchema; - - const formConfig = useForm({ - mode: 'onTouched', - resolver: zodResolver(newConnectionSchema), - }); - - if (!isIntegrationAvailable) return null; - - const settingsIntegrationsPagePath = getSettingsPath( - SettingsPath.Integrations, - ); - - const canSave = formConfig.formState.isValid; - - const handleSave = async () => { - const formValues = formConfig.getValues(); - - try { - const createdConnection = await createOneDatabaseConnection( - newConnectionSchema.parse({ - ...formValues, - foreignDataWrapperType: getForeignDataWrapperType(databaseKey), - }), - ); - - const connectionId = createdConnection.data?.createOneRemoteServer.id; - - if (!connectionId) { - throw new Error('Failed to create connection'); - } - - navigate(SettingsPath.IntegrationDatabaseConnection, { - databaseKey, - connectionId, - }); - } catch (error) { - enqueueErrorSnackBar({ - apolloError: error instanceof ApolloError ? error : undefined, - }); - } - }; - - return ( - - navigate(SettingsPath.IntegrationDatabase, { - databaseKey, - }) - } - onSave={handleSave} - /> - } - > - - -
- - -
-
-
-
- ); -}; diff --git a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationShowDatabaseConnection.tsx b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationShowDatabaseConnection.tsx deleted file mode 100644 index 164392439cd..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationShowDatabaseConnection.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SettingsIntegrationDatabaseConnectionShowContainer } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; - -export const SettingsIntegrationShowDatabaseConnection = () => { - return ( - - - - - - ); -}; diff --git a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationDatabase.stories.tsx b/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationDatabase.stories.tsx deleted file mode 100644 index 1f6b7610863..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationDatabase.stories.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// TEMP_DISABLED_TEST: Removed unused imports due to commented test -import { type Meta, type StoryObj } from '@storybook/react'; - -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; -import { SettingsIntegrationDatabase } from '~/pages/settings/integrations/SettingsIntegrationDatabase'; -import { - PageDecorator, - type PageDecoratorArgs, -} from '~/testing/decorators/PageDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; - -// TEMP_DISABLED_TEST: Removed unused import due to commented test -// import { sleep } from '~/utils/sleep'; - -const meta: Meta = { - title: 'Pages/Settings/Integrations/SettingsIntegrationDatabase', - component: SettingsIntegrationDatabase, - decorators: [PageDecorator], - args: { - routePath: getSettingsPath(SettingsPath.IntegrationDatabase), - routeParams: { ':databaseKey': 'postgresql' }, - }, - parameters: { - msw: graphqlMocks, - }, -}; - -export default meta; - -export type Story = StoryObj; - -// TEMP_DISABLED_TEST: Temporarily commented out due to test failure -// export const Default: Story = { -// play: async ({ canvasElement }) => { -// const canvas = within(canvasElement); -// sleep(1000); - -// expect(await canvas.findByText('PostgreSQL database')).toBeInTheDocument(); -// }, -// }; diff --git a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationEditDatabaseConnection.stories.tsx b/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationEditDatabaseConnection.stories.tsx deleted file mode 100644 index 141d57f68bb..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationEditDatabaseConnection.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { type Meta, type StoryObj } from '@storybook/react'; - -import { SettingsIntegrationEditDatabaseConnection } from '~/pages/settings/integrations/SettingsIntegrationEditDatabaseConnection'; -import { - PageDecorator, - type PageDecoratorArgs, -} from '~/testing/decorators/PageDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; - -const meta: Meta = { - title: - 'Pages/Settings/Integrations/SettingsIntegrationEditDatabaseConnection', - component: SettingsIntegrationEditDatabaseConnection, - decorators: [PageDecorator], - args: { - routePath: '/settings/integrations/:databaseKey/edit', - routeParams: { - ':databaseKey': 'postgresql', - ':connectionId': '67cbfd35-8dd4-4591-b9d4-c1906281a5da', - }, - }, - parameters: { - msw: graphqlMocks, - }, -}; - -export default meta; - -export type Story = StoryObj; - -// TEMP_DISABLED_TEST: Temporarily commented out due to test failure -// export const Default: Story = { -// play: async ({ canvasElement }) => { -// const canvas = within(canvasElement); -// sleep(100); - -// await canvas.findByText('Edit Connection', undefined, { timeout: 3000 }); -// }, -// }; diff --git a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationNewDatabaseConnection.stories.tsx b/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationNewDatabaseConnection.stories.tsx deleted file mode 100644 index 59524bd39d2..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationNewDatabaseConnection.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { type Meta, type StoryObj } from '@storybook/react'; - -import { SettingsIntegrationNewDatabaseConnection } from '~/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection'; -import { - PageDecorator, - type PageDecoratorArgs, -} from '~/testing/decorators/PageDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; - -const meta: Meta = { - title: 'Pages/Settings/Integrations/SettingsIntegrationNewDatabaseConnection', - component: SettingsIntegrationNewDatabaseConnection, - decorators: [PageDecorator], - args: { - routePath: '/settings/integrations/:databaseKey/new', - routeParams: { databaseKey: 'postgresql' }, - }, - parameters: { - msw: graphqlMocks, - }, -}; - -export default meta; - -export type Story = StoryObj; - -// TEMP_DISABLED_TEST: Temporarily commented out due to test failure -// export const Default: Story = { -// play: async ({ canvasElement }) => { -// const canvas = within(canvasElement); - -// await canvas.findByText('Connect a new database', undefined, { -// timeout: 3000, -// }); -// }, -// }; diff --git a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationShowDatabaseConnection.stories.tsx b/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationShowDatabaseConnection.stories.tsx deleted file mode 100644 index 06d2b51c9ce..00000000000 --- a/packages/twenty-front/src/pages/settings/integrations/__stories__/SettingsIntegrationShowDatabaseConnection.stories.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { type Meta, type StoryObj } from '@storybook/react'; -import { within } from '@storybook/test'; - -import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; -import { SettingsIntegrationShowDatabaseConnection } from '~/pages/settings/integrations/SettingsIntegrationShowDatabaseConnection'; -import { - PageDecorator, - type PageDecoratorArgs, -} from '~/testing/decorators/PageDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; - -import { sleep } from '~/utils/sleep'; - -const meta: Meta = { - title: - 'Pages/Settings/Integrations/SettingsIntegrationShowDatabaseConnection', - component: SettingsIntegrationShowDatabaseConnection, - decorators: [PageDecorator], - args: { - routePath: getSettingsPath(SettingsPath.IntegrationDatabaseConnection), - routeParams: { - ':databaseKey': 'postgresql', - ':connectionId': '67cbfd35-8dd4-4591-b9d4-c1906281a5da', - }, - }, - parameters: { - msw: graphqlMocks, - }, -}; - -export default meta; - -export type Story = StoryObj; - -export const Default: Story = { - play: async ({ canvasElement }) => { - within(canvasElement); - sleep(1000); - - // Todo: Implement mocks in graphqlMocks for databaseConnection - // await canvas.findByText('Tables'); - }, -}; diff --git a/packages/twenty-shared/src/types/SettingsPath.ts b/packages/twenty-shared/src/types/SettingsPath.ts index 98205c8e814..a097c78a06a 100644 --- a/packages/twenty-shared/src/types/SettingsPath.ts +++ b/packages/twenty-shared/src/types/SettingsPath.ts @@ -43,10 +43,6 @@ export enum SettingsPath { NewWebhook = 'api-webhooks/webhooks/new', WebhookDetail = 'api-webhooks/webhooks/:webhookId', Integrations = 'integrations', - IntegrationDatabase = 'integrations/:databaseKey', - IntegrationDatabaseConnection = 'integrations/:databaseKey/:connectionId', - IntegrationEditDatabaseConnection = 'integrations/:databaseKey/:connectionId/edit', - IntegrationNewDatabaseConnection = 'integrations/:databaseKey/new', Security = 'security', NewSSOIdentityProvider = 'security/sso/new', diff --git a/packages/twenty-shared/src/workflow/utils/parseBooleanFromStringValue.ts b/packages/twenty-shared/src/workflow/utils/parseBooleanFromStringValue.ts index 04ca1a41894..4093d40f212 100644 --- a/packages/twenty-shared/src/workflow/utils/parseBooleanFromStringValue.ts +++ b/packages/twenty-shared/src/workflow/utils/parseBooleanFromStringValue.ts @@ -1,4 +1,6 @@ -export const parseBooleanFromStringValue = (value: unknown): boolean | unknown => { +export const parseBooleanFromStringValue = ( + value: unknown, +): boolean | unknown => { if (value === 'true') { return true; }