04b0a65e73
[Figma Design](https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=81380-344641&t=FpjWNOK2gZuDQQfr-0) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a side panel layout for the Command Menu, routes modals into a local container, updates the top bar and context chips, and standardizes small button sizes. > > - **Command Menu**: > - **Side Panel Layout**: Introduces `CommandMenuSidePanelLayout` with animated width, hosts `CommandMenuRouter`, and provides a modal container via `ModalContainerContext`. > - **Top Bar**: Redesign (`CommandMenuTopBar`) with back icon, optional AI sparkles action, compact height (`COMMAND_MENU_SEARCH_BAR_HEIGHT=40`), and updated placeholder. > - **Context Chips**: Adds `CommandMenuLastContextChip` and `CommandMenuRecordInfo`; extends `CommandMenuContextChip` with `page` prop; updates `CommandMenuContextChipGroups` to render last chip as record info when applicable. > - **Container Simplification**: `CommandMenuContainer` simplified to just provide contexts and `AgentChatProvider`. > - **Modal System**: > - Adds `ModalContainerContext` and updates `Modal` to portal into provided container; `Modal.Backdrop` supports `isInContainer`. > - Updates usages (e.g., `UserOrMetadataLoader`, `ActionModal`) to align with new modal behavior. > - **Page Integration**: > - Replaces `PageBody` with `CommandMenuSidePanelLayout` in `RecordShowPage` and `RecordIndexContainerGater`. > - Removes global `CommandMenuRouter` from `DefaultLayout` (keeps keyboard shortcuts). > - **UI/Styling**: > - Standardizes several buttons to `size="small"` (e.g., command actions, open record, options, reply, workflow footer). > - Adjusts `ShowPageSubContainer` styling when rendered inside command menu. > - Storybook tests updated for new placeholder text. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 81fcaa145618a2fa1c3e11e2dc88fe832c51374c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Devessier <baptiste@devessier.fr> Co-authored-by: Aman Raj <92664006+araj00@users.noreply.github.com> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
import { GRAPH_TYPE_INFORMATION } from '@/command-menu/pages/page-layout/constants/GraphTypeInformation';
|
|
import { isChartWidget } from '@/command-menu/pages/page-layout/utils/isChartWidget';
|
|
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
|
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
|
import { useTheme } from '@emotion/react';
|
|
import { t } from '@lingui/core/macro';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import {
|
|
IconAppWindow,
|
|
IconFrame,
|
|
type IconComponent,
|
|
} from 'twenty-ui/display';
|
|
import { type PageLayoutWidget } from '~/generated/graphql';
|
|
|
|
type PageLayoutHeaderInfo = {
|
|
headerIcon: IconComponent | undefined;
|
|
headerIconColor: string;
|
|
headerType: string;
|
|
title: string;
|
|
isReadonly: boolean;
|
|
tab: PageLayoutTab | undefined;
|
|
widgetInEditMode: PageLayoutWidget | undefined;
|
|
};
|
|
|
|
type UsePageLayoutHeaderInfoParams = {
|
|
commandMenuPage: CommandMenuPages;
|
|
draftPageLayout: {
|
|
tabs: PageLayoutTab[];
|
|
};
|
|
pageLayoutEditingWidgetId: string | null | undefined;
|
|
openTabId: string | null | undefined;
|
|
editedTitle: string | null | undefined;
|
|
};
|
|
|
|
export const usePageLayoutHeaderInfo = ({
|
|
commandMenuPage,
|
|
draftPageLayout,
|
|
pageLayoutEditingWidgetId,
|
|
openTabId,
|
|
editedTitle,
|
|
}: UsePageLayoutHeaderInfoParams): PageLayoutHeaderInfo | null => {
|
|
const theme = useTheme();
|
|
const iconColor = theme.font.color.tertiary;
|
|
|
|
switch (commandMenuPage) {
|
|
case CommandMenuPages.PageLayoutTabSettings: {
|
|
if (!isDefined(openTabId)) {
|
|
return null;
|
|
}
|
|
|
|
const tab = draftPageLayout.tabs.find((t) => t.id === openTabId);
|
|
|
|
if (!isDefined(tab)) {
|
|
return null;
|
|
}
|
|
|
|
const title = isDefined(editedTitle)
|
|
? editedTitle
|
|
: isDefined(tab.title) && tab.title !== ''
|
|
? tab.title
|
|
: '';
|
|
|
|
return {
|
|
headerIcon: IconAppWindow,
|
|
headerIconColor: iconColor,
|
|
headerType: t`Tab`,
|
|
title,
|
|
isReadonly: false,
|
|
tab,
|
|
widgetInEditMode: undefined,
|
|
};
|
|
}
|
|
|
|
case CommandMenuPages.PageLayoutIframeSettings: {
|
|
if (!isDefined(pageLayoutEditingWidgetId)) {
|
|
return null;
|
|
}
|
|
|
|
const widgetInEditMode = draftPageLayout.tabs
|
|
.flatMap((tab) => tab.widgets)
|
|
.find((widget) => widget.id === pageLayoutEditingWidgetId);
|
|
|
|
if (!isDefined(widgetInEditMode)) {
|
|
return null;
|
|
}
|
|
|
|
const title = isDefined(editedTitle)
|
|
? editedTitle
|
|
: isDefined(widgetInEditMode.title) && widgetInEditMode.title !== ''
|
|
? widgetInEditMode.title
|
|
: '';
|
|
|
|
return {
|
|
headerIcon: IconFrame,
|
|
headerIconColor: iconColor,
|
|
headerType: t`iFrame Widget`,
|
|
title,
|
|
isReadonly: false,
|
|
tab: undefined,
|
|
widgetInEditMode,
|
|
};
|
|
}
|
|
|
|
case CommandMenuPages.PageLayoutGraphTypeSelect:
|
|
case CommandMenuPages.PageLayoutGraphFilter: {
|
|
if (!isDefined(pageLayoutEditingWidgetId)) {
|
|
return null;
|
|
}
|
|
|
|
const widgetInEditMode = draftPageLayout.tabs
|
|
.flatMap((tab) => tab.widgets)
|
|
.find((widget) => widget.id === pageLayoutEditingWidgetId);
|
|
|
|
if (!isDefined(widgetInEditMode)) {
|
|
return null;
|
|
}
|
|
|
|
if (!isChartWidget(widgetInEditMode)) {
|
|
return null;
|
|
}
|
|
|
|
const currentGraphType = widgetInEditMode.configuration.graphType;
|
|
const graphTypeInfo = GRAPH_TYPE_INFORMATION[currentGraphType];
|
|
const graphTypeLabel = t(graphTypeInfo.label);
|
|
|
|
const headerType =
|
|
commandMenuPage === CommandMenuPages.PageLayoutGraphFilter
|
|
? t`${graphTypeLabel} Chart`
|
|
: t`Chart`;
|
|
|
|
const title = isDefined(editedTitle)
|
|
? editedTitle
|
|
: isDefined(widgetInEditMode.title) && widgetInEditMode.title !== ''
|
|
? widgetInEditMode.title
|
|
: '';
|
|
|
|
return {
|
|
headerIcon: graphTypeInfo.icon,
|
|
headerIconColor: iconColor,
|
|
headerType,
|
|
title,
|
|
isReadonly: commandMenuPage === CommandMenuPages.PageLayoutGraphFilter,
|
|
tab: undefined,
|
|
widgetInEditMode,
|
|
};
|
|
}
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
};
|