From 8019ba8782302124b2f55481cd349cb7ac2be153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tha=C3=AFs?= Date: Thu, 23 May 2024 12:19:50 +0200 Subject: [PATCH] feat: implement new SnackBar design (#5515) Closes #5383 ## Light theme image ## Dark theme image --- packages/twenty-front/.storybook/preview.tsx | 3 +- .../activities/hooks/useCustomResolver.ts | 3 +- .../hooks/useHandleResetPassword.ts | 9 +- .../auth/sign-in-up/hooks/useSignInUp.tsx | 5 +- .../components/PromiseRejectionEffect.tsx | 5 +- .../hooks/useFindManyObjectMetadataItems.ts | 3 +- .../hooks/useFindDuplicateRecords.ts | 3 +- .../object-record/hooks/useFindManyRecords.ts | 5 +- .../components/LightCopyIconButton.tsx | 3 +- .../useSpreadsheetRecordImport.ts | 3 +- .../developers/components/ApiKeyInput.tsx | 3 +- ...tegrationEditDatabaseConnectionContent.tsx | 3 +- .../profile/components/ChangePassword.tsx | 9 +- .../components/ToggleImpersonate.tsx | 3 +- .../MatchColumnsStep/MatchColumnsStep.tsx | 3 +- .../steps/components/UploadFlow.tsx | 3 +- .../UploadStep/components/DropZone.tsx | 3 +- .../progress-bar/components/ProgressBar.tsx | 115 ++----- .../__stories__/ProgressBar.stories.tsx | 65 ++-- .../hooks/useProgressAnimation.ts | 58 ++++ .../snack-bar-manager/components/SnackBar.tsx | 288 +++++++++--------- .../components/SnackBarProvider.tsx | 84 +++-- .../__stories__/SnackBar.stories.tsx | 63 ++++ .../__tests__/usePausableTimeout.test.tsx | 47 --- .../hooks/usePausableTimeout.ts | 56 ---- .../src/modules/ui/theme/constants/Colors.ts | 3 +- .../ui/theme/constants/MainColorNames.ts | 2 +- .../modules/ui/theme/constants/MainColors.ts | 15 - .../ui/theme/constants/OverlayBackground.ts | 3 +- .../ui/theme/constants/TextInputStyle.ts | 3 +- .../modules/ui/theme/constants/ThemeDark.ts | 21 -- .../modules/ui/theme/constants/ThemeLight.ts | 22 -- .../components/WorkspaceInviteLink.tsx | 3 +- .../src/pages/auth/ChooseYourPlan.tsx | 3 +- .../src/pages/auth/CreateProfile.tsx | 3 +- .../src/pages/auth/CreateWorkspace.tsx | 3 +- .../twenty-front/src/pages/auth/Invite.tsx | 5 +- .../src/pages/auth/PasswordReset.tsx | 9 +- .../src/pages/settings/SettingsBilling.tsx | 5 +- .../settings/data-model/SettingsNewObject.tsx | 3 +- .../data-model/SettingsObjectEdit.tsx | 3 +- .../data-model/SettingsObjectFieldEdit.tsx | 3 +- .../SettingsObjectNewFieldStep2.tsx | 3 +- ...ttingsIntegrationNewDatabaseConnection.tsx | 3 +- packages/twenty-front/tsup.ui.index.tsx | 5 +- .../display/icon/components/TablerIcons.ts | 1 + .../src/theme/constants/SnackBarCommon.ts | 21 ++ .../src/theme/constants/SnackBarDark.ts | 11 + .../src/theme/constants/SnackBarLight.ts | 11 + .../src/theme/constants/ThemeCommon.ts | 15 - .../src/theme/constants/ThemeDark.ts | 5 +- .../src/theme/constants/ThemeLight.ts | 5 +- packages/twenty-ui/src/theme/index.ts | 3 + 53 files changed, 485 insertions(+), 552 deletions(-) create mode 100644 packages/twenty-front/src/modules/ui/feedback/progress-bar/hooks/useProgressAnimation.ts create mode 100644 packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/__stories__/SnackBar.stories.tsx delete mode 100644 packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/__tests__/usePausableTimeout.test.tsx delete mode 100644 packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/usePausableTimeout.ts delete mode 100644 packages/twenty-front/src/modules/ui/theme/constants/MainColors.ts delete mode 100644 packages/twenty-front/src/modules/ui/theme/constants/ThemeDark.ts delete mode 100644 packages/twenty-front/src/modules/ui/theme/constants/ThemeLight.ts create mode 100644 packages/twenty-ui/src/theme/constants/SnackBarCommon.ts create mode 100644 packages/twenty-ui/src/theme/constants/SnackBarDark.ts create mode 100644 packages/twenty-ui/src/theme/constants/SnackBarLight.ts diff --git a/packages/twenty-front/.storybook/preview.tsx b/packages/twenty-front/.storybook/preview.tsx index d7b1255fc..c0955e59b 100644 --- a/packages/twenty-front/.storybook/preview.tsx +++ b/packages/twenty-front/.storybook/preview.tsx @@ -29,9 +29,8 @@ initialize({ const preview: Preview = { decorators: [ (Story) => { - const mode = useDarkMode() ? 'Dark' : 'Light'; + const theme = useDarkMode() ? THEME_DARK : THEME_LIGHT; - const theme = mode === 'Dark' ? THEME_DARK : THEME_LIGHT; return ( diff --git a/packages/twenty-front/src/modules/activities/hooks/useCustomResolver.ts b/packages/twenty-front/src/modules/activities/hooks/useCustomResolver.ts index ed32eb3f0..ee1e50772 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useCustomResolver.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useCustomResolver.ts @@ -8,6 +8,7 @@ import { import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; type CustomResolverQueryResult< @@ -62,7 +63,7 @@ export const useCustomResolver = < variables: queryVariables, onError: (error) => { enqueueSnackBar(error.message || `Error loading ${objectName}`, { - variant: 'error', + variant: SnackBarVariant.Error, }); }, }); diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts index 45cc945b3..b9003935c 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useEmailPasswordResetLinkMutation } from '~/generated/graphql'; @@ -12,7 +13,7 @@ export const useHandleResetPassword = () => { return async () => { if (!email) { enqueueSnackBar('Invalid email', { - variant: 'error', + variant: SnackBarVariant.Error, }); return; } @@ -24,16 +25,16 @@ export const useHandleResetPassword = () => { if (data?.emailPasswordResetLink?.success === true) { enqueueSnackBar('Password reset link has been sent to the email', { - variant: 'success', + variant: SnackBarVariant.Success, }); } else { enqueueSnackBar('There was some issue', { - variant: 'error', + variant: SnackBarVariant.Error, }); } } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx index 3c9edd5cb..0c23b372b 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx @@ -7,6 +7,7 @@ import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm'; import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken'; import { AppPath } from '@/types/AppPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; @@ -75,7 +76,7 @@ export const useSignInUp = (form: UseFormReturn
) => { }, onError: (error) => { enqueueSnackBar(`${error.message}`, { - variant: 'error', + variant: SnackBarVariant.Error, }); }, onCompleted: (data) => { @@ -124,7 +125,7 @@ export const useSignInUp = (form: UseFormReturn) => { navigateAfterSignInUp(currentWorkspace, currentWorkspaceMember); } catch (err: any) { enqueueSnackBar(err?.message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }, diff --git a/packages/twenty-front/src/modules/error-handler/components/PromiseRejectionEffect.tsx b/packages/twenty-front/src/modules/error-handler/components/PromiseRejectionEffect.tsx index 7dc75ac6c..1b5003201 100644 --- a/packages/twenty-front/src/modules/error-handler/components/PromiseRejectionEffect.tsx +++ b/packages/twenty-front/src/modules/error-handler/components/PromiseRejectionEffect.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect } from 'react'; import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; export const PromiseRejectionEffect = () => { @@ -15,12 +16,12 @@ export const PromiseRejectionEffect = () => { enqueueSnackBar( `Error with custom object that cannot be found : ${event.reason}`, { - variant: 'error', + variant: SnackBarVariant.Error, }, ); } else { enqueueSnackBar(`Error: ${event.reason}`, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }, diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useFindManyObjectMetadataItems.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useFindManyObjectMetadataItems.ts index 203550cb6..4bf7c29d9 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useFindManyObjectMetadataItems.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useFindManyObjectMetadataItems.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { useQuery } from '@apollo/client'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { FieldFilter, @@ -43,7 +44,7 @@ export const useFindManyObjectMetadataItems = ({ enqueueSnackBar( `Error during useFindManyObjectMetadataItems, ${error.message}`, { - variant: 'error', + variant: SnackBarVariant.Error, }, ); }, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts index 9677fed11..4bf2d0c1e 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts @@ -9,6 +9,7 @@ import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/ import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDuplicatesRecordsQuery'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { getFindDuplicateRecordsQueryResponseField } from '@/object-record/utils/getFindDuplicateRecordsQueryResponseField'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { logError } from '~/utils/logError'; @@ -54,7 +55,7 @@ export const useFindDuplicateRecords = ({ enqueueSnackBar( `Error during useFindDuplicateRecords for "${objectMetadataItem.nameSingular}", ${error.message}`, { - variant: 'error', + variant: SnackBarVariant.Error, }, ); }, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts index fe105deaa..9c090f213 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts @@ -17,6 +17,7 @@ import { RecordGqlOperationVariables } from '@/object-record/graphql/types/Recor import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { isDefined } from '~/utils/isDefined'; import { logError } from '~/utils/logError'; @@ -116,7 +117,7 @@ export const useFindManyRecords = ({ enqueueSnackBar( `Error during useFindManyRecords for "${objectMetadataItem.namePlural}", ${error.message}`, { - variant: 'error', + variant: SnackBarVariant.Error, }, ); }, @@ -192,7 +193,7 @@ export const useFindManyRecords = ({ enqueueSnackBar( `Error during fetchMoreObjects for "${objectMetadataItem.namePlural}", ${error}`, { - variant: 'error', + variant: SnackBarVariant.Error, }, ); } finally { diff --git a/packages/twenty-front/src/modules/object-record/record-field/components/LightCopyIconButton.tsx b/packages/twenty-front/src/modules/object-record/record-field/components/LightCopyIconButton.tsx index fe2db91ac..61b6b0fb6 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/components/LightCopyIconButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/components/LightCopyIconButton.tsx @@ -2,6 +2,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconCopy } from 'twenty-ui'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; @@ -24,7 +25,7 @@ export const LightCopyIconButton = ({ copyText }: LightCopyIconButtonProps) => { Icon={IconCopy} onClick={() => { enqueueSnackBar('Text copied to clipboard', { - variant: 'success', + variant: SnackBarVariant.Success, icon: , duration: 2000, }); diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/useSpreadsheetRecordImport.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/useSpreadsheetRecordImport.ts index d426ba6bc..700cc4462 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/useSpreadsheetRecordImport.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/useSpreadsheetRecordImport.ts @@ -6,6 +6,7 @@ import { useCreateManyRecords } from '@/object-record/hooks/useCreateManyRecords import { getSpreadSheetValidation } from '@/object-record/spreadsheet-import/util/getSpreadSheetValidation'; import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport'; import { SpreadsheetOptions, Validation } from '@/spreadsheet-import/types'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { isDefined } from '~/utils/isDefined'; @@ -163,7 +164,7 @@ export const useSpreadsheetRecordImport = (objectNameSingular: string) => { await createManyRecords(createInputs); } catch (error: any) { enqueueSnackBar(error?.message || 'Something went wrong', { - variant: 'error', + variant: SnackBarVariant.Error, }); } }, diff --git a/packages/twenty-front/src/modules/settings/developers/components/ApiKeyInput.tsx b/packages/twenty-front/src/modules/settings/developers/components/ApiKeyInput.tsx index 5ae7e180a..e4f3f7b43 100644 --- a/packages/twenty-front/src/modules/settings/developers/components/ApiKeyInput.tsx +++ b/packages/twenty-front/src/modules/settings/developers/components/ApiKeyInput.tsx @@ -2,6 +2,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconCopy } from 'twenty-ui'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Button } from '@/ui/input/button/components/Button'; import { TextInput } from '@/ui/input/components/TextInput'; @@ -32,7 +33,7 @@ export const ApiKeyInput = ({ apiKey }: ApiKeyInputProps) => { title="Copy" onClick={() => { enqueueSnackBar('Api Key copied to clipboard', { - variant: 'success', + variant: SnackBarVariant.Success, icon: , duration: 2000, }); 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 index ccf7c4223..b0cb9cb5f 100644 --- 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 @@ -20,6 +20,7 @@ import { getConnectionDbName } from '@/settings/integrations/utils/getConnection import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Info } from '@/ui/display/info/components/Info'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; import { @@ -91,7 +92,7 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({ ); } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/modules/settings/profile/components/ChangePassword.tsx b/packages/twenty-front/src/modules/settings/profile/components/ChangePassword.tsx index 4066bfba8..5c3344b40 100644 --- a/packages/twenty-front/src/modules/settings/profile/components/ChangePassword.tsx +++ b/packages/twenty-front/src/modules/settings/profile/components/ChangePassword.tsx @@ -2,6 +2,7 @@ import { useRecoilValue } from 'recoil'; import { H2Title } from 'twenty-ui'; import { currentUserState } from '@/auth/states/currentUserState'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Button } from '@/ui/input/button/components/Button'; import { useEmailPasswordResetLinkMutation } from '~/generated/graphql'; @@ -16,7 +17,7 @@ export const ChangePassword = () => { const handlePasswordResetClick = async () => { if (!currentUser?.email) { enqueueSnackBar('Invalid email', { - variant: 'error', + variant: SnackBarVariant.Error, }); return; } @@ -29,16 +30,16 @@ export const ChangePassword = () => { }); if (data?.emailPasswordResetLink?.success === true) { enqueueSnackBar('Password reset link has been sent to the email', { - variant: 'success', + variant: SnackBarVariant.Success, }); } else { enqueueSnackBar('There was some issue', { - variant: 'error', + variant: SnackBarVariant.Error, }); } } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/modules/settings/workspace/components/ToggleImpersonate.tsx b/packages/twenty-front/src/modules/settings/workspace/components/ToggleImpersonate.tsx index 453cfc21d..2b700540d 100644 --- a/packages/twenty-front/src/modules/settings/workspace/components/ToggleImpersonate.tsx +++ b/packages/twenty-front/src/modules/settings/workspace/components/ToggleImpersonate.tsx @@ -1,6 +1,7 @@ import { useRecoilState } from 'recoil'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Toggle } from '@/ui/input/components/Toggle'; import { useUpdateWorkspaceMutation } from '~/generated/graphql'; @@ -32,7 +33,7 @@ export const ToggleImpersonate = () => { }); } catch (err: any) { enqueueSnackBar(err?.message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep.tsx index 8bd9a0538..547e8d928 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep.tsx @@ -12,6 +12,7 @@ import { setColumn } from '@/spreadsheet-import/utils/setColumn'; import { setIgnoreColumn } from '@/spreadsheet-import/utils/setIgnoreColumn'; import { setSubColumn } from '@/spreadsheet-import/utils/setSubColumn'; import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Modal } from '@/ui/layout/modal/components/Modal'; @@ -170,7 +171,7 @@ export const MatchColumnsStep = ({ } else if (index === existingFieldIndex) { enqueueSnackBar('Columns cannot duplicate', { title: 'Another column unselected', - variant: 'error', + variant: SnackBarVariant.Error, }); return setColumn(column); } else { diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadFlow.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadFlow.tsx index 7e6c07e92..407ae1c00 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadFlow.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadFlow.tsx @@ -8,6 +8,7 @@ import { RawData } from '@/spreadsheet-import/types'; import { exceedsMaxRecords } from '@/spreadsheet-import/utils/exceedsMaxRecords'; import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook'; import { CircularProgressBar } from '@/ui/feedback/progress-bar/components/CircularProgressBar'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Modal } from '@/ui/layout/modal/components/Modal'; @@ -80,7 +81,7 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => { (description: string) => { enqueueSnackBar(description, { title: 'Error', - variant: 'error', + variant: SnackBarVariant.Error, }); }, [enqueueSnackBar], diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadStep/components/DropZone.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadStep/components/DropZone.tsx index 594264b63..8506bcda1 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadStep/components/DropZone.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/UploadStep/components/DropZone.tsx @@ -5,6 +5,7 @@ import * as XLSX from 'xlsx-ugnis'; import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal'; import { readFileAsync } from '@/spreadsheet-import/utils/readFilesAsync'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; @@ -114,7 +115,7 @@ export const DropZone = ({ onContinue, isLoading }: DropZoneProps) => { fileRejections.forEach((fileRejection) => { enqueueSnackBar(fileRejection.errors[0].message, { title: `${fileRejection.file.name} upload rejected`, - variant: 'error', + variant: SnackBarVariant.Error, }); }); }, diff --git a/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/ProgressBar.tsx b/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/ProgressBar.tsx index b677e9d2f..d30e35009 100644 --- a/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/ProgressBar.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/ProgressBar.tsx @@ -1,110 +1,43 @@ -import { - forwardRef, - useCallback, - useEffect, - useImperativeHandle, - useRef, -} from 'react'; -import { useTheme } from '@emotion/react'; +import { useState } from 'react'; import styled from '@emotion/styled'; -import { AnimationControls, motion, useAnimation } from 'framer-motion'; +import { motion } from 'framer-motion'; export type ProgressBarProps = { - duration?: number; - delay?: number; - easing?: string; - barHeight?: number; - barColor?: string; - autoStart?: boolean; className?: string; + color?: string; + value: number; }; export type StyledBarProps = { - barHeight?: number; className?: string; }; -export type ProgressBarControls = AnimationControls & { - start: () => Promise; - pause: () => Promise; -}; - const StyledBar = styled.div` - height: ${({ barHeight }) => barHeight}px; + height: ${({ theme }) => theme.spacing(2)}; overflow: hidden; width: 100%; `; -const StyledBarFilling = styled(motion.div)` +const StyledBarFilling = styled(motion.div)<{ color?: string }>` + background-color: ${({ color, theme }) => color ?? theme.font.color.primary}; height: 100%; - width: 100%; `; -export const ProgressBar = forwardRef( - ( - { - duration = 3, - delay = 0, - easing = 'easeInOut', - barHeight = 24, - barColor, - autoStart = true, - className, - }, - ref, - ) => { - const theme = useTheme(); +export const ProgressBar = ({ className, color, value }: ProgressBarProps) => { + const [initialValue] = useState(value); - const controls = useAnimation(); - // eslint-disable-next-line @nx/workspace-no-state-useref - const startTimestamp = useRef(0); - // eslint-disable-next-line @nx/workspace-no-state-useref - const remainingTime = useRef(duration); - - const start = useCallback(async () => { - startTimestamp.current = Date.now(); - return controls.start({ - scaleX: 0, - transition: { - duration: remainingTime.current / 1000, // convert ms to s for framer-motion - delay: delay / 1000, // likewise - ease: easing, - }, - }); - }, [controls, delay, easing]); - - useImperativeHandle(ref, () => ({ - ...controls, - start: async () => { - return start(); - }, - pause: async () => { - const elapsed = Date.now() - startTimestamp.current; - - remainingTime.current = remainingTime.current - elapsed; - return controls.stop(); - }, - })); - - useEffect(() => { - if (autoStart) { - start(); - } - }, [controls, delay, duration, easing, autoStart, start]); - - return ( - - - - ); - }, -); + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/__stories__/ProgressBar.stories.tsx b/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/__stories__/ProgressBar.stories.tsx index bfd7f4287..1655518de 100644 --- a/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/__stories__/ProgressBar.stories.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/progress-bar/components/__stories__/ProgressBar.stories.tsx @@ -1,60 +1,49 @@ import { Meta, StoryObj } from '@storybook/react'; -import { CatalogDecorator, CatalogStory, ComponentDecorator } from 'twenty-ui'; +import { ComponentDecorator } from 'twenty-ui'; + +import { useProgressAnimation } from '@/ui/feedback/progress-bar/hooks/useProgressAnimation'; import { ProgressBar } from '../ProgressBar'; const meta: Meta = { title: 'UI/Feedback/ProgressBar/ProgressBar', component: ProgressBar, - args: { - duration: 10000, + decorators: [ComponentDecorator], + argTypes: { + className: { control: false }, + value: { control: { type: 'range', min: 0, max: 100, step: 1 } }, }, }; export default meta; type Story = StoryObj; -const args = {}; -const defaultArgTypes = { - control: false, -}; + export const Default: Story = { - args, - decorators: [ComponentDecorator], + args: { + value: 75, + }, }; -export const Catalog: CatalogStory = { - args: { - ...args, - }, +export const Animated: Story = { argTypes: { - barHeight: defaultArgTypes, - barColor: defaultArgTypes, - autoStart: defaultArgTypes, + value: { control: false }, }, - parameters: { - catalog: { - dimensions: [ - { - name: 'animation', - values: [true, false], - props: (autoStart: string) => ({ autoStart: Boolean(autoStart) }), - labels: (autoStart: string) => `AutoStart: ${autoStart}`, + decorators: [ + (Story) => { + const { value } = useProgressAnimation({ + autoPlay: true, + initialValue: 0, + finalValue: 100, + options: { + duration: 10000, }, - { - name: 'colors', - values: [undefined, 'blue'], - props: (barColor: string) => ({ barColor }), - labels: (color: string) => `Color: ${color ?? 'default'}`, - }, - { - name: 'sizes', - values: [undefined, 10], - props: (barHeight: number) => ({ barHeight }), - labels: (size: number) => `Size: ${size ? size + ' px' : 'default'}`, - }, - ], + }); + + return ; }, + ], + parameters: { + chromatic: { disableSnapshot: true }, }, - decorators: [CatalogDecorator], }; diff --git a/packages/twenty-front/src/modules/ui/feedback/progress-bar/hooks/useProgressAnimation.ts b/packages/twenty-front/src/modules/ui/feedback/progress-bar/hooks/useProgressAnimation.ts new file mode 100644 index 000000000..9c09f6107 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/feedback/progress-bar/hooks/useProgressAnimation.ts @@ -0,0 +1,58 @@ +import { useCallback, useEffect, useState } from 'react'; +import { millisecondsToSeconds } from 'date-fns'; +import { + animate, + AnimationPlaybackControls, + ValueAnimationTransition, +} from 'framer-motion'; + +import { isDefined } from '~/utils/isDefined'; + +export const useProgressAnimation = ({ + autoPlay = true, + initialValue = 0, + finalValue = 100, + options, +}: { + autoPlay?: boolean; + initialValue?: number; + finalValue?: number; + options?: ValueAnimationTransition; +}) => { + const [animation, setAnimation] = useState< + AnimationPlaybackControls | undefined + >(); + const [value, setValue] = useState(initialValue); + + const startAnimation = useCallback(() => { + if (isDefined(animation)) return; + + const duration = isDefined(options?.duration) + ? millisecondsToSeconds(options.duration) + : undefined; + + setAnimation( + animate(initialValue, finalValue, { + ...options, + duration, + onUpdate: (nextValue) => { + if (value === nextValue) return; + setValue(nextValue); + options?.onUpdate?.(nextValue); + }, + }), + ); + }, [animation, finalValue, initialValue, options, value]); + + useEffect(() => { + if (autoPlay && !animation) { + startAnimation(); + } + }, [animation, autoPlay, startAnimation]); + + return { + animation, + startAnimation, + value, + }; +}; diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx index 07e0c52dd..350b725e3 100644 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx @@ -1,184 +1,192 @@ -import { useCallback, useMemo, useRef } from 'react'; +import { ComponentPropsWithoutRef, ReactNode, useMemo } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { IconAlertTriangle, IconX } from 'twenty-ui'; - +import { isUndefined } from '@sniptt/guards'; import { - ProgressBar, - ProgressBarControls, -} from '@/ui/feedback/progress-bar/components/ProgressBar'; -import { RGBA } from '@/ui/theme/constants/Rgba'; + IconAlertTriangle, + IconInfoCircle, + IconSquareRoundedCheck, + IconX, +} from 'twenty-ui'; + +import { ProgressBar } from '@/ui/feedback/progress-bar/components/ProgressBar'; +import { useProgressAnimation } from '@/ui/feedback/progress-bar/hooks/useProgressAnimation'; +import { LightButton } from '@/ui/input/button/components/LightButton'; +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { isDefined } from '~/utils/isDefined'; -import { usePausableTimeout } from '../hooks/usePausableTimeout'; +export enum SnackBarVariant { + Default = 'default', + Error = 'error', + Success = 'success', + Info = 'info', + Warning = 'warning', +} -const StyledMotionContainer = styled.div>` - align-items: center; - background-color: ${({ theme, variant }) => { - switch (variant) { - case 'error': - return theme.snackBar.error.background; - case 'success': - return theme.snackBar.success.background; - case 'info': - default: - return theme.color.gray80; - } - }}; - border-radius: ${({ theme }) => theme.border.radius.sm}; +export type SnackBarProps = Pick< + ComponentPropsWithoutRef<'div'>, + 'id' | 'title' +> & { + className?: string; + progress?: number; + duration?: number; + icon?: ReactNode; + message?: string; + onCancel?: () => void; + onClose?: () => void; + role?: 'alert' | 'status'; + variant?: SnackBarVariant; +}; + +const StyledContainer = styled.div` + backdrop-filter: ${({ theme }) => theme.blur.light}; + background-color: ${({ theme }) => theme.background.transparent.primary}; + border-radius: ${({ theme }) => theme.border.radius.md}; box-shadow: ${({ theme }) => theme.boxShadow.strong}; - color: ${({ theme, variant }) => { - switch (variant) { - case 'error': - return theme.snackBar.error.color; - case 'success': - return theme.snackBar.success.color; - case 'info': - default: - return theme.grayScale.gray0; - } - }}; + box-sizing: border-box; cursor: pointer; - display: flex; - height: 40px; - overflow: hidden; + height: 61px; padding: ${({ theme }) => theme.spacing(2)}; - pointer-events: auto; position: relative; + width: 296px; `; -const StyledIconContainer = styled.div` - display: flex; - margin-right: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledProgressBarContainer = styled.div` - height: 5px; +const StyledProgressBar = styled(ProgressBar)` + bottom: 0; + height: auto; left: 0; position: absolute; right: 0; top: 0; + pointer-events: none; `; -const StyledCloseButton = styled.button>` +const StyledHeader = styled.div` align-items: center; - background-color: transparent; - border: none; - border-radius: 12px; - color: ${({ theme, variant }) => { - switch (variant) { - case 'error': - return theme.color.red20; - case 'success': - return theme.color.turquoise20; - case 'info': - default: - return theme.grayScale.gray0; - } - }}; - cursor: pointer; + color: ${({ theme }) => theme.font.color.primary}; display: flex; - height: 24px; - justify-content: center; - margin-left: ${({ theme }) => theme.spacing(6)}; - padding-left: ${({ theme }) => theme.spacing(1)}; - padding-right: ${({ theme }) => theme.spacing(1)}; - width: 24px; - - &:hover { - background-color: ${({ theme }) => RGBA(theme.grayScale.gray0, 0.1)}; - } + font-weight: ${({ theme }) => theme.font.weight.medium}; + gap: ${({ theme }) => theme.spacing(2)}; + height: ${({ theme }) => theme.spacing(6)}; + margin-bottom: ${({ theme }) => theme.spacing(1)}; `; -export type SnackbarVariant = 'info' | 'error' | 'success'; +const StyledActions = styled.div` + align-items: center; + display: flex; + margin-left: auto; +`; -export interface SnackBarProps extends React.ComponentPropsWithoutRef<'div'> { - role?: 'alert' | 'status'; - icon?: React.ReactNode; - message?: string; - allowDismiss?: boolean; - duration?: number; - variant?: SnackbarVariant; - children?: React.ReactNode; - className?: string; - onClose?: () => void; -} +const StyledDescription = styled.div` + color: ${({ theme }) => theme.font.color.tertiary}; + font-size: ${({ theme }) => theme.font.size.sm}; + padding-left: ${({ theme }) => theme.spacing(6)}; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 200px; +`; + +const defaultTitleByVariant: Record = { + [SnackBarVariant.Default]: 'Alert', + [SnackBarVariant.Error]: 'Error', + [SnackBarVariant.Info]: 'Info', + [SnackBarVariant.Success]: 'Success', + [SnackBarVariant.Warning]: 'Warning', +}; export const SnackBar = ({ - role = 'status', - icon: iconComponent, - message, - allowDismiss = true, - duration = 6000, - variant = 'info', - children, - onClose, - id, - title, className, + progress: overrideProgressValue, + duration = 6000, + icon: iconComponent, + id, + message, + onCancel, + onClose, + role = 'status', + variant = SnackBarVariant.Default, + title = defaultTitleByVariant[variant], }: SnackBarProps) => { const theme = useTheme(); - - // eslint-disable-next-line @nx/workspace-no-state-useref - const progressBarRef = useRef(null); - - const closeSnackbar = useCallback(() => { - onClose && onClose(); - }, [onClose]); - - const { pauseTimeout, resumeTimeout } = usePausableTimeout( - closeSnackbar, - duration, - ); + const { animation: progressAnimation, value: progressValue } = + useProgressAnimation({ + autoPlay: isUndefined(overrideProgressValue), + initialValue: isDefined(overrideProgressValue) + ? overrideProgressValue + : 100, + finalValue: 0, + options: { duration, onComplete: onClose }, + }); const icon = useMemo(() => { if (isDefined(iconComponent)) { return iconComponent; } - switch (variant) { - case 'error': - return ( - - ); - case 'success': - case 'info': - default: - return null; - } - }, [iconComponent, theme.icon.size.md, variant]); + const ariaLabel = defaultTitleByVariant[variant]; + const color = theme.snackBar[variant].color; + const size = theme.icon.size.md; - const onMouseEnter = () => { - progressBarRef.current?.pause(); - pauseTimeout(); + switch (variant) { + case SnackBarVariant.Error: + return ( + + ); + case SnackBarVariant.Info: + return ; + case SnackBarVariant.Success: + return ( + + ); + case SnackBarVariant.Warning: + return ( + + ); + default: + return ( + + ); + } + }, [iconComponent, theme.icon.size.md, theme.snackBar, variant]); + + const handleMouseEnter = () => { + if (progressAnimation?.state === 'running') { + progressAnimation.pause(); + } }; - const onMouseLeave = () => { - progressBarRef.current?.start(); - resumeTimeout(); + const handleMouseLeave = () => { + if (progressAnimation?.state === 'paused') { + progressAnimation.play(); + } }; return ( - - - - - {icon && {icon}} - {children ? children : message} - {allowDismiss && ( - - - - )} - + + + {icon} + {title} + + {!!onCancel && } + {!!onClose && ( + + )} + + + {message && {message}} + ); }; diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBarProvider.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBarProvider.tsx index 9e7e769e9..b0e8b51de 100644 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBarProvider.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBarProvider.tsx @@ -1,57 +1,39 @@ import styled from '@emotion/styled'; -import { motion, useReducedMotion } from 'framer-motion'; +import { AnimatePresence, motion } from 'framer-motion'; +import { MOBILE_VIEWPORT } from 'twenty-ui'; import { useSnackBarManagerScopedStates } from '@/ui/feedback/snack-bar-manager/hooks/internal/useSnackBarManagerScopedStates'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SnackBar } from './SnackBar'; + const StyledSnackBarContainer = styled.div` display: flex; flex-direction: column; position: fixed; - right: 0; - top: 0; - z-index: 99999999; -`; + right: ${({ theme }) => theme.spacing(3)}; + bottom: ${({ theme }) => theme.spacing(3)}; + z-index: ${({ theme }) => theme.lastLayerZIndex}; -const StyledSnackBarMotionContainer = styled(motion.div)` - margin-right: ${({ theme }) => theme.spacing(3)}; - margin-top: ${({ theme }) => theme.spacing(3)}; + @media (max-width: ${MOBILE_VIEWPORT}px) { + bottom: ${({ theme }) => theme.spacing(16)}; + right: 50%; + transform: translateX(50%); + } `; const variants = { - initial: { + out: { opacity: 0, - y: -40, + y: 40, }, - animate: { + in: { opacity: 1, y: 0, }, - exit: { - opacity: 0, - y: -40, - }, -}; - -const reducedVariants = { - initial: { - opacity: 0, - y: -40, - }, - animate: { - opacity: 1, - y: 0, - }, - exit: { - opacity: 0, - y: -40, - }, }; export const SnackBarProvider = ({ children }: React.PropsWithChildren) => { - const reducedMotion = useReducedMotion(); - const { snackBarInternal } = useSnackBarManagerScopedStates(); const { handleSnackBarClose } = useSnackBar(); @@ -59,24 +41,26 @@ export const SnackBarProvider = ({ children }: React.PropsWithChildren) => { <> {children} - {snackBarInternal.queue.map( - ({ duration, icon, id, message, title, variant }) => ( - - handleSnackBarClose(id)} - /> - - ), - )} + + {snackBarInternal.queue.map( + ({ duration, icon, id, message, title, variant }) => ( + + handleSnackBarClose(id)} + /> + + ), + )} + ); diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/__stories__/SnackBar.stories.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/__stories__/SnackBar.stories.tsx new file mode 100644 index 000000000..3d55b5f50 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/__stories__/SnackBar.stories.tsx @@ -0,0 +1,63 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { fn } from '@storybook/test'; + +import { + CatalogDecorator, + CatalogStory, + ComponentDecorator, +} from '@ui/testing'; +import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; + +import { SnackBar, SnackBarVariant } from '../SnackBar'; + +const meta: Meta = { + title: 'UI/Feedback/SnackBarManager/SnackBar', + component: SnackBar, + decorators: [SnackBarDecorator], + argTypes: { + className: { control: false }, + icon: { control: false }, + }, + args: { + title: 'Lorem ipsum', + message: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec eros tincidunt lacinia.', + onCancel: undefined, + onClose: fn(), + role: 'status', + variant: SnackBarVariant.Default, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + decorators: [ComponentDecorator], + parameters: { + chromatic: { disableSnapshot: true }, + }, +}; + +export const Catalog: CatalogStory = { + args: { + onCancel: fn(), + }, + decorators: [CatalogDecorator], + parameters: { + catalog: { + dimensions: [ + { + name: 'progress', + values: [0, 75, 100], + props: (progress) => ({ progress }), + }, + { + name: 'variants', + values: Object.values(SnackBarVariant), + props: (variant: SnackBarVariant) => ({ variant }), + }, + ], + }, + }, +}; diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/__tests__/usePausableTimeout.test.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/__tests__/usePausableTimeout.test.tsx deleted file mode 100644 index 07d970a38..000000000 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/__tests__/usePausableTimeout.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { act, renderHook } from '@testing-library/react'; - -import { usePausableTimeout } from '@/ui/feedback/snack-bar-manager/hooks/usePausableTimeout'; - -jest.useFakeTimers(); - -describe('usePausableTimeout', () => { - it('should pause and resume timeout', () => { - let callbackExecuted = false; - const callback = () => { - callbackExecuted = true; - }; - - const { result } = renderHook(() => usePausableTimeout(callback, 1000)); - - // timetravel 500ms into the future - act(() => { - jest.advanceTimersByTime(500); - }); - - expect(callbackExecuted).toBe(false); - - act(() => { - result.current.pauseTimeout(); - }); - - // timetravel another 500ms into the future - act(() => { - jest.advanceTimersByTime(500); - }); - - // The callback should not have been executed while paused - expect(callbackExecuted).toBe(false); - - act(() => { - result.current.resumeTimeout(); - }); - - // advance all timers controlled by Jest to their final state - act(() => { - jest.runAllTimers(); - }); - - // The callback should now have been executed - expect(callbackExecuted).toBe(true); - }); -}); diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/usePausableTimeout.ts b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/usePausableTimeout.ts deleted file mode 100644 index 6e58b0887..000000000 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/usePausableTimeout.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useCallback, useEffect, useRef } from 'react'; - -import { isDefined } from '~/utils/isDefined'; - -export const usePausableTimeout = (callback: () => void, delay: number) => { - // eslint-disable-next-line @nx/workspace-no-state-useref - const savedCallback = useRef<() => void>(callback); - // eslint-disable-next-line @nx/workspace-no-state-useref - const remainingTime = useRef(delay); - // eslint-disable-next-line @nx/workspace-no-state-useref - const startTime = useRef(Date.now()); - // eslint-disable-next-line @nx/workspace-no-state-useref - const timeoutId = useRef | null>(null); - - const tick = () => { - if (isDefined(savedCallback.current)) { - savedCallback.current(); - } - }; - - const startTimeout = useCallback(() => { - startTime.current = Date.now(); - timeoutId.current = setTimeout(tick, remainingTime.current); - }, []); - - // Remember the latest callback - useEffect(() => { - savedCallback.current = callback; - }, [callback]); - - // Set up the timeout loop - useEffect(() => { - if (delay !== null) { - startTimeout(); - return () => { - if (isDefined(timeoutId.current)) { - clearTimeout(timeoutId.current); - } - }; - } - }, [delay, startTimeout]); - - const pauseTimeout = () => { - if (isDefined(timeoutId.current)) { - clearTimeout(timeoutId.current); - } - const elapsedTime = Date.now() - startTime.current; - remainingTime.current = remainingTime.current - elapsedTime; - }; - - const resumeTimeout = () => { - startTimeout(); - }; - - return { pauseTimeout, resumeTimeout }; -}; diff --git a/packages/twenty-front/src/modules/ui/theme/constants/Colors.ts b/packages/twenty-front/src/modules/ui/theme/constants/Colors.ts index 022a67b70..c8580a51e 100644 --- a/packages/twenty-front/src/modules/ui/theme/constants/Colors.ts +++ b/packages/twenty-front/src/modules/ui/theme/constants/Colors.ts @@ -1,4 +1,5 @@ -import { MAIN_COLORS } from '@/ui/theme/constants/MainColors'; +import { MAIN_COLORS } from 'twenty-ui'; + import { SECONDARY_COLORS } from '@/ui/theme/constants/SecondaryColors'; export const COLOR = { diff --git a/packages/twenty-front/src/modules/ui/theme/constants/MainColorNames.ts b/packages/twenty-front/src/modules/ui/theme/constants/MainColorNames.ts index 648389ee7..626de706a 100644 --- a/packages/twenty-front/src/modules/ui/theme/constants/MainColorNames.ts +++ b/packages/twenty-front/src/modules/ui/theme/constants/MainColorNames.ts @@ -1,4 +1,4 @@ -import { MAIN_COLORS } from '@/ui/theme/constants/MainColors'; +import { MAIN_COLORS } from 'twenty-ui'; export const MAIN_COLOR_NAMES = Object.keys(MAIN_COLORS) as ThemeColor[]; diff --git a/packages/twenty-front/src/modules/ui/theme/constants/MainColors.ts b/packages/twenty-front/src/modules/ui/theme/constants/MainColors.ts deleted file mode 100644 index d9d969a62..000000000 --- a/packages/twenty-front/src/modules/ui/theme/constants/MainColors.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable @nx/workspace-no-hardcoded-colors */ -import { GRAY_SCALE } from '@/ui/theme/constants/GrayScale'; - -export const MAIN_COLORS = { - green: '#55ef3c', - turquoise: '#15de8f', - sky: '#00e0ff', - blue: '#1961ed', - purple: '#915ffd', - pink: '#f54bd0', - red: '#f83e3e', - orange: '#ff7222', - yellow: '#ffd338', - gray: GRAY_SCALE.gray30, -}; diff --git a/packages/twenty-front/src/modules/ui/theme/constants/OverlayBackground.ts b/packages/twenty-front/src/modules/ui/theme/constants/OverlayBackground.ts index 083133298..e7f7de01c 100644 --- a/packages/twenty-front/src/modules/ui/theme/constants/OverlayBackground.ts +++ b/packages/twenty-front/src/modules/ui/theme/constants/OverlayBackground.ts @@ -1,6 +1,5 @@ import { css } from '@emotion/react'; - -import { ThemeType } from './ThemeLight'; +import { ThemeType } from 'twenty-ui'; export const OVERLAY_BACKGROUND = (props: { theme: ThemeType }) => css` backdrop-filter: blur(12px) saturate(200%) contrast(50%) brightness(130%); diff --git a/packages/twenty-front/src/modules/ui/theme/constants/TextInputStyle.ts b/packages/twenty-front/src/modules/ui/theme/constants/TextInputStyle.ts index 655f4b0e2..640b28a08 100644 --- a/packages/twenty-front/src/modules/ui/theme/constants/TextInputStyle.ts +++ b/packages/twenty-front/src/modules/ui/theme/constants/TextInputStyle.ts @@ -1,6 +1,5 @@ import { css } from '@emotion/react'; - -import { ThemeType } from './ThemeLight'; +import { ThemeType } from 'twenty-ui'; export const TEXT_INPUT_STYLE = (props: { theme: ThemeType }) => css` background-color: transparent; diff --git a/packages/twenty-front/src/modules/ui/theme/constants/ThemeDark.ts b/packages/twenty-front/src/modules/ui/theme/constants/ThemeDark.ts deleted file mode 100644 index a80f03071..000000000 --- a/packages/twenty-front/src/modules/ui/theme/constants/ThemeDark.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ACCENT_DARK } from '@/ui/theme/constants/AccentDark'; -import { BACKGROUND_DARK } from '@/ui/theme/constants/BackgroundDark'; -import { BORDER_DARK } from '@/ui/theme/constants/BorderDark'; -import { BOX_SHADOW_DARK } from '@/ui/theme/constants/BoxShadowDark'; -import { FONT_DARK } from '@/ui/theme/constants/FontDark'; -import { TAG_DARK } from '@/ui/theme/constants/TagDark'; -import { THEME_COMMON } from '@/ui/theme/constants/ThemeCommon'; -import { ThemeType } from '@/ui/theme/constants/ThemeLight'; - -export const THEME_DARK: ThemeType = { - ...THEME_COMMON, - ...{ - accent: ACCENT_DARK, - background: BACKGROUND_DARK, - border: BORDER_DARK, - tag: TAG_DARK, - boxShadow: BOX_SHADOW_DARK, - font: FONT_DARK, - name: 'dark', - }, -}; diff --git a/packages/twenty-front/src/modules/ui/theme/constants/ThemeLight.ts b/packages/twenty-front/src/modules/ui/theme/constants/ThemeLight.ts deleted file mode 100644 index 3f837e48f..000000000 --- a/packages/twenty-front/src/modules/ui/theme/constants/ThemeLight.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ACCENT_LIGHT } from '@/ui/theme/constants/AccentLight'; -import { BACKGROUND_LIGHT } from '@/ui/theme/constants/BackgroundLight'; -import { BORDER_LIGHT } from '@/ui/theme/constants/BorderLight'; -import { BOX_SHADOW_LIGHT } from '@/ui/theme/constants/BoxShadowLight'; -import { FONT_LIGHT } from '@/ui/theme/constants/FontLight'; -import { TAG_LIGHT } from '@/ui/theme/constants/TagLight'; -import { THEME_COMMON } from '@/ui/theme/constants/ThemeCommon'; - -export const THEME_LIGHT = { - ...THEME_COMMON, - ...{ - accent: ACCENT_LIGHT, - background: BACKGROUND_LIGHT, - border: BORDER_LIGHT, - tag: TAG_LIGHT, - boxShadow: BOX_SHADOW_LIGHT, - font: FONT_LIGHT, - name: 'light', - }, -}; - -export type ThemeType = typeof THEME_LIGHT; diff --git a/packages/twenty-front/src/modules/workspace/components/WorkspaceInviteLink.tsx b/packages/twenty-front/src/modules/workspace/components/WorkspaceInviteLink.tsx index a87a9f371..92bdf69af 100644 --- a/packages/twenty-front/src/modules/workspace/components/WorkspaceInviteLink.tsx +++ b/packages/twenty-front/src/modules/workspace/components/WorkspaceInviteLink.tsx @@ -2,6 +2,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconCopy, IconLink } from 'twenty-ui'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Button } from '@/ui/input/button/components/Button'; import { TextInput } from '@/ui/input/components/TextInput'; @@ -40,7 +41,7 @@ export const WorkspaceInviteLink = ({ title="Copy link" onClick={() => { enqueueSnackBar('Link copied to clipboard', { - variant: 'success', + variant: SnackBarVariant.Success, icon: , duration: 2000, }); diff --git a/packages/twenty-front/src/pages/auth/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/auth/ChooseYourPlan.tsx index aca84dad1..4f00f56d1 100644 --- a/packages/twenty-front/src/pages/auth/ChooseYourPlan.tsx +++ b/packages/twenty-front/src/pages/auth/ChooseYourPlan.tsx @@ -11,6 +11,7 @@ import { SubscriptionCard } from '@/billing/components/SubscriptionCard'; import { billingState } from '@/client-config/states/billingState'; import { AppPath } from '@/types/AppPath'; import { Loader } from '@/ui/feedback/loader/components/Loader'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { CardPicker } from '@/ui/input/components/CardPicker'; @@ -131,7 +132,7 @@ export const ChooseYourPlan = () => { enqueueSnackBar( 'Checkout session error. Please retry or contact Twenty team', { - variant: 'error', + variant: SnackBarVariant.Error, }, ); return; diff --git a/packages/twenty-front/src/pages/auth/CreateProfile.tsx b/packages/twenty-front/src/pages/auth/CreateProfile.tsx index fe052b3a8..3c41918ec 100644 --- a/packages/twenty-front/src/pages/auth/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/auth/CreateProfile.tsx @@ -16,6 +16,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; @@ -114,7 +115,7 @@ export const CreateProfile = () => { ); } catch (error: any) { enqueueSnackBar(error?.message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }, diff --git a/packages/twenty-front/src/pages/auth/CreateWorkspace.tsx b/packages/twenty-front/src/pages/auth/CreateWorkspace.tsx index c6ff8f8c9..adea46985 100644 --- a/packages/twenty-front/src/pages/auth/CreateWorkspace.tsx +++ b/packages/twenty-front/src/pages/auth/CreateWorkspace.tsx @@ -18,6 +18,7 @@ import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetada import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { AppPath } from '@/types/AppPath'; import { Loader } from '@/ui/feedback/loader/components/Loader'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; @@ -93,7 +94,7 @@ export const CreateWorkspace = () => { }, 20); } catch (error: any) { enqueueSnackBar(error?.message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }, diff --git a/packages/twenty-front/src/pages/auth/Invite.tsx b/packages/twenty-front/src/pages/auth/Invite.tsx index c6ea7afc7..eb6baa700 100644 --- a/packages/twenty-front/src/pages/auth/Invite.tsx +++ b/packages/twenty-front/src/pages/auth/Invite.tsx @@ -12,6 +12,7 @@ import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspace import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { AppPath } from '@/types/AppPath'; import { Loader } from '@/ui/feedback/loader/components/Loader'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { useWorkspaceSwitching } from '@/ui/navigation/navigation-drawer/hooks/useWorkspaceSwitching'; @@ -61,7 +62,7 @@ export const Invite = () => { !workspaceFromInviteHashLoading ) { enqueueSnackBar('workspace does not exist', { - variant: 'error', + variant: SnackBarVariant.Error, }); if (isDefined(currentWorkspace)) { navigate(AppPath.Index); @@ -76,7 +77,7 @@ export const Invite = () => { enqueueSnackBar( `You already belong to ${workspaceFromInviteHash?.displayName} workspace`, { - variant: 'info', + variant: SnackBarVariant.Info, }, ); navigate(AppPath.Index); diff --git a/packages/twenty-front/src/pages/auth/PasswordReset.tsx b/packages/twenty-front/src/pages/auth/PasswordReset.tsx index 332fb5a07..01e917b4a 100644 --- a/packages/twenty-front/src/pages/auth/PasswordReset.tsx +++ b/packages/twenty-front/src/pages/auth/PasswordReset.tsx @@ -16,6 +16,7 @@ import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp'; import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex'; import { AppPath } from '@/types/AppPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; @@ -95,7 +96,7 @@ export const PasswordReset = () => { skip: !passwordResetToken, onError: (error) => { enqueueSnackBar(error?.message ?? 'Token Invalid', { - variant: 'error', + variant: SnackBarVariant.Error, }); if (!isLoggedIn) { navigate(AppPath.SignInUp); @@ -128,14 +129,14 @@ export const PasswordReset = () => { if (!data?.updatePasswordViaResetToken.success) { enqueueSnackBar('There was an error while updating password.', { - variant: 'error', + variant: SnackBarVariant.Error, }); return; } if (isLoggedIn) { enqueueSnackBar('Password has been updated', { - variant: 'success', + variant: SnackBarVariant.Success, }); navigate(AppPath.Index); return; @@ -152,7 +153,7 @@ export const PasswordReset = () => { enqueueSnackBar( (err as Error)?.message || 'An error occurred while updating password', { - variant: 'error', + variant: SnackBarVariant.Error, }, ); } diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index 4641972d1..a0fdad919 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -19,6 +19,7 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain import { SupportChat } from '@/support/components/SupportChat'; import { AppPath } from '@/types/AppPath'; import { Info } from '@/ui/display/info/components/Info'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; 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'; @@ -127,13 +128,13 @@ export const SettingsBilling = () => { setCurrentWorkspace(newCurrentWorkspace); } enqueueSnackBar(`Subscription has been switched ${switchingInfo.to}`, { - variant: 'success', + variant: SnackBarVariant.Success, }); } catch (error: any) { enqueueSnackBar( `Error while switching subscription ${switchingInfo.to}.`, { - variant: 'error', + variant: SnackBarVariant.Error, }, ); } diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsNewObject.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsNewObject.tsx index dfbddcd7c..f6a338810 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsNewObject.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsNewObject.tsx @@ -16,6 +16,7 @@ import { import { settingsCreateObjectInputSchema } from '@/settings/data-model/validation-schemas/settingsCreateObjectInputSchema'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; @@ -58,7 +59,7 @@ export const SettingsNewObject = () => { ); } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectEdit.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectEdit.tsx index 9e0fdb800..e12103ee9 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectEdit.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectEdit.tsx @@ -22,6 +22,7 @@ import { settingsUpdateObjectInputSchema } from '@/settings/data-model/validatio import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Button } from '@/ui/input/button/components/Button'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; @@ -83,7 +84,7 @@ export const SettingsObjectEdit = () => { navigate(`${settingsObjectsPagePath}/${getObjectSlug(formValues)}`); } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx index e464b0ab2..f74e69c54 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx @@ -24,6 +24,7 @@ import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fi import { SettingsDataModelFieldTypeSelect } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect'; import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema'; import { AppPath } from '@/types/AppPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Button } from '@/ui/input/button/components/Button'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; @@ -136,7 +137,7 @@ export const SettingsObjectFieldEdit = () => { navigate(`/settings/objects/${objectSlug}`); } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep2.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep2.tsx index dce0de098..3b09a2113 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep2.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep2.tsx @@ -25,6 +25,7 @@ import { SettingsDataModelFieldTypeSelect } from '@/settings/data-model/fields/f import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema'; import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType'; import { AppPath } from '@/types/AppPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; @@ -262,7 +263,7 @@ export const SettingsObjectNewFieldStep2 = () => { navigate(`/settings/objects/${objectSlug}`); } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx index 88f0e5fd5..4e70226b4 100644 --- a/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx +++ b/packages/twenty-front/src/pages/settings/integrations/SettingsIntegrationNewDatabaseConnection.tsx @@ -20,6 +20,7 @@ import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/ import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; @@ -123,7 +124,7 @@ export const SettingsIntegrationNewDatabaseConnection = () => { ); } catch (error) { enqueueSnackBar((error as Error).message, { - variant: 'error', + variant: SnackBarVariant.Error, }); } }; diff --git a/packages/twenty-front/tsup.ui.index.tsx b/packages/twenty-front/tsup.ui.index.tsx index 88c77cb2e..edb0ae481 100644 --- a/packages/twenty-front/tsup.ui.index.tsx +++ b/packages/twenty-front/tsup.ui.index.tsx @@ -1,10 +1,7 @@ -import { ThemeType } from '@/ui/theme/constants/ThemeLight'; +import { ThemeType } from 'twenty-ui'; export { ThemeProvider } from '@emotion/react'; -export { THEME_DARK } from './src/modules/ui/theme/constants/ThemeDark'; -export { THEME_LIGHT } from './src/modules/ui/theme/constants/ThemeLight'; - declare module '@emotion/react' { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface Theme extends ThemeType {} diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index d8baf3027..f423ea666 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -133,6 +133,7 @@ export { IconSend, IconSettings, IconSortDescending, + IconSquareRoundedCheck, IconTable, IconTag, IconTags, diff --git a/packages/twenty-ui/src/theme/constants/SnackBarCommon.ts b/packages/twenty-ui/src/theme/constants/SnackBarCommon.ts new file mode 100644 index 000000000..8fe725611 --- /dev/null +++ b/packages/twenty-ui/src/theme/constants/SnackBarCommon.ts @@ -0,0 +1,21 @@ +import { MAIN_COLORS } from '@ui/theme/constants/MainColors'; +import { RGBA } from '@ui/theme/constants/Rgba'; + +export const SNACK_BAR_COMMON = { + success: { + color: MAIN_COLORS.turquoise, + backgroundColor: RGBA(MAIN_COLORS.turquoise, 0.04), + }, + error: { + color: MAIN_COLORS.red, + backgroundColor: RGBA(MAIN_COLORS.red, 0.04), + }, + warning: { + color: MAIN_COLORS.orange, + backgroundColor: RGBA(MAIN_COLORS.orange, 0.04), + }, + info: { + color: MAIN_COLORS.blue, + backgroundColor: RGBA(MAIN_COLORS.blue, 0.04), + }, +}; diff --git a/packages/twenty-ui/src/theme/constants/SnackBarDark.ts b/packages/twenty-ui/src/theme/constants/SnackBarDark.ts new file mode 100644 index 000000000..4148b99dc --- /dev/null +++ b/packages/twenty-ui/src/theme/constants/SnackBarDark.ts @@ -0,0 +1,11 @@ +import { BACKGROUND_DARK } from '@ui/theme/constants/BackgroundDark'; +import { FONT_DARK } from '@ui/theme/constants/FontDark'; +import { SNACK_BAR_COMMON } from '@ui/theme/constants/SnackBarCommon'; + +export const SNACK_BAR_DARK = { + ...SNACK_BAR_COMMON, + default: { + color: FONT_DARK.color.primary, + backgroundColor: BACKGROUND_DARK.transparent.light, + }, +}; diff --git a/packages/twenty-ui/src/theme/constants/SnackBarLight.ts b/packages/twenty-ui/src/theme/constants/SnackBarLight.ts new file mode 100644 index 000000000..15230a03d --- /dev/null +++ b/packages/twenty-ui/src/theme/constants/SnackBarLight.ts @@ -0,0 +1,11 @@ +import { BACKGROUND_LIGHT } from '@ui/theme/constants/BackgroundLight'; +import { FONT_LIGHT } from '@ui/theme/constants/FontLight'; +import { SNACK_BAR_COMMON } from '@ui/theme/constants/SnackBarCommon'; + +export const SNACK_BAR_LIGHT = { + ...SNACK_BAR_COMMON, + default: { + color: FONT_LIGHT.color.primary, + backgroundColor: BACKGROUND_LIGHT.transparent.light, + }, +}; diff --git a/packages/twenty-ui/src/theme/constants/ThemeCommon.ts b/packages/twenty-ui/src/theme/constants/ThemeCommon.ts index 8c450fb87..9ba6fadef 100644 --- a/packages/twenty-ui/src/theme/constants/ThemeCommon.ts +++ b/packages/twenty-ui/src/theme/constants/ThemeCommon.ts @@ -1,4 +1,3 @@ -/* eslint-disable @nx/workspace-no-hardcoded-colors */ import { ANIMATION } from './Animation'; import { BLUR } from './Blur'; import { COLOR } from './Colors'; @@ -15,20 +14,6 @@ export const THEME_COMMON = { text: TEXT, blur: BLUR, animation: ANIMATION, - snackBar: { - success: { - background: '#16A26B', - color: '#D0F8E9', - }, - error: { - background: '#B43232', - color: '#FED8D8', - }, - info: { - background: COLOR.gray80, - color: GRAY_SCALE.gray0, - }, - }, spacingMultiplicator: 4, spacing: (...args: number[]) => args.map((multiplicator) => `${multiplicator * 4}px`).join(' '), diff --git a/packages/twenty-ui/src/theme/constants/ThemeDark.ts b/packages/twenty-ui/src/theme/constants/ThemeDark.ts index 6f3284f03..248256e7d 100644 --- a/packages/twenty-ui/src/theme/constants/ThemeDark.ts +++ b/packages/twenty-ui/src/theme/constants/ThemeDark.ts @@ -1,4 +1,4 @@ -import { ThemeType } from '..'; +import { SNACK_BAR_DARK, ThemeType } from '..'; import { ACCENT_DARK } from './AccentDark'; import { BACKGROUND_DARK } from './BackgroundDark'; @@ -14,9 +14,10 @@ export const THEME_DARK: ThemeType = { accent: ACCENT_DARK, background: BACKGROUND_DARK, border: BORDER_DARK, - tag: TAG_DARK, boxShadow: BOX_SHADOW_DARK, font: FONT_DARK, name: 'dark', + snackBar: SNACK_BAR_DARK, + tag: TAG_DARK, }, }; diff --git a/packages/twenty-ui/src/theme/constants/ThemeLight.ts b/packages/twenty-ui/src/theme/constants/ThemeLight.ts index 9331102fc..11b4d111c 100644 --- a/packages/twenty-ui/src/theme/constants/ThemeLight.ts +++ b/packages/twenty-ui/src/theme/constants/ThemeLight.ts @@ -1,3 +1,5 @@ +import { SNACK_BAR_LIGHT } from '@ui/theme/constants/SnackBarLight'; + import { ACCENT_LIGHT } from './AccentLight'; import { BACKGROUND_LIGHT } from './BackgroundLight'; import { BORDER_LIGHT } from './BorderLight'; @@ -12,9 +14,10 @@ export const THEME_LIGHT = { accent: ACCENT_LIGHT, background: BACKGROUND_LIGHT, border: BORDER_LIGHT, - tag: TAG_LIGHT, boxShadow: BOX_SHADOW_LIGHT, font: FONT_LIGHT, name: 'light', + snackBar: SNACK_BAR_LIGHT, + tag: TAG_LIGHT, }, }; diff --git a/packages/twenty-ui/src/theme/index.ts b/packages/twenty-ui/src/theme/index.ts index ca92f2bd6..4f603f646 100644 --- a/packages/twenty-ui/src/theme/index.ts +++ b/packages/twenty-ui/src/theme/index.ts @@ -23,6 +23,9 @@ export * from './constants/Modal'; export * from './constants/OverlayBackground'; export * from './constants/Rgba'; export * from './constants/SecondaryColors'; +export * from './constants/SnackBarCommon'; +export * from './constants/SnackBarDark'; +export * from './constants/SnackBarLight'; export * from './constants/TagDark'; export * from './constants/TagLight'; export * from './constants/Text';