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>
265 lines
7.0 KiB
TypeScript
265 lines
7.0 KiB
TypeScript
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
|
import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState';
|
|
import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState';
|
|
import { commandMenuWorkflowStepIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowStepIdComponentState';
|
|
import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState';
|
|
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
|
import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow';
|
|
import { useSetInitialWorkflowRunRightDrawerTab } from '@/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab';
|
|
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
|
|
import { t } from '@lingui/core/macro';
|
|
import { useRecoilCallback } from 'recoil';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import {
|
|
IconBolt,
|
|
type IconComponent,
|
|
IconSettingsAutomation,
|
|
} from 'twenty-ui/display';
|
|
import { v4 } from 'uuid';
|
|
|
|
export const useWorkflowCommandMenu = () => {
|
|
const { navigateCommandMenu } = useNavigateCommandMenu();
|
|
const { setInitialWorkflowRunRightDrawerTab } =
|
|
useSetInitialWorkflowRunRightDrawerTab();
|
|
|
|
const openWorkflowTriggerTypeInCommandMenu = useRecoilCallback(
|
|
({ set }) => {
|
|
return (workflowId: string) => {
|
|
const pageId = v4();
|
|
|
|
set(
|
|
commandMenuWorkflowIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowId,
|
|
);
|
|
|
|
navigateCommandMenu({
|
|
page: CommandMenuPages.WorkflowTriggerSelectType,
|
|
pageTitle: t`Trigger Type`,
|
|
pageIcon: IconBolt,
|
|
pageId,
|
|
});
|
|
};
|
|
},
|
|
[navigateCommandMenu],
|
|
);
|
|
|
|
const openWorkflowCreateStepInCommandMenu = useRecoilCallback(
|
|
({ set }) => {
|
|
return (workflowId: string) => {
|
|
const pageId = v4();
|
|
|
|
set(
|
|
commandMenuWorkflowIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowId,
|
|
);
|
|
|
|
navigateCommandMenu({
|
|
page: CommandMenuPages.WorkflowStepCreate,
|
|
pageTitle: t`Select Action`,
|
|
pageIcon: IconSettingsAutomation,
|
|
pageId,
|
|
});
|
|
};
|
|
},
|
|
[navigateCommandMenu],
|
|
);
|
|
|
|
const openWorkflowEditStepInCommandMenu = useRecoilCallback(
|
|
({ set }) => {
|
|
return (
|
|
workflowId: string,
|
|
title: string,
|
|
icon: IconComponent,
|
|
stepId?: string,
|
|
) => {
|
|
const pageId = v4();
|
|
|
|
set(
|
|
commandMenuWorkflowIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowId,
|
|
);
|
|
|
|
if (isDefined(stepId)) {
|
|
set(
|
|
commandMenuWorkflowStepIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
stepId,
|
|
);
|
|
|
|
set(
|
|
workflowSelectedNodeComponentState.atomFamily({
|
|
instanceId: workflowId,
|
|
}),
|
|
stepId,
|
|
);
|
|
}
|
|
|
|
navigateCommandMenu({
|
|
page: CommandMenuPages.WorkflowStepEdit,
|
|
pageTitle: title,
|
|
pageIcon: icon,
|
|
pageId,
|
|
});
|
|
};
|
|
},
|
|
[navigateCommandMenu],
|
|
);
|
|
|
|
const openWorkflowEditStepTypeInCommandMenu = useRecoilCallback(
|
|
({ set }) => {
|
|
return (workflowId: string) => {
|
|
const pageId = v4();
|
|
|
|
set(
|
|
commandMenuWorkflowIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowId,
|
|
);
|
|
|
|
navigateCommandMenu({
|
|
page: CommandMenuPages.WorkflowStepEditType,
|
|
pageTitle: t`Select action`,
|
|
pageIcon: IconSettingsAutomation,
|
|
pageId,
|
|
});
|
|
};
|
|
},
|
|
[navigateCommandMenu],
|
|
);
|
|
|
|
const openWorkflowViewStepInCommandMenu = useRecoilCallback(
|
|
({ set }) => {
|
|
return ({
|
|
workflowId,
|
|
workflowVersionId,
|
|
title,
|
|
icon,
|
|
stepId,
|
|
}: {
|
|
workflowId: string;
|
|
workflowVersionId: string;
|
|
title: string;
|
|
icon: IconComponent;
|
|
stepId?: string;
|
|
}) => {
|
|
const pageId = v4();
|
|
|
|
set(
|
|
commandMenuWorkflowIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowId,
|
|
);
|
|
set(
|
|
commandMenuWorkflowVersionIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowVersionId,
|
|
);
|
|
|
|
if (isDefined(stepId)) {
|
|
set(
|
|
commandMenuWorkflowStepIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
stepId,
|
|
);
|
|
|
|
set(
|
|
workflowSelectedNodeComponentState.atomFamily({
|
|
instanceId: workflowVersionId,
|
|
}),
|
|
stepId,
|
|
);
|
|
}
|
|
|
|
navigateCommandMenu({
|
|
page: CommandMenuPages.WorkflowStepView,
|
|
pageTitle: title,
|
|
pageIcon: icon,
|
|
pageId,
|
|
});
|
|
};
|
|
},
|
|
[navigateCommandMenu],
|
|
);
|
|
|
|
const openWorkflowRunViewStepInCommandMenu = useRecoilCallback(
|
|
({ set }) => {
|
|
return ({
|
|
workflowId,
|
|
workflowRunId,
|
|
title,
|
|
icon,
|
|
workflowSelectedNode,
|
|
stepExecutionStatus,
|
|
}: {
|
|
workflowId: string;
|
|
workflowRunId: string;
|
|
title: string;
|
|
icon: IconComponent;
|
|
workflowSelectedNode: string;
|
|
stepExecutionStatus: WorkflowRunStepStatus;
|
|
}) => {
|
|
const pageId = v4();
|
|
|
|
set(
|
|
commandMenuWorkflowIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowId,
|
|
);
|
|
set(
|
|
commandMenuWorkflowRunIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowRunId,
|
|
);
|
|
set(
|
|
commandMenuWorkflowStepIdComponentState.atomFamily({
|
|
instanceId: pageId,
|
|
}),
|
|
workflowSelectedNode,
|
|
);
|
|
|
|
set(
|
|
workflowSelectedNodeComponentState.atomFamily({
|
|
instanceId: workflowRunId,
|
|
}),
|
|
workflowSelectedNode,
|
|
);
|
|
|
|
navigateCommandMenu({
|
|
page: CommandMenuPages.WorkflowRunStepView,
|
|
pageTitle: title,
|
|
pageIcon: icon,
|
|
pageId,
|
|
});
|
|
|
|
setInitialWorkflowRunRightDrawerTab({
|
|
workflowSelectedNode,
|
|
stepExecutionStatus,
|
|
});
|
|
};
|
|
},
|
|
[navigateCommandMenu, setInitialWorkflowRunRightDrawerTab],
|
|
);
|
|
|
|
return {
|
|
openWorkflowTriggerTypeInCommandMenu,
|
|
openWorkflowCreateStepInCommandMenu,
|
|
openWorkflowEditStepInCommandMenu,
|
|
openWorkflowEditStepTypeInCommandMenu,
|
|
openWorkflowViewStepInCommandMenu,
|
|
openWorkflowRunViewStepInCommandMenu,
|
|
};
|
|
};
|