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 c74b2e9d71..fae8bdd6c8 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 @@ -9,9 +9,9 @@ import { useContext, useMemo, } from 'react'; -import { Link } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; import { + HorizontalSeparator, IconAlertTriangle, IconInfoCircle, IconSquareRoundedCheck, @@ -19,6 +19,7 @@ import { } from 'twenty-ui/display'; import { ProgressBar, useProgressAnimation } from 'twenty-ui/feedback'; import { LightButton, LightIconButton } from 'twenty-ui/input'; +import { UndecoratedLink } from 'twenty-ui/navigation'; import { MOBILE_VIEWPORT, ThemeContext, @@ -39,9 +40,9 @@ export type SnackBarProps = Pick, 'id'> & { duration?: number; icon?: ReactNode; message: string; - actionText?: string; - actionOnClick?: () => void; - actionTo?: string; + buttonLabel?: string; + buttonOnClick?: () => void; + buttonTo?: string; detailedMessage?: string; onCancel?: () => void; onClose?: () => void; @@ -56,9 +57,9 @@ const StyledContainer = styled.div` border-radius: ${themeCssVariables.border.radius.md}; box-shadow: ${themeCssVariables.boxShadow.strong}; box-sizing: border-box; - cursor: pointer; margin-top: ${themeCssVariables.spacing[2]}; - padding: ${themeCssVariables.spacing[2]}; + padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[2]} + ${themeCssVariables.spacing[1]}; position: relative; width: 296px; @@ -70,12 +71,15 @@ const StyledContainer = styled.div` const StyledProgressBarContainer = styled.div` bottom: 0; - height: auto; left: 0; pointer-events: none; position: absolute; right: 0; top: 0; + + & > [role='progressbar'] { + height: 100%; + } `; const StyledHeader = styled.div` @@ -112,24 +116,15 @@ const StyledDescription = styled.div` width: 200px; `; -const StyledLinkContainer = styled.div` - > a { - color: ${themeCssVariables.font.color.tertiary}; - display: block; - font-size: ${themeCssVariables.font.size.sm}; - max-width: 200px; - overflow: hidden; - padding-left: ${themeCssVariables.spacing[6]}; - text-overflow: ellipsis; - white-space: nowrap; - &:hover { - color: ${themeCssVariables.font.color.secondary}; - } - } +const StyledBottomActionContainer = styled.div` + margin-top: ${themeCssVariables.spacing[2]}; `; -const StyledActionButton = styled.div` - padding-left: ${themeCssVariables.spacing[6]}; +const StyledBottomAction = styled.div` + align-items: center; + display: flex; + justify-content: flex-end; + padding-top: ${themeCssVariables.spacing[1]}; `; const defaultAriaLabelByVariant: Record< @@ -151,9 +146,9 @@ export const SnackBar = ({ id, message, detailedMessage, - actionText, - actionOnClick, - actionTo, + buttonLabel, + buttonOnClick, + buttonTo, onCancel, onClose, role = 'status', @@ -205,15 +200,11 @@ export const SnackBar = ({ }, [iconComponent, variant, i18n, theme.icon.size.md, theme.snackBar]); const handleMouseEnter = () => { - if (progressAnimation?.state === 'running') { - progressAnimation.pause(); - } + progressAnimation?.pause(); }; const handleMouseLeave = () => { - if (progressAnimation?.state === 'paused') { - progressAnimation.play(); - } + progressAnimation?.play(); }; const sanitizedMessage = sanitizeMessageToRenderInSnackbar(message); @@ -251,16 +242,21 @@ export const SnackBar = ({ {isDefined(sanitizedDetailedMessage) && ( {sanitizedDetailedMessage} )} - {actionText && actionTo && ( - - {actionText} - - )} - {actionText && actionOnClick && !actionTo && ( - - - - )} + {isDefined(buttonLabel) && + (isDefined(buttonOnClick) || isDefined(buttonTo)) && ( + + + + {isDefined(buttonTo) ? ( + + + + ) : ( + + )} + + + )} ); }; 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 abd9644d01..d3211d7da0 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 @@ -58,9 +58,9 @@ export const SnackBarProvider = ({ children }: React.PropsWithChildren) => { message, detailedMessage, variant, - actionText, - actionOnClick, - actionTo, + buttonLabel, + buttonOnClick, + buttonTo, }) => ( { message, detailedMessage, variant, - actionText, - actionOnClick, - actionTo, + buttonLabel, + buttonOnClick, + buttonTo, }} onClose={() => 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 index fcbed02210..faefb61249 100644 --- 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 @@ -41,6 +41,33 @@ export const Default: Story = { }, }; +export const WithBottomButton: Story = { + args: { + variant: SnackBarVariant.Error, + message: 'An error has occurred', + detailedMessage: 'Error during useFindManyRecord...', + buttonLabel: 'Open Record', + buttonOnClick: fn(), + }, + decorators: [ComponentDecorator], + parameters: { + chromatic: { disableSnapshot: true }, + }, +}; + +export const SuccessWithButton: Story = { + args: { + variant: SnackBarVariant.Success, + message: 'Record created successfully', + buttonLabel: 'View Record', + buttonOnClick: fn(), + }, + decorators: [ComponentDecorator], + parameters: { + chromatic: { disableSnapshot: true }, + }, +}; + export const Catalog: CatalogStory = { args: { onCancel: fn(), @@ -63,3 +90,22 @@ export const Catalog: CatalogStory = { }, }, }; + +export const CatalogWithButton: CatalogStory = { + args: { + buttonLabel: 'Open Record', + buttonOnClick: fn(), + }, + decorators: [CatalogDecorator], + parameters: { + catalog: { + dimensions: [ + { + name: 'variants', + values: Object.values(SnackBarVariant), + props: (variant: SnackBarVariant) => ({ variant }), + }, + ], + }, + }, +}; diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/buildErrorAction.ts b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/buildErrorAction.ts index 048f42752c..df3fb2c3e9 100644 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/buildErrorAction.ts +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/buildErrorAction.ts @@ -7,7 +7,7 @@ import { type SnackBarOptions } from '@/ui/feedback/snack-bar-manager/states/sna export const buildErrorAction = ( apolloError?: ErrorLike, -): Pick | null => { +): Pick | null => { if (!apolloError) { return null; } @@ -16,8 +16,8 @@ export const buildErrorAction = ( if (isDefined(conflictingRecord)) { return { - actionText: t`View existing record`, - actionTo: getAppPath(AppPath.RecordShowPage, { + buttonLabel: t`View existing record`, + buttonTo: getAppPath(AppPath.RecordShowPage, { objectNameSingular: conflictingRecord.conflictingObjectNameSingular, objectRecordId: conflictingRecord.conflictingRecordId, }),