Files
twenty/packages/twenty-front/src/modules/command-menu-item/contexts/CommandMenuContextProviderWithWorkflowEnrichment.tsx
T
nitinandGitHub 4649736d49 [Command Menu] Fix record-selection command filtering in edit mode (#20034)
https://github.com/user-attachments/assets/fe1461c7-0d5c-4c6f-8c2e-2cf569e7de90

## What

Fix `RECORD_SELECTION` items leaking into the command menu when nothing
is selected, and unify how the menu renders in normal vs edit mode.

## The bug

`RECORD_SELECTION`-availability items were showing up even when
`numberOfSelectedRecords === 0`. New util
`doesCommandMenuItemMatchSelectionState` gates them, applied
consistently in the runtime provider and the editor.

## The refactor

`PinnedCommandMenuItemButtonsEditMode` was a 140-line near-duplicate of
`PinnedCommandMenuItemButtons` with its own (drifting) filter logic.
Killed it. Edit mode now flows through the same
`CommandMenuContextProvider` with a new `isInPreviewMode` flag — one
filter chain, one rendering path.

## Behavior in edit mode

**Header (pinned buttons in page header):**
- Runs the full filter chain — object metadata, page type, selection
state, page layout, *and the conditional availability expression*
- Buttons render at full styling but are inert via `pointer-events:
none` + `cursor: not-allowed`
- Preview now reflects exactly what users will see on the live page (not
a grayed-out approximation)

**Side panel editor:**
- New `useEditableCommandMenuItems` hook
- Same filters as runtime *minus* the conditional availability
expression and `FALLBACK` items — so it surfaces everything that's
actually configurable for this page context
- Still gates on selection state — if no records selected,
`RECORD_SELECTION` items are hidden from the editor too. Open to
feedback if we'd rather always show them so users can pin them ahead of
time.

## Misc

- `usePinnedCommandMenuItemsInlineLayout` — visible count now waits
until every item is measured before committing. Fixes a flash of wrong
counts on mount/resize
- Renamed `useCommandMenuContextApi` → `useCurrentCommandMenuContextApi`
— name now conveys it reads from the *current* scoped context store
- Copy: "Records selected" → "Record(s) selected"
2026-04-29 13:34:28 +00:00

65 lines
2.0 KiB
TypeScript

import { type CommandMenuContextApi } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type CommandMenuContextType } from '@/command-menu-item/contexts/CommandMenuContext';
import { useWorkflowsWithCurrentVersions } from '@/command-menu-item/hooks/useWorkflowsWithCurrentVersions';
import { CommandMenuContextProviderContent } from './CommandMenuContextProviderContent';
type CommandMenuContextProviderWithWorkflowEnrichmentProps = {
displayType: CommandMenuContextType['displayType'];
containerType: CommandMenuContextType['containerType'];
children: React.ReactNode;
commandMenuContextApi: CommandMenuContextApi;
selectedWorkflowRecordIds: string[];
isInPreviewMode: boolean;
};
export const CommandMenuContextProviderWithWorkflowEnrichment = ({
displayType,
containerType,
children,
commandMenuContextApi,
selectedWorkflowRecordIds,
isInPreviewMode,
}: CommandMenuContextProviderWithWorkflowEnrichmentProps) => {
const workflowsWithCurrentVersions = useWorkflowsWithCurrentVersions(
selectedWorkflowRecordIds,
);
const enrichedSelectedRecords = commandMenuContextApi.selectedRecords.map(
(record) => {
const workflowWithCurrentVersion = workflowsWithCurrentVersions.find(
(workflow) => workflow.id === record.id,
);
if (!isDefined(workflowWithCurrentVersion)) {
return record;
}
return {
...record,
currentVersion: workflowWithCurrentVersion.currentVersion,
versions: workflowWithCurrentVersion.versions,
statuses: workflowWithCurrentVersion.statuses,
};
},
);
const enrichedCommandMenuContextApi = {
...commandMenuContextApi,
selectedRecords: enrichedSelectedRecords,
};
return (
<CommandMenuContextProviderContent
displayType={displayType}
containerType={containerType}
commandMenuContextApi={enrichedCommandMenuContextApi}
isInPreviewMode={isInPreviewMode}
>
{children}
</CommandMenuContextProviderContent>
);
};