Page layout refactoring (#14535)

In this PR:
- Refactored the page layout renderer
- Converted all the states to component states
- Removed the page layout edition in settings

TODOs in next PRs:
- Fix bug with the drag selector not taking the scroll into account to
display the dragged area
- Readd the tab edition in edit mode
This commit is contained in:
Raphaël Bosi
2025-09-16 17:42:51 +02:00
committed by GitHub
parent e69131790c
commit 3b868c3d2a
90 changed files with 2070 additions and 1928 deletions
@@ -1,4 +1,5 @@
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { useCreatePageLayoutWidget } from '@/page-layout/hooks/useCreatePageLayoutWidget';
import { GraphType, WidgetType } from '@/page-layout/mocks/mockWidgets';
import styled from '@emotion/styled';
@@ -57,7 +58,10 @@ const graphTypeOptions = [
export const CommandMenuPageLayoutGraphTypeSelect = () => {
const { closeCommandMenu } = useCommandMenu();
const { createPageLayoutWidget } = useCreatePageLayoutWidget();
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const { createPageLayoutWidget } = useCreatePageLayoutWidget(pageLayoutId);
const handleSelectGraphType = (graphType: GraphType) => {
createPageLayoutWidget(WidgetType.GRAPH, graphType);
@@ -1,13 +1,15 @@
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
import { useCreatePageLayoutIframeWidget } from '@/page-layout/hooks/useCreatePageLayoutIframeWidget';
import { useUpdatePageLayoutWidget } from '@/page-layout/hooks/useUpdatePageLayoutWidget';
import { pageLayoutDraftState } from '@/page-layout/states/pageLayoutDraftState';
import { pageLayoutEditingWidgetIdState } from '@/page-layout/states/pageLayoutEditingWidgetIdState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import styled from '@emotion/styled';
import { isString } from '@sniptt/guards';
import { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { isValidUrl } from 'twenty-shared/utils';
import { Button } from 'twenty-ui/input';
@@ -33,21 +35,37 @@ const StyledButtonContainer = styled.div`
export const CommandMenuPageLayoutIframeConfig = () => {
const { closeCommandMenu } = useCommandMenu();
const { createPageLayoutIframeWidget } = useCreatePageLayoutIframeWidget();
const { updatePageLayoutWidget } = useUpdatePageLayoutWidget();
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const { createPageLayoutIframeWidget } =
useCreatePageLayoutIframeWidget(pageLayoutId);
const { updatePageLayoutWidget } = useUpdatePageLayoutWidget(pageLayoutId);
const [pageLayoutEditingWidgetId, setPageLayoutEditingWidgetId] =
useRecoilState(pageLayoutEditingWidgetIdState);
const pageLayoutDraft = useRecoilValue(pageLayoutDraftState);
useRecoilComponentState(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
const pageLayoutDraft = useRecoilComponentValue(
pageLayoutDraftComponentState,
pageLayoutId,
);
const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets);
const editingWidget = allWidgets.find(
(w) => w.id === pageLayoutEditingWidgetId,
);
const isEditMode = !!editingWidget;
const [title, setTitle] = useState(editingWidget?.title || '');
const configUrl = editingWidget?.configuration?.url;
const [url, setUrl] = useState(isString(configUrl) ? configUrl : '');
const [urlError, setUrlError] = useState('');
const validateUrl = (urlString: string): boolean => {
@@ -84,6 +102,7 @@ export const CommandMenuPageLayoutIframeConfig = () => {
url: url.trim(),
},
});
setPageLayoutEditingWidgetId(null);
} else {
createPageLayoutIframeWidget(title.trim(), url.trim());
@@ -1,9 +1,10 @@
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
import { WidgetType } from '@/page-layout/mocks/mockWidgets';
import { pageLayoutDraggedAreaState } from '@/page-layout/states/pageLayoutDraggedAreaState';
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
import { IconChartPie, IconFrame, IconList } from 'twenty-ui/display';
import { MenuItemCommand } from 'twenty-ui/navigation';
@@ -50,27 +51,31 @@ const widgetTypeOptions = [
];
export const CommandMenuPageLayoutWidgetTypeSelect = () => {
const { navigateCommandMenu } = useNavigateCommandMenu();
const setPageLayoutDraggedArea = useSetRecoilState(
pageLayoutDraggedAreaState,
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const setPageLayoutDraggedArea = useSetRecoilComponentState(
pageLayoutDraggedAreaComponentState,
pageLayoutId,
);
const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu();
const handleSelectWidget = (widgetType: WidgetType) => {
switch (widgetType) {
case WidgetType.GRAPH:
navigateCommandMenu({
page: CommandMenuPages.PageLayoutGraphTypeSelect,
pageTitle: 'Select Graph Type',
pageIcon: IconChartPie,
case WidgetType.GRAPH: {
navigatePageLayoutCommandMenu({
commandMenuPage: CommandMenuPages.PageLayoutGraphTypeSelect,
});
break;
case WidgetType.IFRAME:
navigateCommandMenu({
page: CommandMenuPages.PageLayoutIframeConfig,
pageTitle: 'Configure iFrame',
pageIcon: IconFrame,
}
case WidgetType.IFRAME: {
navigatePageLayoutCommandMenu({
commandMenuPage: CommandMenuPages.PageLayoutIframeConfig,
});
break;
}
default:
setPageLayoutDraggedArea(null);
break;