diff --git a/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBar.tsx b/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBar.tsx index 3a3f5750f6..c42e7a9f09 100644 --- a/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBar.tsx +++ b/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBar.tsx @@ -1,15 +1,24 @@ +import { styled } from '@linaria/react'; +import { useLingui } from '@lingui/react/macro'; +import { AnimatePresence, motion } from 'framer-motion'; +import { useAtomValue } from 'jotai'; +import { useContext } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { IconCheck, IconPaint } from 'twenty-ui/display'; +import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; + +import { LayoutCustomizationBarMenuDropdown } from '@/layout-customization/components/LayoutCustomizationBarMenuDropdown'; +import { LayoutCustomizationBarResetConfirmationModal } from '@/layout-customization/components/LayoutCustomizationBarResetConfirmationModal'; import { useCancelLayoutCustomization } from '@/layout-customization/hooks/useCancelLayoutCustomization'; import { useIsLayoutCustomizationDirty } from '@/layout-customization/hooks/useIsLayoutCustomizationDirty'; import { useSaveLayoutCustomization } from '@/layout-customization/hooks/useSaveLayoutCustomization'; import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState'; +import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; +import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState'; +import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { styled } from '@linaria/react'; -import { useLingui } from '@lingui/react/macro'; -import { AnimatePresence, motion } from 'framer-motion'; -import { useContext } from 'react'; -import { IconCheck, IconPaint } from 'twenty-ui/display'; -import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; +import { PageLayoutType } from '~/generated-metadata/graphql'; const StyledContainer = styled.div` align-items: center; @@ -22,10 +31,27 @@ const StyledContainer = styled.div` width: 100%; `; +const StyledLeftSection = styled.div` + align-items: center; + display: flex; + flex: 1; + gap: ${themeCssVariables.spacing[2]}; +`; + const StyledTitle = styled.span` align-items: center; display: flex; + flex: 1; gap: ${themeCssVariables.spacing[2]}; + justify-content: center; + text-align: center; +`; + +const StyledRightSection = styled.div` + align-items: center; + display: flex; + flex: 1; + justify-content: flex-end; `; const LayoutCustomizationBarContent = () => { @@ -36,6 +62,26 @@ const LayoutCustomizationBarContent = () => { const { cancel } = useCancelLayoutCustomization(); const { isDirty } = useIsLayoutCustomizationDirty(); + const currentPageLayoutId = useAtomStateValue(currentPageLayoutIdState); + const persistedPageLayout = useAtomValue( + pageLayoutPersistedComponentState.atomFamily({ + instanceId: currentPageLayoutId ?? '', + }), + ); + const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector); + + const recordPageLayoutObject = + isDefined(currentPageLayoutId) && + persistedPageLayout?.type === PageLayoutType.RECORD_PAGE + ? objectMetadataItems.find( + (item) => item.id === persistedPageLayout.objectMetadataId, + ) + : undefined; + + const title = isDefined(recordPageLayoutObject) + ? t`${recordPageLayoutObject.labelPlural} layout edition` + : t`Layout customization`; + return ( { }} > + + {isDefined(recordPageLayoutObject) && ( + + )} + - {t`Layout customization`} + {title} - + + + + {isDefined(recordPageLayoutObject) && isDefined(currentPageLayoutId) && ( + + )} ); }; diff --git a/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBarMenuDropdown.tsx b/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBarMenuDropdown.tsx new file mode 100644 index 0000000000..f6876c648a --- /dev/null +++ b/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBarMenuDropdown.tsx @@ -0,0 +1,66 @@ +import { styled } from '@linaria/react'; +import { useLingui } from '@lingui/react/macro'; +import { IconDotsVertical, IconReload } from 'twenty-ui/display'; +import { LightIconButton } from 'twenty-ui/input'; +import { MenuItem } from 'twenty-ui/navigation'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { LAYOUT_CUSTOMIZATION_BAR_DROPDOWN_ID } from '@/layout-customization/constants/LayoutCustomizationBarDropdownId'; +import { RESET_RECORD_PAGE_LAYOUT_MODAL_ID } from '@/layout-customization/constants/ResetRecordPageLayoutModalId'; +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 { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useModal } from '@/ui/layout/modal/hooks/useModal'; + +const StyledInvertedIconButtonWrapper = styled.span` + align-items: center; + display: flex; + + button { + color: ${themeCssVariables.font.color.inverted}; + } + + button:hover { + background: ${themeCssVariables.background.transparent.light}; + } +`; + +export const LayoutCustomizationBarMenuDropdown = () => { + const { t } = useLingui(); + const { closeDropdown } = useCloseDropdown(); + const { openModal } = useModal(); + + const handleResetClick = () => { + closeDropdown(LAYOUT_CUSTOMIZATION_BAR_DROPDOWN_ID); + openModal(RESET_RECORD_PAGE_LAYOUT_MODAL_ID); + }; + + return ( + + + + } + dropdownComponents={ + + + + + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBarResetConfirmationModal.tsx b/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBarResetConfirmationModal.tsx new file mode 100644 index 0000000000..3469053de9 --- /dev/null +++ b/packages/twenty-front/src/modules/layout-customization/components/LayoutCustomizationBarResetConfirmationModal.tsx @@ -0,0 +1,36 @@ +import { useLingui } from '@lingui/react/macro'; + +import { RESET_RECORD_PAGE_LAYOUT_MODAL_ID } from '@/layout-customization/constants/ResetRecordPageLayoutModalId'; +import { useRefreshPageLayoutAfterReset } from '@/page-layout/hooks/useRefreshPageLayoutAfterReset'; +import { useResetPageLayoutToDefault } from '@/page-layout/hooks/useResetPageLayoutToDefault'; +import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; + +type LayoutCustomizationBarResetConfirmationModalProps = { + pageLayoutId: string; +}; + +export const LayoutCustomizationBarResetConfirmationModal = ({ + pageLayoutId, +}: LayoutCustomizationBarResetConfirmationModalProps) => { + const { t } = useLingui(); + + const { resetPageLayoutToDefault } = useResetPageLayoutToDefault(); + const { refreshPageLayoutAfterReset } = + useRefreshPageLayoutAfterReset(pageLayoutId); + + const handleConfirmReset = async () => { + await resetPageLayoutToDefault({ pageLayoutId }); + await refreshPageLayoutAfterReset(); + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/layout-customization/constants/LayoutCustomizationBarDropdownId.ts b/packages/twenty-front/src/modules/layout-customization/constants/LayoutCustomizationBarDropdownId.ts new file mode 100644 index 0000000000..263d131a8c --- /dev/null +++ b/packages/twenty-front/src/modules/layout-customization/constants/LayoutCustomizationBarDropdownId.ts @@ -0,0 +1,2 @@ +export const LAYOUT_CUSTOMIZATION_BAR_DROPDOWN_ID = + 'layout-customization-bar-dropdown'; diff --git a/packages/twenty-front/src/modules/layout-customization/constants/ResetRecordPageLayoutModalId.ts b/packages/twenty-front/src/modules/layout-customization/constants/ResetRecordPageLayoutModalId.ts new file mode 100644 index 0000000000..35d8f87002 --- /dev/null +++ b/packages/twenty-front/src/modules/layout-customization/constants/ResetRecordPageLayoutModalId.ts @@ -0,0 +1,2 @@ +export const RESET_RECORD_PAGE_LAYOUT_MODAL_ID = + 'reset-record-page-layout-modal';