8978 add navigation inside the command menu for showpage (#9103)

Closes #8978

- Added new options in the actions config files: `shortLabel`,
`availableOn`
- Added two actions: Navigate to previous records and Navigate to next
records
- Modified `useRecordShowPagePagination` to loop on records when we are
on first record and we hit previous or when we are on last record and we
hit next
- Introduced a new component state
`contextStoreCurrentViewTypeComponentState`
This commit is contained in:
Raphaël Bosi
2024-12-17 17:48:12 +01:00
committed by GitHub
parent bb8c763f9c
commit b033a50d7c
22 changed files with 529 additions and 147 deletions

View File

@ -1,9 +1,12 @@
import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
import { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
import { ShowPageSingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/ShowPageSingleRecordActionMenuEntrySetterEffect';
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -46,6 +49,10 @@ const ActionEffects = ({
contextStoreTargetedRecordsRuleComponentState,
);
const contextStoreCurrentViewType = useRecoilComponentValueV2(
contextStoreCurrentViewTypeComponentState,
);
const isWorkflowEnabled = useIsFeatureEnabled('IS_WORKFLOW_ENABLED');
return (
@ -59,9 +66,17 @@ const ActionEffects = ({
{contextStoreTargetedRecordsRule.mode === 'selection' &&
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
<>
<SingleRecordActionMenuEntrySetterEffect
objectMetadataItem={objectMetadataItem}
/>
{contextStoreCurrentViewType === ContextStoreViewType.ShowPage && (
<ShowPageSingleRecordActionMenuEntrySetterEffect
objectMetadataItem={objectMetadataItem}
/>
)}
{(contextStoreCurrentViewType === ContextStoreViewType.Table ||
contextStoreCurrentViewType === ContextStoreViewType.Kanban) && (
<SingleRecordActionMenuEntrySetterEffect
objectMetadataItem={objectMetadataItem}
/>
)}
{isWorkflowEnabled && (
<WorkflowRunRecordActionMenuEntrySetterEffect
objectMetadataItem={objectMetadataItem}

View File

@ -0,0 +1,76 @@
import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useEffect } from 'react';
import { isDefined } from 'twenty-ui';
export const ShowPageSingleRecordActionMenuEntrySetterEffect = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
const isPageHeaderV2Enabled = useIsFeatureEnabled(
'IS_PAGE_HEADER_V2_ENABLED',
);
const actionConfig = getActionConfig(
objectMetadataItem,
isPageHeaderV2Enabled,
);
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
const selectedRecordId =
contextStoreTargetedRecordsRule.mode === 'selection'
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
: undefined;
if (!isDefined(selectedRecordId)) {
throw new Error('Selected record ID is required');
}
const actionMenuEntries = Object.values(actionConfig ?? {})
.filter((action) =>
action.availableOn?.includes(ActionAvailableOn.SHOW_PAGE),
)
.map((action) => {
const { shouldBeRegistered, onClick, ConfirmationModal } =
action.actionHook({
recordId: selectedRecordId,
objectMetadataItem,
});
if (shouldBeRegistered) {
return {
...action,
onClick,
ConfirmationModal,
};
}
return undefined;
})
.filter(isDefined);
useEffect(() => {
for (const action of actionMenuEntries) {
addActionMenuEntry(action);
}
return () => {
for (const action of actionMenuEntries) {
removeActionMenuEntry(action.key);
}
};
}, [actionMenuEntries, addActionMenuEntry, removeActionMenuEntry]);
return null;
};

View File

@ -1,4 +1,5 @@
import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
@ -37,6 +38,11 @@ export const SingleRecordActionMenuEntrySetterEffect = ({
}
const actionMenuEntries = Object.values(actionConfig ?? {})
.filter((action) =>
action.availableOn?.includes(
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
),
)
.map((action) => {
const { shouldBeRegistered, onClick, ConfirmationModal } =
action.actionHook({

View File

@ -1,6 +1,7 @@
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
import {
ActionMenuEntry,
@ -22,6 +23,10 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
label: 'Add to favorites',
position: 0,
Icon: IconHeart,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useAddToFavoritesSingleRecordAction,
},
removeFromFavoritesSingleRecord: {
@ -31,6 +36,10 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
label: 'Remove from favorites',
position: 1,
Icon: IconHeartOff,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useRemoveFromFavoritesSingleRecordAction,
},
deleteSingleRecord: {
@ -42,6 +51,10 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
Icon: IconTrash,
accent: 'danger',
isPinned: true,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useDeleteSingleRecordAction,
},
};

View File

@ -1,13 +1,22 @@
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
ActionMenuEntryType,
} from '@/action-menu/types/ActionMenuEntry';
import { IconHeart, IconHeartOff, IconTrash } from 'twenty-ui';
import {
IconChevronDown,
IconChevronUp,
IconHeart,
IconHeartOff,
IconTrash,
} from 'twenty-ui';
export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
string,
@ -20,9 +29,14 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
scope: ActionMenuEntryScope.RecordSelection,
key: 'add-to-favorites-single-record',
label: 'Add to favorites',
shortLabel: 'Add to favorites',
position: 0,
isPinned: true,
Icon: IconHeart,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useAddToFavoritesSingleRecordAction,
},
removeFromFavoritesSingleRecord: {
@ -30,20 +44,54 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
scope: ActionMenuEntryScope.RecordSelection,
key: 'remove-from-favorites-single-record',
label: 'Remove from favorites',
shortLabel: 'Remove from favorites',
isPinned: true,
position: 1,
Icon: IconHeartOff,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useRemoveFromFavoritesSingleRecordAction,
},
deleteSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'delete-single-record',
label: 'Delete',
label: 'Delete record',
shortLabel: 'Delete',
position: 2,
Icon: IconTrash,
accent: 'danger',
isPinned: true,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useDeleteSingleRecordAction,
},
navigateToPreviousRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'navigate-to-previous-record',
label: 'Navigate to previous record',
shortLabel: '',
position: 3,
isPinned: true,
Icon: IconChevronUp,
availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToPreviousRecordSingleRecordAction,
},
navigateToNextRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'navigate-to-next-record',
label: 'Navigate to next record',
shortLabel: '',
position: 4,
isPinned: true,
Icon: IconChevronDown,
availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToNextRecordSingleRecordAction,
},
};

View File

@ -0,0 +1,15 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
export const useNavigateToNextRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
({ recordId, objectMetadataItem }) => {
const { navigateToNextRecord } = useRecordShowPagePagination(
objectMetadataItem.nameSingular,
recordId,
);
return {
shouldBeRegistered: true,
onClick: navigateToNextRecord,
};
};

View File

@ -0,0 +1,15 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
export const useNavigateToPreviousRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
({ recordId, objectMetadataItem }) => {
const { navigateToPreviousRecord } = useRecordShowPagePagination(
objectMetadataItem.nameSingular,
recordId,
);
return {
shouldBeRegistered: true,
onClick: navigateToPreviousRecord,
};
};

View File

@ -1,3 +1,5 @@
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
import { useActivateDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction';
import { useActivateLastPublishedVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction';
import { useDeactivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction';
@ -6,6 +8,7 @@ import { useSeeActiveVersionWorkflowSingleRecordAction } from '@/action-menu/act
import { useSeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction';
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
import { useTestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
import {
ActionMenuEntry,
@ -13,6 +16,8 @@ import {
ActionMenuEntryType,
} from '@/action-menu/types/ActionMenuEntry';
import {
IconChevronDown,
IconChevronUp,
IconHistory,
IconHistoryToggle,
IconPlayerPause,
@ -30,81 +35,143 @@ export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
activateWorkflowDraftSingleRecord: {
key: 'activate-workflow-draft-single-record',
label: 'Activate Draft',
shortLabel: 'Activate Draft',
isPinned: true,
position: 1,
Icon: IconPower,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useActivateDraftWorkflowSingleRecordAction,
},
activateWorkflowLastPublishedVersionSingleRecord: {
key: 'activate-workflow-last-published-version-single-record',
label: 'Activate last published version',
shortLabel: 'Activate last version',
isPinned: true,
position: 2,
Icon: IconPower,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useActivateLastPublishedVersionWorkflowSingleRecordAction,
},
deactivateWorkflowSingleRecord: {
key: 'deactivate-workflow-single-record',
label: 'Deactivate Workflow',
shortLabel: 'Deactivate',
isPinned: true,
position: 3,
Icon: IconPlayerPause,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useDeactivateWorkflowSingleRecordAction,
},
discardWorkflowDraftSingleRecord: {
key: 'discard-workflow-draft-single-record',
label: 'Discard Draft',
shortLabel: 'Discard Draft',
isPinned: true,
position: 4,
Icon: IconTrash,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useDiscardDraftWorkflowSingleRecordAction,
},
seeWorkflowActiveVersionSingleRecord: {
key: 'see-workflow-active-version-single-record',
label: 'See active version',
shortLabel: 'See active version',
isPinned: false,
position: 5,
Icon: IconHistory,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useSeeActiveVersionWorkflowSingleRecordAction,
},
seeWorkflowRunsSingleRecord: {
key: 'see-workflow-runs-single-record',
label: 'See runs',
shortLabel: 'See runs',
isPinned: false,
position: 6,
Icon: IconHistoryToggle,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useSeeRunsWorkflowSingleRecordAction,
},
seeWorkflowVersionsHistorySingleRecord: {
key: 'see-workflow-versions-history-single-record',
label: 'See versions history',
shortLabel: 'See versions',
isPinned: false,
position: 7,
Icon: IconHistory,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useSeeVersionsWorkflowSingleRecordAction,
},
testWorkflowSingleRecord: {
key: 'test-workflow-single-record',
label: 'Test Workflow',
shortLabel: 'Test',
isPinned: true,
position: 8,
Icon: IconPlayerPlay,
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useTestWorkflowSingleRecordAction,
},
navigateToPreviousRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'navigate-to-previous-record',
label: 'Navigate to previous workflow',
shortLabel: '',
position: 9,
Icon: IconChevronUp,
availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToPreviousRecordSingleRecordAction,
},
navigateToNextRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'navigate-to-next-record',
label: 'Navigate to next workflow',
shortLabel: '',
position: 10,
Icon: IconChevronDown,
availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToNextRecordSingleRecordAction,
},
};

View File

@ -1,13 +1,22 @@
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
import { useSeeExecutionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeExecutionsWorkflowVersionSingleRecordAction';
import { useSeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction';
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
ActionMenuEntryType,
} from '@/action-menu/types/ActionMenuEntry';
import { IconHistory, IconHistoryToggle, IconPencil } from 'twenty-ui';
import {
IconChevronDown,
IconChevronUp,
IconHistory,
IconHistoryToggle,
IconPencil,
} from 'twenty-ui';
export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
string,
@ -23,6 +32,10 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
Icon: IconPencil,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useUseAsDraftWorkflowVersionSingleRecordAction,
},
seeWorkflowExecutionsSingleRecord: {
@ -32,6 +45,10 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
Icon: IconHistoryToggle,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useSeeExecutionsWorkflowVersionSingleRecordAction,
},
seeWorkflowVersionsHistorySingleRecord: {
@ -41,6 +58,32 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
Icon: IconHistory,
availableOn: [
ActionAvailableOn.SHOW_PAGE,
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
actionHook: useSeeVersionsWorkflowVersionSingleRecordAction,
},
navigateToPreviousRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'navigate-to-previous-record',
label: 'Navigate to previous version',
shortLabel: '',
position: 9,
Icon: IconChevronUp,
availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToPreviousRecordSingleRecordAction,
},
navigateToNextRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: 'navigate-to-next-record',
label: 'Navigate to next version',
shortLabel: '',
position: 10,
Icon: IconChevronDown,
availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToNextRecordSingleRecordAction,
},
};

View File

@ -0,0 +1,6 @@
export enum ActionAvailableOn {
INDEX_PAGE_BULK_SELECTION = 'INDEX_PAGE_BULK_SELECTION',
INDEX_PAGE_SINGLE_RECORD_SELECTION = 'INDEX_PAGE_SINGLE_RECORD_SELECTION',
INDEX_PAGE_NO_SELECTION = 'INDEX_PAGE_NO_SELECTION',
SHOW_PAGE = 'SHOW_PAGE',
}

View File

@ -1,7 +1,7 @@
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
import { PageHeaderOpenCommandMenuButton } from '@/ui/layout/page-header/components/PageHeaderOpenCommandMenuButton';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { Button, useIsMobile } from 'twenty-ui';
import { Button, IconButton, useIsMobile } from 'twenty-ui';
export const RecordShowActionMenuButtons = () => {
const actionMenuEntries = useRecoilComponentValueV2(
@ -15,18 +15,29 @@ export const RecordShowActionMenuButtons = () => {
return (
<>
{!isMobile &&
pinnedEntries.map((entry, index) => (
<Button
key={index}
Icon={entry.Icon}
size="small"
variant="secondary"
accent="default"
title={entry.label}
onClick={() => entry.onClick?.()}
ariaLabel={entry.label}
/>
))}
pinnedEntries.map((entry, index) =>
entry.shortLabel ? (
<Button
key={index}
Icon={entry.Icon}
size="small"
variant="secondary"
accent="default"
title={entry.shortLabel}
onClick={() => entry.onClick?.()}
ariaLabel={entry.label}
/>
) : (
<IconButton
Icon={entry.Icon}
size="small"
variant="secondary"
accent="default"
onClick={() => entry.onClick?.()}
ariaLabel={entry.label}
/>
),
)}
<PageHeaderOpenCommandMenuButton key="more" />
</>
);

View File

@ -1,3 +1,4 @@
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn';
import { MouseEvent, ReactElement } from 'react';
import { IconComponent, MenuItemAccent } from 'twenty-ui';
@ -16,10 +17,12 @@ export type ActionMenuEntry = {
scope: ActionMenuEntryScope;
key: string;
label: string;
shortLabel?: string;
position: number;
Icon: IconComponent;
isPinned?: boolean;
accent?: MenuItemAccent;
availableOn?: ActionAvailableOn[];
onClick?: (event?: MouseEvent<HTMLElement>) => void;
ConfirmationModal?: ReactElement;
};