diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx index ccc98aefa9..5382e897ed 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx @@ -1,5 +1,3 @@ -import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { PageLayoutGridLayoutDragSelector } from '@/page-layout/components/PageLayoutGridLayoutDragSelector'; import { PageLayoutGridOverlay } from '@/page-layout/components/PageLayoutGridOverlay'; import { EMPTY_LAYOUT } from '@/page-layout/constants/EmptyLayout'; @@ -75,8 +73,6 @@ export const PageLayoutGridLayout = () => { const activeTabId = useRecoilComponentValue(activeTabIdComponentState); - const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu(); - const { currentPageLayout } = useCurrentPageLayout(); if (!isDefined(activeTabId) || !isDefined(currentPageLayout)) { @@ -128,14 +124,7 @@ export const PageLayoutGridLayout = () => { > {isLayoutEmpty ? (
- { - navigatePageLayoutCommandMenu({ - commandMenuPage: - CommandMenuPages.PageLayoutWidgetTypeSelect, - }); - }} - /> +
) : ( activeTabWidgets?.map((widget) => ( diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetHeader.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetHeader.tsx index 035e8ccf25..627b072966 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetHeader.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetHeader.tsx @@ -6,6 +6,8 @@ const StyledHeader = styled.div` align-items: center; display: flex; gap: ${({ theme }) => theme.spacing(2)}; + height: ${({ theme }) => theme.spacing(6)}; + flex-shrink: 0; `; const StyledDragHandleButton = styled(IconButton)` diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetPlaceholder.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetPlaceholder.tsx index 1ebe2a77df..2f2346bc85 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetPlaceholder.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetPlaceholder.tsx @@ -1,5 +1,14 @@ +import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; +import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { useSetIsDashboardInEditMode } from '@/dashboards/hooks/useSetDashboardInEditMode'; +import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; +import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState'; import { WidgetContainer } from '@/page-layout/widgets/components/WidgetContainer'; import { WidgetHeader } from '@/page-layout/widgets/components/WidgetHeader'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { t } from '@lingui/core/macro'; +import { Trans } from '@lingui/react/macro'; import { AnimatedPlaceholder, AnimatedPlaceholderEmptyContainer, @@ -9,14 +18,36 @@ import { EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/layout'; -type WidgetPlaceholderProps = { - onClick: () => void; -}; +export const WidgetPlaceholder = () => { + const pageLayoutId = useAvailableComponentInstanceIdOrThrow( + PageLayoutComponentInstanceContext, + ); + + const isPageLayoutInEditMode = useRecoilComponentValue( + isPageLayoutInEditModeComponentState, + ); + + const { setIsDashboardInEditMode } = + useSetIsDashboardInEditMode(pageLayoutId); + + const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu(); + + const handleClick = () => { + if (!isPageLayoutInEditMode) { + setIsDashboardInEditMode(true); + } + navigatePageLayoutCommandMenu({ + commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect, + }); + }; -export const WidgetPlaceholder = ({ onClick }: WidgetPlaceholderProps) => { return ( - - + + { - No widgets yet + No widgets yet - Click to add your first widget + Click to add your first widget diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetPlaceholder.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetPlaceholder.stories.tsx index 7ca3242803..9474cf7224 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetPlaceholder.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetPlaceholder.stories.tsx @@ -1,48 +1,55 @@ +import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { PageLayoutTestWrapper } from '@/page-layout/hooks/__tests__/PageLayoutTestWrapper'; import { WidgetPlaceholder } from '@/page-layout/widgets/components/WidgetPlaceholder'; import { type Meta, type StoryObj } from '@storybook/react'; +import { type MutableSnapshot } from 'recoil'; import { ComponentDecorator } from 'twenty-ui/testing'; +import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; +import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; const meta: Meta = { title: 'Modules/PageLayout/Widgets/WidgetPlaceholder', component: WidgetPlaceholder, decorators: [ - (Story) => ( - - - - ), + (Story) => { + const initializeState = (snapshot: MutableSnapshot) => { + snapshot.set( + objectMetadataItemsState, + generatedMockObjectMetadataItems, + ); + snapshot.set(isAppWaitingForFreshObjectMetadataState, false); + }; + + return ( + + + + ); + }, ComponentDecorator, + I18nFrontDecorator, ], parameters: { layout: 'centered', docs: { description: { component: - 'A placeholder widget that appears when no widgets are present. Shows an empty state with a call-to-action to add the first widget.', + 'A self-contained placeholder widget that appears when no widgets are present. Automatically enables edit mode when clicked and opens the widget type selection command menu.', }, }, }, - argTypes: { - onClick: { - action: 'onClick', - description: - 'Callback function triggered when the placeholder is clicked', - }, - }, }; export default meta; type Story = StoryObj; export const Default: Story = { - args: { - onClick: () => {}, - }, parameters: { docs: { description: { - story: 'Default widget placeholder state.', + story: + 'Default widget placeholder state. Click to trigger the add widget flow.', }, }, },