refactor(command-menu-item): rename Actions to CommandMenuItem (#18489)
actions are being renamed to command menu item, they will be migrated to server and will be served as headless front components --------- Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const Action = ({
|
||||
onClick,
|
||||
closeSidePanelOnShowPageOptionsActionExecution = false,
|
||||
closeSidePanelOnCommandMenuListActionExecution = true,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
closeSidePanelOnShowPageOptionsActionExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListActionExecution?: boolean;
|
||||
}) => {
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu({
|
||||
closeSidePanelOnShowPageOptionsActionExecution,
|
||||
closeSidePanelOnCommandMenuListActionExecution,
|
||||
});
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
closeActionMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <ActionDisplay onClick={handleClick} />;
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { type PathParam } from 'react-router-dom';
|
||||
import { type AppPath } from 'twenty-shared/types';
|
||||
import { getAppPath } from 'twenty-shared/utils';
|
||||
|
||||
export const ActionLink = <T extends AppPath>({
|
||||
to,
|
||||
params,
|
||||
queryParams,
|
||||
}: {
|
||||
to: T;
|
||||
params?: { [key in PathParam<T>]: string | null };
|
||||
queryParams?: Record<string, any>;
|
||||
}) => {
|
||||
const { closeActionMenu } = useCloseActionMenu();
|
||||
|
||||
const path = getAppPath(to, params, queryParams);
|
||||
|
||||
return <ActionDisplay onClick={closeActionMenu} to={path} />;
|
||||
};
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
import { type ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { styled } from '@linaria/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { AppTooltip, TooltipDelay, TooltipPosition } from 'twenty-ui/display';
|
||||
import { Button, IconButton } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
`;
|
||||
|
||||
export const ActionButton = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
const label = getActionLabel(action.label);
|
||||
|
||||
const shortLabel = isDefined(action.shortLabel)
|
||||
? getActionLabel(action.shortLabel)
|
||||
: undefined;
|
||||
|
||||
const buttonAccent = action.isPrimaryCTA ? 'blue' : 'default';
|
||||
|
||||
return (
|
||||
<>
|
||||
{isDefined(action.shortLabel) ? (
|
||||
<Button
|
||||
Icon={action.Icon}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
accent={buttonAccent}
|
||||
to={to}
|
||||
onClick={onClick}
|
||||
title={shortLabel}
|
||||
ariaLabel={label}
|
||||
/>
|
||||
) : (
|
||||
<div id={`action-menu-entry-${action.key}`} key={action.key}>
|
||||
<IconButton
|
||||
Icon={action.Icon}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
accent={buttonAccent}
|
||||
to={to}
|
||||
onClick={onClick}
|
||||
ariaLabel={label}
|
||||
/>
|
||||
<StyledWrapper>
|
||||
<AppTooltip
|
||||
anchorSelect={`#action-menu-entry-${action.key}`}
|
||||
content={label}
|
||||
delay={TooltipDelay.longDelay}
|
||||
place={TooltipPosition.Bottom}
|
||||
offset={5}
|
||||
noArrow
|
||||
/>
|
||||
</StyledWrapper>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
|
||||
export const ActionComponent = ({ action }: { action: ActionConfig }) => {
|
||||
return (
|
||||
<ActionConfigContext.Provider value={action}>
|
||||
{action.component}
|
||||
</ActionConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { isHeadlessFrontComponentMountedFamilySelector } from '@/front-components/selectors/isHeadlessFrontComponentMountedFamilySelector';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { ActionDisplay } from './ActionDisplay';
|
||||
|
||||
export const HeadlessFrontComponentAction = ({
|
||||
frontComponentId,
|
||||
onClick,
|
||||
}: {
|
||||
frontComponentId: string;
|
||||
onClick: () => void;
|
||||
}) => {
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu({
|
||||
closeSidePanelOnShowPageOptionsActionExecution: false,
|
||||
closeSidePanelOnCommandMenuListActionExecution: false,
|
||||
});
|
||||
|
||||
const isMounted = useAtomFamilySelectorValue(
|
||||
isHeadlessFrontComponentMountedFamilySelector,
|
||||
frontComponentId,
|
||||
);
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if (isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeActionMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <ActionDisplay onClick={handleClick} disabled={isMounted} />;
|
||||
};
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
import { ActionButton } from '@/action-menu/actions/display/components/ActionButton';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const meta: Meta<typeof ActionButton> = {
|
||||
title: 'Modules/ActionMenu/Actions/Display/ActionButton',
|
||||
component: ActionButton,
|
||||
decorators: [ComponentDecorator, RouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ActionButton>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockActionMenuActions({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesAction = mockActions.find(
|
||||
(action) => action.key === SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleAction = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesAction,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.shortLabel ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleAction,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const menuItem = await canvas.findByText(
|
||||
getActionLabel(goToPeopleAction?.shortLabel ?? ''),
|
||||
);
|
||||
expect(menuItem).toBeVisible();
|
||||
expect(canvas.getByRole('link')).toHaveAttribute('href', '/objects/people');
|
||||
},
|
||||
};
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
import { ActionComponent } from '@/action-menu/actions/display/components/ActionComponent';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const mockActions = createMockActionMenuActions({});
|
||||
|
||||
const addToFavoritesAction = mockActions.find(
|
||||
(action) => action.key === SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
if (!addToFavoritesAction) {
|
||||
throw new Error('Add to favorites action not found');
|
||||
}
|
||||
|
||||
const meta: Meta<typeof ActionComponent> = {
|
||||
title: 'Modules/ActionMenu/Actions/Display/ActionComponent',
|
||||
component: ActionComponent,
|
||||
decorators: [
|
||||
ComponentDecorator,
|
||||
(Story) => (
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
actionMenuType: 'index-page-action-menu',
|
||||
displayType: 'button',
|
||||
actions: [addToFavoritesAction],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
),
|
||||
],
|
||||
args: {
|
||||
action: addToFavoritesAction,
|
||||
},
|
||||
parameters: {
|
||||
container: {
|
||||
width: 'auto',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ActionComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
expect(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.shortLabel ?? ''),
|
||||
),
|
||||
).toBeVisible();
|
||||
},
|
||||
};
|
||||
-203
@@ -1,203 +0,0 @@
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { CancelDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/CancelDashboardSingleRecordAction';
|
||||
import { DuplicateDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/DuplicateDashboardSingleRecordAction';
|
||||
import { EditDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/EditDashboardSingleRecordAction';
|
||||
import { SaveDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/SaveDashboardSingleRecordAction';
|
||||
import { DashboardSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/types/DashboardSingleRecordActionKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from 'twenty-shared/types';
|
||||
import { PageLayoutSingleRecordActionKeys } from '@/page-layout/actions/PageLayoutSingleRecordActionKeys';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconCancel,
|
||||
IconCopyPlus,
|
||||
IconDeviceFloppy,
|
||||
IconPencil,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const DASHBOARD_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
config: {
|
||||
[PageLayoutSingleRecordActionKeys.EDIT_LAYOUT]: {
|
||||
key: PageLayoutSingleRecordActionKeys.EDIT_LAYOUT,
|
||||
label: msg`Edit Dashboard`,
|
||||
shortLabel: msg`Edit`,
|
||||
isPinned: true,
|
||||
position: 3,
|
||||
Icon: IconPencil,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
isDefined(selectedRecord?.pageLayoutId) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <EditDashboardSingleRecordAction />,
|
||||
},
|
||||
[PageLayoutSingleRecordActionKeys.SAVE_LAYOUT]: {
|
||||
key: PageLayoutSingleRecordActionKeys.SAVE_LAYOUT,
|
||||
label: msg`Save Dashboard`,
|
||||
shortLabel: msg`Save`,
|
||||
isPinned: true,
|
||||
isPrimaryCTA: true,
|
||||
position: 4,
|
||||
Icon: IconDeviceFloppy,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
isDefined(selectedRecord?.pageLayoutId) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [ActionViewType.PAGE_EDIT_MODE],
|
||||
component: <SaveDashboardSingleRecordAction />,
|
||||
},
|
||||
[PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION]: {
|
||||
key: PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION,
|
||||
label: msg`Cancel Edition`,
|
||||
shortLabel: msg`Cancel`,
|
||||
isPinned: true,
|
||||
position: 5,
|
||||
Icon: IconCancel,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
isDefined(selectedRecord?.pageLayoutId) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [ActionViewType.PAGE_EDIT_MODE],
|
||||
component: <CancelDashboardSingleRecordAction />,
|
||||
},
|
||||
[DashboardSingleRecordActionKeys.DUPLICATE_DASHBOARD]: {
|
||||
key: DashboardSingleRecordActionKeys.DUPLICATE_DASHBOARD,
|
||||
label: msg`Duplicate Dashboard`,
|
||||
shortLabel: msg`Duplicate`,
|
||||
isPinned: false,
|
||||
position: 6,
|
||||
Icon: IconCopyPlus,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <DuplicateDashboardSingleRecordAction />,
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordActionKeys.DELETE,
|
||||
SingleRecordActionKeys.DESTROY,
|
||||
SingleRecordActionKeys.RESTORE,
|
||||
MultipleRecordsActionKeys.DELETE,
|
||||
MultipleRecordsActionKeys.DESTROY,
|
||||
MultipleRecordsActionKeys.RESTORE,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 0,
|
||||
label: msg`Navigate to next dashboard`,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 1,
|
||||
label: msg`Navigate to previous dashboard`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
position: 2,
|
||||
label: msg`Create new dashboard`,
|
||||
},
|
||||
[SingleRecordActionKeys.DELETE]: {
|
||||
position: 7,
|
||||
label: msg`Delete dashboard`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DELETE]: {
|
||||
position: 12,
|
||||
label: msg`Delete dashboards`,
|
||||
},
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
position: 8,
|
||||
isPinned: true,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
position: 9,
|
||||
isPinned: true,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 10,
|
||||
label: msg`Export dashboard`,
|
||||
},
|
||||
[SingleRecordActionKeys.DESTROY]: {
|
||||
position: 11,
|
||||
label: msg`Permanently destroy dashboard`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DESTROY]: {
|
||||
position: 13,
|
||||
label: msg`Permanently destroy dashboards`,
|
||||
},
|
||||
[SingleRecordActionKeys.RESTORE]: {
|
||||
position: 14,
|
||||
label: msg`Restore dashboard`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.RESTORE]: {
|
||||
position: 15,
|
||||
label: msg`Restore dashboards`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 22,
|
||||
label: msg`See deleted dashboards`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 23,
|
||||
label: msg`Hide deleted dashboards`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 24,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 25,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 26,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 27,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
position: 28,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 29,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 30,
|
||||
},
|
||||
},
|
||||
});
|
||||
-843
@@ -1,843 +0,0 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { DeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/DeleteMultipleRecordsAction';
|
||||
import { DestroyMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/DestroyMultipleRecordsAction';
|
||||
import { ExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/ExportMultipleRecordsAction';
|
||||
import { MergeMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/MergeMultipleRecordsAction';
|
||||
import { RestoreMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/RestoreMultipleRecordsAction';
|
||||
import { UpdateMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/UpdateMultipleRecordsAction';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { CreateNewIndexRecordNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/CreateNewIndexRecordNoSelectionRecordAction';
|
||||
import { CreateNewViewNoSelectionRecord } from '@/action-menu/actions/record-actions/no-selection/components/CreateNewViewNoSelectionRecord';
|
||||
import { HideDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/HideDeletedRecordsNoSelectionRecordAction';
|
||||
import { ImportRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/ImportRecordsNoSelectionRecordAction';
|
||||
import { SeeDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/SeeDeletedRecordsNoSelectionRecordAction';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { AddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/AddToFavoritesSingleRecordAction';
|
||||
import { DeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction';
|
||||
import { DestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DestroySingleRecordAction';
|
||||
import { ExportNoteActionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/ExportNoteActionSingleRecordAction';
|
||||
import { ExportSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/ExportSingleRecordAction';
|
||||
import { NavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToNextRecordSingleRecordAction';
|
||||
import { NavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToPreviousRecordSingleRecordAction';
|
||||
import { RemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/RemoveFromFavoritesSingleRecordAction';
|
||||
import { RestoreSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/RestoreSingleRecordAction';
|
||||
import { CancelRecordPageLayoutSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/CancelRecordPageLayoutSingleRecordAction';
|
||||
import { EditRecordPageLayoutSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/EditRecordPageLayoutSingleRecordAction';
|
||||
import { SaveRecordPageLayoutSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/SaveRecordPageLayoutSingleRecordAction';
|
||||
import { RecordPageLayoutSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/record-page-layout-actions/types/RecordPageLayoutSingleRecordActionKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import {
|
||||
BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
MUTATION_MAX_MERGE_RECORDS,
|
||||
} from 'twenty-shared/constants';
|
||||
import {
|
||||
ActionViewType,
|
||||
AppPath,
|
||||
CoreObjectNameSingular,
|
||||
SettingsPath,
|
||||
} from 'twenty-shared/types';
|
||||
import {
|
||||
IconArrowMerge,
|
||||
IconBuildingSkyscraper,
|
||||
IconCancel,
|
||||
IconCheckbox,
|
||||
IconChevronDown,
|
||||
IconChevronUp,
|
||||
IconDeviceFloppy,
|
||||
IconEdit,
|
||||
IconEyeOff,
|
||||
IconFileExport,
|
||||
IconFileImport,
|
||||
IconHeart,
|
||||
IconHeartOff,
|
||||
IconLayout,
|
||||
IconLayoutDashboard,
|
||||
IconPencil,
|
||||
IconPlus,
|
||||
IconRefresh,
|
||||
IconRotate2,
|
||||
IconSettings,
|
||||
IconSettingsAutomation,
|
||||
IconTargetArrow,
|
||||
IconTrash,
|
||||
IconTrashX,
|
||||
IconUser,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
PermissionFlagType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
| NoSelectionRecordActionKeys
|
||||
| SingleRecordActionKeys
|
||||
| MultipleRecordsActionKeys
|
||||
| RecordPageLayoutSingleRecordActionKeys,
|
||||
ActionConfig
|
||||
> = {
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
label: msg`Navigate to next record`,
|
||||
position: 0,
|
||||
isPinned: true,
|
||||
Icon: IconChevronDown,
|
||||
shouldBeRegistered: ({ isInSidePanel }) => !isInSidePanel,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <NavigateToNextRecordSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
label: msg`Navigate to previous record`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
Icon: IconChevronUp,
|
||||
shouldBeRegistered: ({ isInSidePanel }) => !isInSidePanel,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <NavigateToPreviousRecordSingleRecordAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
label: msg`Create new record`,
|
||||
shortLabel: msg`New record`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
Icon: IconPlus,
|
||||
shouldBeRegistered: ({ objectPermissions, hasAnySoftDeleteFilterOnView }) =>
|
||||
(objectPermissions.canUpdateObjectRecords &&
|
||||
!hasAnySoftDeleteFilterOnView) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <CreateNewIndexRecordNoSelectionRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.DELETE]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.DELETE,
|
||||
label: msg`Delete`,
|
||||
shortLabel: msg`Delete`,
|
||||
position: 3,
|
||||
Icon: IconTrash,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
objectPermissions,
|
||||
}) =>
|
||||
(isDefined(selectedRecord) &&
|
||||
!selectedRecord.isRemote &&
|
||||
!hasAnySoftDeleteFilterOnView &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isDefined(selectedRecord?.deletedAt)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <DeleteSingleRecordAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DELETE]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.DELETE,
|
||||
label: msg`Delete records`,
|
||||
shortLabel: msg`Delete`,
|
||||
position: 4,
|
||||
Icon: IconTrash,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
(objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
!hasAnySoftDeleteFilterOnView &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <DeleteMultipleRecordsAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.RESTORE]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.RESTORE,
|
||||
label: msg`Restore record`,
|
||||
shortLabel: msg`Restore`,
|
||||
position: 5,
|
||||
Icon: IconRefresh,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
isShowPage,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
}) =>
|
||||
(!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
((isDefined(isShowPage) && isShowPage) ||
|
||||
(isDefined(hasAnySoftDeleteFilterOnView) &&
|
||||
hasAnySoftDeleteFilterOnView))) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <RestoreSingleRecordAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.RESTORE]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.RESTORE,
|
||||
label: msg`Restore records`,
|
||||
shortLabel: msg`Restore`,
|
||||
position: 6,
|
||||
Icon: IconRefresh,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
(objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(hasAnySoftDeleteFilterOnView) &&
|
||||
hasAnySoftDeleteFilterOnView &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <RestoreMultipleRecordsAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.DESTROY]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.DESTROY,
|
||||
label: msg`Permanently destroy record`,
|
||||
shortLabel: msg`Destroy`,
|
||||
position: 7,
|
||||
Icon: IconTrashX,
|
||||
accent: 'danger',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions, isRemote }) =>
|
||||
(objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <DestroySingleRecordAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DESTROY]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.DESTROY,
|
||||
label: msg`Permanently destroy records`,
|
||||
shortLabel: msg`Destroy`,
|
||||
position: 8,
|
||||
Icon: IconTrashX,
|
||||
accent: 'danger',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
(objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(hasAnySoftDeleteFilterOnView) &&
|
||||
hasAnySoftDeleteFilterOnView &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <DestroyMultipleRecordsAction />,
|
||||
},
|
||||
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
label: msg`Add to favorites`,
|
||||
shortLabel: msg`Add to favorites`,
|
||||
position: 9,
|
||||
isPinned: true,
|
||||
Icon: IconHeart,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
isFavorite,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
}) =>
|
||||
!selectedRecord?.isRemote &&
|
||||
!isFavorite &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <AddToFavoritesSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
label: msg`Remove from favorites`,
|
||||
shortLabel: msg`Remove from favorites`,
|
||||
isPinned: true,
|
||||
position: 10,
|
||||
Icon: IconHeartOff,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
isFavorite,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
}) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
isDefined(isFavorite) &&
|
||||
isFavorite &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <RemoveFromFavoritesSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_NOTE_TO_PDF]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.EXPORT_NOTE_TO_PDF,
|
||||
label: msg`Export to PDF`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 11,
|
||||
isPinned: false,
|
||||
Icon: IconFileExport,
|
||||
shouldBeRegistered: ({ selectedRecord, isNoteOrTask }) =>
|
||||
isDefined(isNoteOrTask) &&
|
||||
isNoteOrTask &&
|
||||
isNonEmptyString(selectedRecord?.bodyV2?.blocknote),
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <ExportNoteActionSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 12,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
|
||||
component: <ExportMultipleRecordsAction />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 13,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <ExportSingleRecordAction />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[MultipleRecordsActionKeys.UPDATE]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.UPDATE,
|
||||
label: msg`Update records`,
|
||||
shortLabel: msg`Update`,
|
||||
position: 14,
|
||||
Icon: IconEdit,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ objectPermissions, isRemote }) =>
|
||||
objectPermissions.canUpdateObjectRecords && !isRemote,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <UpdateMultipleRecordsAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.MERGE]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.MERGE,
|
||||
label: msg`Merge records`,
|
||||
shortLabel: msg`Merge`,
|
||||
position: 15,
|
||||
Icon: IconArrowMerge,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
numberOfSelectedRecords,
|
||||
objectPermissions,
|
||||
}) =>
|
||||
isDefined(objectMetadataItem?.duplicateCriteria) &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
Boolean(objectPermissions.canUpdateObjectRecords) &&
|
||||
Boolean(objectPermissions.canDestroyObjectRecords) &&
|
||||
numberOfSelectedRecords <= MUTATION_MAX_MERGE_RECORDS,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <MergeMultipleRecordsAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.EXPORT]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.EXPORT,
|
||||
label: msg`Export records`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 16,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <ExportMultipleRecordsAction />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.IMPORT_RECORDS]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.IMPORT_RECORDS,
|
||||
label: msg`Import records`,
|
||||
shortLabel: msg`Import`,
|
||||
position: 17,
|
||||
Icon: IconFileImport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <ImportRecordsNoSelectionRecordAction />,
|
||||
requiredPermissionFlag: PermissionFlagType.IMPORT_CSV,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
label: msg`Export view`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 18,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <ExportMultipleRecordsAction />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
label: msg`See deleted records`,
|
||||
shortLabel: msg`Deleted records`,
|
||||
position: 19,
|
||||
Icon: IconRotate2,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <SeeDeletedRecordsNoSelectionRecordAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_VIEW]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.CREATE_NEW_VIEW,
|
||||
label: msg`Create View`,
|
||||
shortLabel: msg`Create View`,
|
||||
position: 20,
|
||||
Icon: IconLayout,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <CreateNewViewNoSelectionRecord />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
label: msg`Hide deleted records`,
|
||||
shortLabel: msg`Hide deleted`,
|
||||
position: 21,
|
||||
Icon: IconEyeOff,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
isDefined(hasAnySoftDeleteFilterOnView) && hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <HideDeletedRecordsNoSelectionRecordAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
label: msg`Go to Workflows`,
|
||||
shortLabel: msg`See Workflows`,
|
||||
position: 22,
|
||||
Icon: IconSettingsAutomation,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Workflow) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Workflow ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Workflow }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'W'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
label: msg`Go to People`,
|
||||
shortLabel: msg`People`,
|
||||
position: 23,
|
||||
Icon: IconUser,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Person) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Person ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Person }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'P'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
label: msg`Go to Companies`,
|
||||
shortLabel: msg`Companies`,
|
||||
position: 24,
|
||||
Icon: IconBuildingSkyscraper,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Company) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Company ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Company }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'C'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_DASHBOARDS,
|
||||
label: msg`Go to Dashboards`,
|
||||
shortLabel: msg`Dashboards`,
|
||||
position: 25,
|
||||
Icon: IconLayoutDashboard,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Dashboard) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Dashboard }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'D'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
label: msg`Go to Opportunities`,
|
||||
shortLabel: msg`Opportunities`,
|
||||
position: 26,
|
||||
Icon: IconTargetArrow,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Opportunity) &&
|
||||
(objectMetadataItem?.nameSingular !==
|
||||
CoreObjectNameSingular.Opportunity ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Opportunity }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'O'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
label: msg`Go to Settings`,
|
||||
shortLabel: msg`Settings`,
|
||||
position: 27,
|
||||
Icon: IconSettings,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.SettingsCatchAll}
|
||||
params={{
|
||||
'*': SettingsPath.ProfilePage,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'S'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
label: msg`Go to Tasks`,
|
||||
shortLabel: msg`Tasks`,
|
||||
position: 28,
|
||||
Icon: IconCheckbox,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Task) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Task ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Task }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'T'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
label: msg`Go to Notes`,
|
||||
shortLabel: msg`Notes`,
|
||||
position: 29,
|
||||
Icon: IconCheckbox,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Note) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Note ||
|
||||
viewType === ActionViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Note }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'N'],
|
||||
},
|
||||
|
||||
[RecordPageLayoutSingleRecordActionKeys.EDIT_RECORD_PAGE_LAYOUT]: {
|
||||
key: RecordPageLayoutSingleRecordActionKeys.EDIT_RECORD_PAGE_LAYOUT,
|
||||
label: msg`Edit Page Layout`,
|
||||
shortLabel: msg`Edit Layout`,
|
||||
isPinned: true,
|
||||
position: 30,
|
||||
Icon: IconPencil,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
isFeatureFlagEnabled,
|
||||
}) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
|
||||
) &&
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <EditRecordPageLayoutSingleRecordAction />,
|
||||
},
|
||||
[RecordPageLayoutSingleRecordActionKeys.SAVE_RECORD_PAGE_LAYOUT]: {
|
||||
key: RecordPageLayoutSingleRecordActionKeys.SAVE_RECORD_PAGE_LAYOUT,
|
||||
label: msg`Save Page Layout`,
|
||||
shortLabel: msg`Save`,
|
||||
isPinned: true,
|
||||
isPrimaryCTA: true,
|
||||
position: 31,
|
||||
Icon: IconDeviceFloppy,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
isFeatureFlagEnabled,
|
||||
}) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
|
||||
) &&
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
|
||||
availableOn: [ActionViewType.PAGE_EDIT_MODE],
|
||||
component: <SaveRecordPageLayoutSingleRecordAction />,
|
||||
},
|
||||
[RecordPageLayoutSingleRecordActionKeys.CANCEL_RECORD_PAGE_LAYOUT_EDITION]: {
|
||||
key: RecordPageLayoutSingleRecordActionKeys.CANCEL_RECORD_PAGE_LAYOUT_EDITION,
|
||||
label: msg`Cancel Edition`,
|
||||
shortLabel: msg`Cancel`,
|
||||
isPinned: true,
|
||||
position: 32,
|
||||
Icon: IconCancel,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
isFeatureFlagEnabled,
|
||||
}) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
|
||||
) &&
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
|
||||
availableOn: [ActionViewType.PAGE_EDIT_MODE],
|
||||
component: <CancelRecordPageLayoutSingleRecordAction />,
|
||||
},
|
||||
};
|
||||
-387
@@ -1,387 +0,0 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { NoSelectionWorkflowRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/workflow-actions/types/NoSelectionWorkflowRecordActionKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { ActivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/ActivateWorkflowSingleRecordAction';
|
||||
import { AddNodeWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/AddNodeWorkflowSingleRecordAction';
|
||||
import { DeactivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/DeactivateWorkflowSingleRecordAction';
|
||||
import { DiscardDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/DiscardDraftWorkflowSingleRecordAction';
|
||||
import { DuplicateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/DuplicateWorkflowSingleRecordAction';
|
||||
import { SeeActiveVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/SeeActiveVersionWorkflowSingleRecordAction';
|
||||
import { SeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/SeeRunsWorkflowSingleRecordAction';
|
||||
import { SeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/SeeVersionsWorkflowSingleRecordAction';
|
||||
import { TestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/TestWorkflowSingleRecordAction';
|
||||
import { TidyUpWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/TidyUpWorkflowSingleRecordAction';
|
||||
import { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType, AppPath } from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import {
|
||||
type WorkflowStep,
|
||||
type WorkflowTrigger,
|
||||
type WorkflowWithCurrentVersion,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconCopy,
|
||||
IconHistoryToggle,
|
||||
IconNoteOff,
|
||||
IconPlayerPause,
|
||||
IconPlayerPlay,
|
||||
IconPlus,
|
||||
IconPower,
|
||||
IconReorder,
|
||||
IconVersions,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
const areWorkflowTriggerAndStepsDefined = (
|
||||
workflowWithCurrentVersion: WorkflowWithCurrentVersion | undefined,
|
||||
): workflowWithCurrentVersion is WorkflowWithCurrentVersion & {
|
||||
currentVersion: {
|
||||
trigger: WorkflowTrigger;
|
||||
steps: Array<WorkflowStep>;
|
||||
};
|
||||
} => {
|
||||
return (
|
||||
isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) &&
|
||||
isDefined(workflowWithCurrentVersion.currentVersion?.steps) &&
|
||||
workflowWithCurrentVersion.currentVersion.steps.length > 0
|
||||
);
|
||||
};
|
||||
|
||||
export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
config: {
|
||||
[WorkflowSingleRecordActionKeys.ACTIVATE]: {
|
||||
key: WorkflowSingleRecordActionKeys.ACTIVATE,
|
||||
label: msg`Activate Workflow`,
|
||||
shortLabel: msg`Activate`,
|
||||
isPinned: true,
|
||||
isPrimaryCTA: true,
|
||||
position: 3,
|
||||
Icon: IconPower,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
(workflowWithCurrentVersion.currentVersion.status === 'DRAFT' ||
|
||||
!workflowWithCurrentVersion.versions?.some(
|
||||
(version) => version.status === 'ACTIVE',
|
||||
)) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <ActivateWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.DEACTIVATE]: {
|
||||
key: WorkflowSingleRecordActionKeys.DEACTIVATE,
|
||||
label: msg`Deactivate Workflow`,
|
||||
shortLabel: msg`Deactivate`,
|
||||
isPinned: true,
|
||||
position: 4,
|
||||
Icon: IconPlayerPause,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE' &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <DeactivateWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.DISCARD_DRAFT]: {
|
||||
key: WorkflowSingleRecordActionKeys.DISCARD_DRAFT,
|
||||
label: msg`Discard Draft`,
|
||||
shortLabel: msg`Discard Draft`,
|
||||
isPinned: true,
|
||||
position: 5,
|
||||
Icon: IconNoteOff,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.versions.length > 1 &&
|
||||
workflowWithCurrentVersion.currentVersion.status === 'DRAFT' &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <DiscardDraftWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.TEST]: {
|
||||
key: WorkflowSingleRecordActionKeys.TEST,
|
||||
label: msg`Test Workflow`,
|
||||
shortLabel: msg`Test`,
|
||||
isPinned: true,
|
||||
position: 6,
|
||||
Icon: IconPlayerPlay,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
((workflowWithCurrentVersion.currentVersion.trigger.type === 'MANUAL' &&
|
||||
!isDefined(
|
||||
workflowWithCurrentVersion.currentVersion.trigger.settings
|
||||
.objectType,
|
||||
)) ||
|
||||
workflowWithCurrentVersion.currentVersion.trigger.type ===
|
||||
'WEBHOOK' ||
|
||||
workflowWithCurrentVersion.currentVersion.trigger.type === 'CRON') &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <TestWorkflowSingleRecordAction />,
|
||||
},
|
||||
|
||||
[WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION]: {
|
||||
key: WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION,
|
||||
label: msg`See active version`,
|
||||
shortLabel: msg`See active version`,
|
||||
isPinned: false,
|
||||
position: 7,
|
||||
Icon: IconVersions,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion, selectedRecord }) =>
|
||||
(workflowWithCurrentVersion?.statuses?.includes('ACTIVE') || false) &&
|
||||
(workflowWithCurrentVersion?.statuses?.includes('DRAFT') || false) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeActiveVersionWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.SEE_RUNS]: {
|
||||
key: WorkflowSingleRecordActionKeys.SEE_RUNS,
|
||||
label: msg`See runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
isPinned: true,
|
||||
position: 8,
|
||||
Icon: IconHistoryToggle,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeRunsWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.SEE_VERSIONS]: {
|
||||
key: WorkflowSingleRecordActionKeys.SEE_VERSIONS,
|
||||
label: msg`See versions history`,
|
||||
shortLabel: msg`See versions`,
|
||||
isPinned: false,
|
||||
position: 9,
|
||||
Icon: IconVersions,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeVersionsWorkflowSingleRecordAction />,
|
||||
},
|
||||
|
||||
[WorkflowSingleRecordActionKeys.ADD_NODE]: {
|
||||
key: WorkflowSingleRecordActionKeys.ADD_NODE,
|
||||
label: msg`Add a node`,
|
||||
shortLabel: msg`Add a node`,
|
||||
isPinned: true,
|
||||
position: 10,
|
||||
Icon: IconPlus,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <AddNodeWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.TIDY_UP]: {
|
||||
key: WorkflowSingleRecordActionKeys.TIDY_UP,
|
||||
label: msg`Tidy up workflow`,
|
||||
shortLabel: msg`Tidy up`,
|
||||
isPinned: false,
|
||||
position: 11,
|
||||
Icon: IconReorder,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
component: <TidyUpWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.DUPLICATE_WORKFLOW]: {
|
||||
key: WorkflowSingleRecordActionKeys.DUPLICATE_WORKFLOW,
|
||||
label: msg`Duplicate Workflow`,
|
||||
shortLabel: msg`Duplicate`,
|
||||
isPinned: false,
|
||||
position: 12,
|
||||
Icon: IconCopy,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
isDefined(workflowWithCurrentVersion.currentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <DuplicateWorkflowSingleRecordAction />,
|
||||
},
|
||||
[NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS,
|
||||
label: msg`Go to runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
position: 22,
|
||||
Icon: IconHistoryToggle,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordActionKeys.DELETE,
|
||||
SingleRecordActionKeys.DESTROY,
|
||||
SingleRecordActionKeys.RESTORE,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsActionKeys.DELETE,
|
||||
MultipleRecordsActionKeys.DESTROY,
|
||||
MultipleRecordsActionKeys.RESTORE,
|
||||
MultipleRecordsActionKeys.EXPORT,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 0,
|
||||
label: msg`Navigate to next workflow`,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 1,
|
||||
label: msg`Navigate to previous workflow`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
position: 2,
|
||||
label: msg`Create new workflow`,
|
||||
},
|
||||
[SingleRecordActionKeys.DELETE]: {
|
||||
position: 12,
|
||||
label: msg`Delete workflow`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DELETE]: {
|
||||
position: 13,
|
||||
label: msg`Delete workflows`,
|
||||
},
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
position: 14,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
position: 15,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordActionKeys.DESTROY]: {
|
||||
position: 16,
|
||||
label: msg`Permanently destroy workflow`,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 17,
|
||||
label: msg`Export workflow`,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 18,
|
||||
label: msg`Export workflow`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.EXPORT]: {
|
||||
position: 19,
|
||||
label: msg`Export workflows`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||
position: 20,
|
||||
label: msg`Export view`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DESTROY]: {
|
||||
position: 21,
|
||||
label: msg`Permanently destroy workflows`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 22,
|
||||
label: msg`See deleted workflows`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 23,
|
||||
label: msg`Hide deleted workflows`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.IMPORT_RECORDS]: {
|
||||
position: 24,
|
||||
label: msg`Import workflows`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 25,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 26,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 27,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 28,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
position: 29,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 30,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 31,
|
||||
},
|
||||
},
|
||||
});
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { SeeVersionWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/components/SeeVersionWorkflowRunSingleRecordAction';
|
||||
import { SeeWorkflowWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/components/SeeWorkflowWorkflowRunSingleRecordAction';
|
||||
import { StopWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/components/StopWorkflowRunSingleRecordAction';
|
||||
import { WorkflowRunSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/types/WorkflowRunSingleRecordActionsKeys';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from 'twenty-shared/types';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
IconPlayerStop,
|
||||
IconSettingsAutomation,
|
||||
IconVersions,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
config: {
|
||||
[WorkflowRunSingleRecordActionKeys.SEE_VERSION]: {
|
||||
key: WorkflowRunSingleRecordActionKeys.SEE_VERSION,
|
||||
label: msg`See version`,
|
||||
shortLabel: msg`See version`,
|
||||
position: 0,
|
||||
isPinned: true,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconVersions,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeVersionWorkflowRunSingleRecordAction />,
|
||||
},
|
||||
|
||||
[WorkflowRunSingleRecordActionKeys.SEE_WORKFLOW]: {
|
||||
key: WorkflowRunSingleRecordActionKeys.SEE_WORKFLOW,
|
||||
label: msg`See workflow`,
|
||||
shortLabel: msg`See workflow`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconSettingsAutomation,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeWorkflowWorkflowRunSingleRecordAction />,
|
||||
},
|
||||
[WorkflowRunSingleRecordActionKeys.STOP]: {
|
||||
key: WorkflowRunSingleRecordActionKeys.STOP,
|
||||
label: msg`Stop`,
|
||||
shortLabel: msg`Stop`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconPlayerStop,
|
||||
shouldBeRegistered: ({ selectedRecord, isSelectAll }) => {
|
||||
if (isSelectAll === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const stoppableStatuses = ['NOT_STARTED', 'ENQUEUED', 'RUNNING'];
|
||||
return stoppableStatuses.includes(selectedRecord?.status);
|
||||
},
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
],
|
||||
component: <StopWorkflowRunSingleRecordAction />,
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsActionKeys.EXPORT,
|
||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 3,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 4,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 5,
|
||||
label: msg`Export run`,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 5,
|
||||
label: msg`Export run`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.EXPORT]: {
|
||||
position: 6,
|
||||
label: msg`Export runs`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||
position: 7,
|
||||
label: msg`Export view`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 8,
|
||||
label: msg`See deleted runs`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 9,
|
||||
label: msg`Hide deleted runs`,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 10,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 11,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 12,
|
||||
isPinned: true,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 13,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 14,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 15,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 16,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
position: 17,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 18,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 19,
|
||||
},
|
||||
},
|
||||
});
|
||||
-200
@@ -1,200 +0,0 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { NoSelectionWorkflowRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/workflow-actions/types/NoSelectionWorkflowRecordActionKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { SeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/SeeRunsWorkflowVersionSingleRecordAction';
|
||||
import { SeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/SeeVersionsWorkflowVersionSingleRecordAction';
|
||||
import { SeeWorkflowWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/SeeWorkflowWorkflowVersionSingleRecordAction';
|
||||
import { UseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/UseAsDraftWorkflowVersionSingleRecordAction';
|
||||
import { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType, AppPath } from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconHistoryToggle,
|
||||
IconPencil,
|
||||
IconSettingsAutomation,
|
||||
IconVersions,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
{
|
||||
config: {
|
||||
[WorkflowVersionSingleRecordActionKeys.SEE_RUNS]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.SEE_RUNS,
|
||||
label: msg`See runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconHistoryToggle,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeRunsWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordActionKeys.SEE_WORKFLOW]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.SEE_WORKFLOW,
|
||||
label: msg`See workflow`,
|
||||
shortLabel: msg`See workflow`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconSettingsAutomation,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord?.workflow?.id),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeWorkflowWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordActionKeys.USE_AS_DRAFT]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.USE_AS_DRAFT,
|
||||
label: msg`Use as draft`,
|
||||
shortLabel: msg`Use as draft`,
|
||||
position: 3,
|
||||
isPinned: true,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconPencil,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && selectedRecord.status !== 'DRAFT',
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <UseAsDraftWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordActionKeys.SEE_VERSIONS]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.SEE_VERSIONS,
|
||||
label: msg`See versions history`,
|
||||
shortLabel: msg`See versions`,
|
||||
position: 4,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconVersions,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeVersionsWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS,
|
||||
label: msg`Go to runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
position: 14,
|
||||
Icon: IconHistoryToggle,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsActionKeys.EXPORT,
|
||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
position: 5,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
position: 6,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 7,
|
||||
label: msg`Export version`,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 7,
|
||||
label: msg`Export version`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.EXPORT]: {
|
||||
position: 8,
|
||||
label: msg`Export versions`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||
position: 9,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 10,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 11,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 12,
|
||||
label: msg`Navigate to previous version`,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 13,
|
||||
label: msg`Navigate to next version`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 15,
|
||||
isPinned: true,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 16,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 17,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 18,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 19,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
position: 20,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 21,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType, AppPath, SettingsPath } from 'twenty-shared/types';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { IconSettings } from 'twenty-ui/display';
|
||||
|
||||
export const WORKSPACE_MEMBERS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
{
|
||||
config: {
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
label: msg`Manage members in settings`,
|
||||
shortLabel: msg`Manage in settings`,
|
||||
position: 14,
|
||||
Icon: IconSettings,
|
||||
isPinned: true,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.SettingsCatchAll}
|
||||
params={{
|
||||
'*': SettingsPath.WorkspaceMembersPage,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'S'],
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsActionKeys.EXPORT,
|
||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 0,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 1,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 2,
|
||||
label: msg`Export member`,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 2,
|
||||
label: msg`Export member`,
|
||||
},
|
||||
[MultipleRecordsActionKeys.EXPORT]: {
|
||||
position: 3,
|
||||
label: msg`Export members`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||
position: 4,
|
||||
label: msg`Export view`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 5,
|
||||
label: msg`See deleted members`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 6,
|
||||
label: msg`Hide deleted members`,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 7,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 8,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 9,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 10,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 11,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 12,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 13,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 15,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export enum NoSelectionWorkflowRecordActionKeys {
|
||||
GO_TO_RUNS = 'go-to-runs',
|
||||
}
|
||||
-324
@@ -1,324 +0,0 @@
|
||||
import { DEFAULT_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { type DefaultRecordActionConfigKeys } from '@/action-menu/actions/types/DefaultRecordActionConfigKeys';
|
||||
import { IconHeart, IconPlus } from 'twenty-ui/display';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
|
||||
const MockComponent = <div>Mock Component</div>;
|
||||
|
||||
describe('inheritActionsFromDefaultConfig', () => {
|
||||
it('should return empty object when no action keys are provided', () => {
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: {},
|
||||
actionKeys: [],
|
||||
propertiesToOverwrite: {},
|
||||
});
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should return only provided config when no default action keys are specified', () => {
|
||||
const customConfig: Record<string, ActionConfig> = {
|
||||
'custom-action': {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: 'custom-action',
|
||||
label: 'Custom Action',
|
||||
position: 100,
|
||||
Icon: IconPlus,
|
||||
shouldBeRegistered: () => true,
|
||||
component: MockComponent,
|
||||
},
|
||||
};
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: customConfig,
|
||||
actionKeys: [],
|
||||
propertiesToOverwrite: {},
|
||||
});
|
||||
|
||||
expect(result).toEqual(customConfig);
|
||||
});
|
||||
|
||||
it('should inherit actions from default config', () => {
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
];
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: {},
|
||||
actionKeys,
|
||||
propertiesToOverwrite: {},
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]:
|
||||
DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]:
|
||||
DEFAULT_RECORD_ACTIONS_CONFIG[SingleRecordActionKeys.ADD_TO_FAVORITES],
|
||||
});
|
||||
});
|
||||
|
||||
it('should overwrite specific properties of inherited actions', () => {
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
];
|
||||
|
||||
const propertiesToOverwrite = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
label: 'Custom Create Label',
|
||||
position: 999,
|
||||
isPinned: false,
|
||||
},
|
||||
};
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: {},
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
});
|
||||
|
||||
const expectedAction = {
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
label: 'Custom Create Label',
|
||||
position: 999,
|
||||
isPinned: false,
|
||||
};
|
||||
|
||||
expect(result).toEqual({
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: expectedAction,
|
||||
});
|
||||
});
|
||||
|
||||
it('should overwrite properties for multiple actions', () => {
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
];
|
||||
|
||||
const propertiesToOverwrite = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
position: 10,
|
||||
},
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
label: 'Custom Favorite Label',
|
||||
Icon: IconHeart,
|
||||
},
|
||||
};
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: {},
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
});
|
||||
|
||||
expect(result[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]).toEqual({
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
position: 10,
|
||||
});
|
||||
|
||||
expect(result[SingleRecordActionKeys.ADD_TO_FAVORITES]).toEqual({
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[SingleRecordActionKeys.ADD_TO_FAVORITES],
|
||||
label: 'Custom Favorite Label',
|
||||
Icon: IconHeart,
|
||||
});
|
||||
});
|
||||
|
||||
it('should only overwrite properties for specified actions', () => {
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
];
|
||||
|
||||
const propertiesToOverwrite = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
position: 10,
|
||||
},
|
||||
};
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: {},
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
});
|
||||
|
||||
expect(result[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]).toEqual({
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
position: 10,
|
||||
});
|
||||
|
||||
expect(result[SingleRecordActionKeys.ADD_TO_FAVORITES]).toEqual(
|
||||
DEFAULT_RECORD_ACTIONS_CONFIG[SingleRecordActionKeys.ADD_TO_FAVORITES],
|
||||
);
|
||||
});
|
||||
|
||||
it('should merge inherited actions with provided config', () => {
|
||||
const customConfig: Record<string, ActionConfig> = {
|
||||
'custom-action': {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: 'custom-action',
|
||||
label: 'Custom Action',
|
||||
position: 100,
|
||||
Icon: IconPlus,
|
||||
shouldBeRegistered: () => true,
|
||||
component: MockComponent,
|
||||
},
|
||||
};
|
||||
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
];
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: customConfig,
|
||||
actionKeys,
|
||||
propertiesToOverwrite: {},
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]:
|
||||
DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
'custom-action': customConfig['custom-action'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should prioritize provided config over inherited actions when keys conflict', () => {
|
||||
const customConfig: Record<string, ActionConfig> = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
label: 'Overridden Create Action',
|
||||
position: 999,
|
||||
Icon: IconHeart,
|
||||
shouldBeRegistered: () => false,
|
||||
component: MockComponent,
|
||||
},
|
||||
};
|
||||
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
];
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: customConfig,
|
||||
actionKeys,
|
||||
propertiesToOverwrite: {},
|
||||
});
|
||||
|
||||
expect(result[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]).toEqual(
|
||||
customConfig[NoSelectionRecordActionKeys.CREATE_NEW_RECORD],
|
||||
);
|
||||
expect(result[NoSelectionRecordActionKeys.CREATE_NEW_RECORD].label).toBe(
|
||||
'Overridden Create Action',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle complex scenario with inheritance, overrides, and custom config', () => {
|
||||
const customConfig: Record<string, ActionConfig> = {
|
||||
'custom-action-1': {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: 'custom-action-1',
|
||||
label: 'Custom Action 1',
|
||||
position: 50,
|
||||
Icon: IconPlus,
|
||||
shouldBeRegistered: () => true,
|
||||
component: MockComponent,
|
||||
},
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
label: 'Completely Custom Favorites',
|
||||
position: 1000,
|
||||
Icon: IconHeart,
|
||||
shouldBeRegistered: () => false,
|
||||
component: MockComponent,
|
||||
},
|
||||
};
|
||||
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
];
|
||||
|
||||
const propertiesToOverwrite = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
label: 'Modified Create Label',
|
||||
position: 5,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
isPinned: false,
|
||||
},
|
||||
};
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: customConfig,
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
});
|
||||
|
||||
expect(Object.keys(result)).toHaveLength(4);
|
||||
|
||||
expect(result['custom-action-1']).toEqual(customConfig['custom-action-1']);
|
||||
|
||||
expect(result[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]).toEqual({
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
label: 'Modified Create Label',
|
||||
position: 5,
|
||||
});
|
||||
|
||||
expect(result[SingleRecordActionKeys.ADD_TO_FAVORITES]).toEqual(
|
||||
customConfig[SingleRecordActionKeys.ADD_TO_FAVORITES],
|
||||
);
|
||||
|
||||
expect(result[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]).toEqual({
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES
|
||||
],
|
||||
isPinned: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty overrides gracefully', () => {
|
||||
const actionKeys: DefaultRecordActionConfigKeys[] = [
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
];
|
||||
|
||||
const propertiesToOverwrite = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {},
|
||||
};
|
||||
|
||||
const result = inheritActionsFromDefaultConfig({
|
||||
config: {},
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
});
|
||||
|
||||
expect(result[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]).toEqual(
|
||||
DEFAULT_RECORD_ACTIONS_CONFIG[
|
||||
NoSelectionRecordActionKeys.CREATE_NEW_RECORD
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import { DEFAULT_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { type DefaultRecordActionConfigKeys } from '@/action-menu/actions/types/DefaultRecordActionConfigKeys';
|
||||
|
||||
export const inheritActionsFromDefaultConfig = ({
|
||||
config,
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
}: {
|
||||
config: Record<string, ActionConfig>;
|
||||
actionKeys: DefaultRecordActionConfigKeys[];
|
||||
propertiesToOverwrite: Partial<
|
||||
Record<DefaultRecordActionConfigKeys, Partial<ActionConfig>>
|
||||
>;
|
||||
}): Record<string, ActionConfig> => {
|
||||
const actionsFromDefaultConfig = actionKeys.reduce(
|
||||
(acc, key) => ({
|
||||
...acc,
|
||||
[key]: {
|
||||
...DEFAULT_RECORD_ACTIONS_CONFIG[key],
|
||||
...propertiesToOverwrite[key],
|
||||
},
|
||||
}),
|
||||
{} as Record<string, ActionConfig>,
|
||||
);
|
||||
|
||||
return {
|
||||
...actionsFromDefaultConfig,
|
||||
...config,
|
||||
};
|
||||
};
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
import { ActionOpenSidePanelPage } from '@/action-menu/actions/components/ActionOpenSidePanelPage';
|
||||
import { EditNavigationSidebarNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/EditNavigationSidebarNoSelectionRecordAction';
|
||||
import { RecordAgnosticActionsKeys } from '@/action-menu/actions/record-agnostic-actions/types/RecordAgnosticActionsKeys';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { ActionViewType, SidePanelPages } from 'twenty-shared/types';
|
||||
import {
|
||||
IconHistory,
|
||||
IconLayout,
|
||||
IconSearch,
|
||||
IconSparkles,
|
||||
} from 'twenty-ui/display';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const RECORD_AGNOSTIC_ACTIONS_CONFIG: Record<string, ActionConfig> = {
|
||||
[RecordAgnosticActionsKeys.SEARCH_RECORDS]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.SEARCH_RECORDS,
|
||||
label: msg`Search records`,
|
||||
shortLabel: msg`Search`,
|
||||
position: 0,
|
||||
isPinned: false,
|
||||
Icon: IconSearch,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
component: (
|
||||
<ActionOpenSidePanelPage
|
||||
page={SidePanelPages.SearchRecords}
|
||||
pageTitle={msg`Search`}
|
||||
pageIcon={IconSearch}
|
||||
shouldResetSearchState={true}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['/'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticActionsKeys.SEARCH_RECORDS_FALLBACK]: {
|
||||
type: ActionType.Fallback,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.SEARCH_RECORDS_FALLBACK,
|
||||
label: msg`Search records`,
|
||||
shortLabel: msg`Search`,
|
||||
position: 1,
|
||||
isPinned: false,
|
||||
Icon: IconSearch,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
component: (
|
||||
<ActionOpenSidePanelPage
|
||||
page={SidePanelPages.SearchRecords}
|
||||
pageTitle={msg`Search`}
|
||||
pageIcon={IconSearch}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['/'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticActionsKeys.ASK_AI]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.ASK_AI,
|
||||
label: msg`Ask AI`,
|
||||
shortLabel: msg`Ask AI`,
|
||||
position: 2,
|
||||
isPinned: false,
|
||||
Icon: IconSparkles,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
component: (
|
||||
<ActionOpenSidePanelPage
|
||||
page={SidePanelPages.AskAI}
|
||||
pageTitle={msg`Ask AI`}
|
||||
pageIcon={IconSparkles}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['@'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticActionsKeys.VIEW_PREVIOUS_AI_CHATS]: {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.VIEW_PREVIOUS_AI_CHATS,
|
||||
label: msg`View Previous AI Chats`,
|
||||
shortLabel: msg`Previous AI Chats`,
|
||||
position: 3,
|
||||
isPinned: false,
|
||||
Icon: IconHistory,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
component: (
|
||||
<ActionOpenSidePanelPage
|
||||
page={SidePanelPages.ViewPreviousAIChats}
|
||||
pageTitle={msg`View Previous AI Chats`}
|
||||
pageIcon={IconSparkles}
|
||||
/>
|
||||
),
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticActionsKeys.EDIT_NAVIGATION_SIDEBAR]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.EDIT_NAVIGATION_SIDEBAR,
|
||||
label: msg`Edit navigation sidebar`,
|
||||
shortLabel: msg`Edit sidebar`,
|
||||
position: 4,
|
||||
Icon: IconLayout,
|
||||
isPinned: false,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
shouldBeRegistered: ({ isFeatureFlagEnabled }) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
|
||||
),
|
||||
component: <EditNavigationSidebarNoSelectionRecordAction />,
|
||||
},
|
||||
};
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
import { RECORD_AGNOSTIC_ACTIONS_CONFIG } from '@/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig';
|
||||
import { RecordAgnosticActionsKeys } from '@/action-menu/actions/record-agnostic-actions/types/RecordAgnosticActionsKeys';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useRecordAgnosticActions = () => {
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const actions: Record<string, ActionConfig> = {
|
||||
[RecordAgnosticActionsKeys.SEARCH_RECORDS]:
|
||||
RECORD_AGNOSTIC_ACTIONS_CONFIG[RecordAgnosticActionsKeys.SEARCH_RECORDS],
|
||||
[RecordAgnosticActionsKeys.SEARCH_RECORDS_FALLBACK]:
|
||||
RECORD_AGNOSTIC_ACTIONS_CONFIG[
|
||||
RecordAgnosticActionsKeys.SEARCH_RECORDS_FALLBACK
|
||||
],
|
||||
[RecordAgnosticActionsKeys.EDIT_NAVIGATION_SIDEBAR]:
|
||||
RECORD_AGNOSTIC_ACTIONS_CONFIG[
|
||||
RecordAgnosticActionsKeys.EDIT_NAVIGATION_SIDEBAR
|
||||
],
|
||||
};
|
||||
|
||||
if (isAiEnabled) {
|
||||
actions[RecordAgnosticActionsKeys.ASK_AI] =
|
||||
RECORD_AGNOSTIC_ACTIONS_CONFIG[RecordAgnosticActionsKeys.ASK_AI];
|
||||
actions[RecordAgnosticActionsKeys.VIEW_PREVIOUS_AI_CHATS] =
|
||||
RECORD_AGNOSTIC_ACTIONS_CONFIG[
|
||||
RecordAgnosticActionsKeys.VIEW_PREVIOUS_AI_CHATS
|
||||
];
|
||||
}
|
||||
|
||||
return actions;
|
||||
};
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
import { type MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { type NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { type SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
|
||||
export type DefaultRecordActionConfigKeys =
|
||||
| NoSelectionRecordActionKeys
|
||||
| SingleRecordActionKeys
|
||||
| MultipleRecordsActionKeys;
|
||||
@@ -1,44 +0,0 @@
|
||||
import { DASHBOARD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/DashboardActionsConfig';
|
||||
import { DEFAULT_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig';
|
||||
import { WORKFLOW_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkflowActionsConfig';
|
||||
import { WORKFLOW_RUNS_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig';
|
||||
import { WORKFLOW_VERSIONS_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig';
|
||||
import { WORKSPACE_MEMBERS_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkspaceMembersActionsConfig';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const getActionConfig = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem?: ObjectMetadataItem;
|
||||
}): Record<string, ActionConfig> => {
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (objectMetadataItem.nameSingular) {
|
||||
case CoreObjectNameSingular.Dashboard: {
|
||||
return DASHBOARD_ACTIONS_CONFIG;
|
||||
}
|
||||
case CoreObjectNameSingular.Workflow: {
|
||||
return WORKFLOW_ACTIONS_CONFIG;
|
||||
}
|
||||
case CoreObjectNameSingular.WorkflowVersion: {
|
||||
return WORKFLOW_VERSIONS_ACTIONS_CONFIG;
|
||||
}
|
||||
case CoreObjectNameSingular.WorkflowRun: {
|
||||
return WORKFLOW_RUNS_ACTIONS_CONFIG;
|
||||
}
|
||||
case CoreObjectNameSingular.WorkspaceMember: {
|
||||
return WORKSPACE_MEMBERS_ACTIONS_CONFIG;
|
||||
}
|
||||
case CoreObjectNameSingular.Company: {
|
||||
return DEFAULT_RECORD_ACTIONS_CONFIG;
|
||||
}
|
||||
default: {
|
||||
return DEFAULT_RECORD_ACTIONS_CONFIG;
|
||||
}
|
||||
}
|
||||
};
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import { ActionComponent } from '@/action-menu/actions/display/components/ActionComponent';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { getSidePanelActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getSidePanelActionMenuDropdownIdFromActionMenuId';
|
||||
import { OptionsDropdownMenu } from '@/ui/layout/dropdown/components/OptionsDropdownMenu';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const CommandMenuActionMenuDropdown = () => {
|
||||
const { actions } = useContext(ActionMenuContext);
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
ActionMenuComponentInstanceContext,
|
||||
);
|
||||
|
||||
const dropdownId =
|
||||
getSidePanelActionMenuDropdownIdFromActionMenuId(actionMenuId);
|
||||
|
||||
const recordSelectionActions = actions.filter(
|
||||
(action) => action.scope === ActionScope.RecordSelection,
|
||||
);
|
||||
|
||||
const selectableItemIdArray = recordSelectionActions.map(
|
||||
(action) => action.key,
|
||||
);
|
||||
|
||||
return (
|
||||
<OptionsDropdownMenu
|
||||
dropdownId={dropdownId}
|
||||
selectableListId={actionMenuId}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
>
|
||||
{recordSelectionActions.map((action) => (
|
||||
<ActionComponent action={action} key={action.key} />
|
||||
))}
|
||||
</OptionsDropdownMenu>
|
||||
);
|
||||
};
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
import { ACTION_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME } from '@/action-menu/confirmation-modal/constants/ActionMenuConfirmationModalResultBrowserEventName';
|
||||
import { ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID } from '@/action-menu/confirmation-modal/constants/ActionMenuConfirmationModalId';
|
||||
import { actionMenuConfirmationModalConfigState } from '@/action-menu/confirmation-modal/states/actionMenuConfirmationModalState';
|
||||
import {
|
||||
type ActionMenuConfirmationModalResult,
|
||||
type ActionMenuConfirmationModalResultBrowserEventDetail,
|
||||
} from '@/action-menu/confirmation-modal/types/ActionMenuConfirmationModalResultBrowserEventDetail';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const ActionMenuConfirmationModalManager = () => {
|
||||
const actionMenuConfirmationModalConfig = useAtomStateValue(
|
||||
actionMenuConfirmationModalConfigState,
|
||||
);
|
||||
const isModalOpened = useAtomComponentStateValue(
|
||||
isModalOpenedComponentState,
|
||||
ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID,
|
||||
);
|
||||
const setActionMenuConfirmationModalConfig = useSetAtomState(
|
||||
actionMenuConfirmationModalConfigState,
|
||||
);
|
||||
|
||||
const callerId = actionMenuConfirmationModalConfig?.frontComponentId;
|
||||
|
||||
const emitConfirmationResult = (
|
||||
confirmationResult: ActionMenuConfirmationModalResult,
|
||||
) => {
|
||||
if (!callerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent<ActionMenuConfirmationModalResultBrowserEventDetail>(
|
||||
ACTION_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME,
|
||||
{
|
||||
detail: {
|
||||
frontComponentId: callerId,
|
||||
confirmationResult,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
setActionMenuConfirmationModalConfig(null);
|
||||
};
|
||||
|
||||
if (!actionMenuConfirmationModalConfig || !isModalOpened) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
modalInstanceId={ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID}
|
||||
title={actionMenuConfirmationModalConfig.title}
|
||||
subtitle={actionMenuConfirmationModalConfig.subtitle}
|
||||
onConfirmClick={() => emitConfirmationResult('confirm')}
|
||||
onClose={() => emitConfirmationResult('cancel')}
|
||||
confirmButtonText={actionMenuConfirmationModalConfig.confirmButtonText}
|
||||
confirmButtonAccent={
|
||||
actionMenuConfirmationModalConfig.confirmButtonAccent
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
export const ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID =
|
||||
'action-menu-confirmation-modal';
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
export const ACTION_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME =
|
||||
'action-menu-confirmation-modal-result';
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID } from '@/action-menu/confirmation-modal/constants/ActionMenuConfirmationModalId';
|
||||
import {
|
||||
type ActionMenuConfirmationModalConfig,
|
||||
actionMenuConfirmationModalConfigState,
|
||||
} from '@/action-menu/confirmation-modal/states/actionMenuConfirmationModalState';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const useActionMenuConfirmationModal = () => {
|
||||
const store = useStore();
|
||||
const setActionMenuConfirmationModalConfig = useSetAtomState(
|
||||
actionMenuConfirmationModalConfigState,
|
||||
);
|
||||
const { openModal } = useModal();
|
||||
|
||||
const openConfirmationModal = useCallback(
|
||||
(config: ActionMenuConfirmationModalConfig) => {
|
||||
const existingActionMenuConfirmationModalConfig = store.get(
|
||||
actionMenuConfirmationModalConfigState.atom,
|
||||
);
|
||||
const isActionMenuConfirmationModalOpened = store.get(
|
||||
isModalOpenedComponentState.atomFamily({
|
||||
instanceId: ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
existingActionMenuConfirmationModalConfig !== null ||
|
||||
isActionMenuConfirmationModalOpened
|
||||
) {
|
||||
throw new Error(
|
||||
'Action menu confirmation modal is already active for another front component',
|
||||
);
|
||||
}
|
||||
|
||||
setActionMenuConfirmationModalConfig(config);
|
||||
|
||||
openModal(ACTION_MENU_CONFIRMATION_MODAL_INSTANCE_ID);
|
||||
},
|
||||
[store, setActionMenuConfirmationModalConfig, openModal],
|
||||
);
|
||||
|
||||
return { openConfirmationModal };
|
||||
};
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
export type ActionMenuConfirmationModalResult = 'confirm' | 'cancel';
|
||||
|
||||
export type ActionMenuConfirmationModalResultBrowserEventDetail = {
|
||||
frontComponentId: string;
|
||||
confirmationResult: ActionMenuConfirmationModalResult;
|
||||
};
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID = 'action-menu-dropdown';
|
||||
@@ -1,4 +0,0 @@
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const ActionConfigContext = createContext<ActionConfig | null>(null);
|
||||
@@ -1,21 +0,0 @@
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export type ActionMenuContextType = {
|
||||
isInSidePanel: boolean;
|
||||
displayType: 'button' | 'listItem' | 'dropdownItem';
|
||||
actionMenuType:
|
||||
| 'command-menu'
|
||||
| 'show-page-action-menu'
|
||||
| 'index-page-action-menu'
|
||||
| 'index-page-action-menu-dropdown'
|
||||
| 'command-menu-show-page-action-menu-dropdown';
|
||||
actions: ActionConfig[];
|
||||
};
|
||||
|
||||
export const ActionMenuContext = createContext<ActionMenuContextType>({
|
||||
isInSidePanel: false,
|
||||
actionMenuType: 'command-menu',
|
||||
displayType: 'button',
|
||||
actions: [],
|
||||
});
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
import { useRunWorkflowRecordActions } from '@/action-menu/actions/record-actions/run-workflow-actions/hooks/useRunWorkflowRecordActions';
|
||||
import { useRunWorkflowRecordAgnosticActions } from '@/action-menu/actions/record-agnostic-actions/run-workflow-actions/hooks/useRunWorkflowRecordAgnosticActions';
|
||||
import {
|
||||
ActionMenuContext,
|
||||
type ActionMenuContextType,
|
||||
} from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { useRegisteredActions } from '@/action-menu/hooks/useRegisteredActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { useCommandMenuContextApi } from '@/action-menu/hooks/useCommandMenuContextApi';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
export const ActionMenuContextProviderDefault = ({
|
||||
objectMetadataItem,
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
children,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
isInSidePanel: ActionMenuContextType['isInSidePanel'];
|
||||
displayType: ActionMenuContextType['displayType'];
|
||||
actionMenuType: ActionMenuContextType['actionMenuType'];
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const params = useShouldActionBeRegisteredParams({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const shouldBeRegisteredParams = {
|
||||
...params,
|
||||
};
|
||||
|
||||
const actions = useRegisteredActions(shouldBeRegisteredParams);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const isRecordSelection =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length > 0;
|
||||
|
||||
const runWorkflowRecordActions = useRunWorkflowRecordActions({
|
||||
objectMetadataItem,
|
||||
skip: !isRecordSelection,
|
||||
});
|
||||
|
||||
const runWorkflowRecordAgnosticActions =
|
||||
useRunWorkflowRecordAgnosticActions();
|
||||
|
||||
const commandMenuContextApi = useCommandMenuContextApi();
|
||||
|
||||
const commandMenuItemFrontComponentActions =
|
||||
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
|
||||
|
||||
return (
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
actions: [
|
||||
...actions,
|
||||
...runWorkflowRecordActions,
|
||||
...runWorkflowRecordAgnosticActions,
|
||||
...commandMenuItemFrontComponentActions,
|
||||
],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
||||
import { getSidePanelActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getSidePanelActionMenuDropdownIdFromActionMenuId';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useCloseActionMenu = ({
|
||||
closeSidePanelOnShowPageOptionsActionExecution = false,
|
||||
closeSidePanelOnCommandMenuListActionExecution = true,
|
||||
}: {
|
||||
closeSidePanelOnShowPageOptionsActionExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListActionExecution?: boolean;
|
||||
} = {}) => {
|
||||
const { actionMenuType, isInSidePanel } = useContext(ActionMenuContext);
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
ActionMenuComponentInstanceContext,
|
||||
);
|
||||
|
||||
const dropdownId = isInSidePanel
|
||||
? getSidePanelActionMenuDropdownIdFromActionMenuId(actionMenuId)
|
||||
: getActionMenuDropdownIdFromActionMenuId(actionMenuId);
|
||||
|
||||
const closeActionMenu = () => {
|
||||
if (actionMenuType === 'command-menu') {
|
||||
if (
|
||||
isDefined(closeSidePanelOnCommandMenuListActionExecution) &&
|
||||
!closeSidePanelOnCommandMenuListActionExecution
|
||||
) {
|
||||
return;
|
||||
}
|
||||
closeSidePanelMenu();
|
||||
}
|
||||
|
||||
if (
|
||||
actionMenuType === 'index-page-action-menu-dropdown' ||
|
||||
actionMenuType === 'command-menu-show-page-action-menu-dropdown'
|
||||
) {
|
||||
closeDropdown(dropdownId);
|
||||
}
|
||||
|
||||
if (
|
||||
actionMenuType === 'command-menu-show-page-action-menu-dropdown' &&
|
||||
isDefined(closeSidePanelOnShowPageOptionsActionExecution) &&
|
||||
closeSidePanelOnShowPageOptionsActionExecution
|
||||
) {
|
||||
closeSidePanelMenu();
|
||||
}
|
||||
};
|
||||
|
||||
return { closeActionMenu };
|
||||
};
|
||||
@@ -1,90 +0,0 @@
|
||||
import { useRecordAgnosticActions } from '@/action-menu/actions/record-agnostic-actions/hooks/useRecordAgnosticActions';
|
||||
import { useRelatedRecordActions } from '@/action-menu/actions/record-agnostic-actions/hooks/useRelatedRecordActions';
|
||||
import { ActionViewType } from 'twenty-shared/types';
|
||||
import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams';
|
||||
import { getActionConfig } from '@/action-menu/actions/utils/getActionConfig';
|
||||
import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
export const useRegisteredActions = (
|
||||
shouldBeRegisteredParams: ShouldBeRegisteredFunctionParams,
|
||||
) => {
|
||||
const { objectMetadataItem } = shouldBeRegisteredParams;
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useAtomComponentStateValue(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
);
|
||||
|
||||
const contextStoreIsPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
|
||||
const viewType = getActionViewType(
|
||||
contextStoreCurrentViewType,
|
||||
contextStoreTargetedRecordsRule,
|
||||
);
|
||||
|
||||
const recordActionConfig = getActionConfig({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const relatedRecordActionConfig = useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon,
|
||||
startPosition: Object.keys(recordActionConfig).length + 1,
|
||||
});
|
||||
|
||||
const recordAgnosticActionConfig = useRecordAgnosticActions();
|
||||
|
||||
const actionsConfig = {
|
||||
...recordActionConfig,
|
||||
...relatedRecordActionConfig,
|
||||
...recordAgnosticActionConfig,
|
||||
};
|
||||
|
||||
const permissionMap = usePermissionFlagMap();
|
||||
|
||||
const actionsToRegister = Object.values(actionsConfig).filter((action) => {
|
||||
if (contextStoreIsPageInEditMode) {
|
||||
return (
|
||||
isDefined(action.availableOn) &&
|
||||
action.availableOn.includes(ActionViewType.PAGE_EDIT_MODE)
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(viewType)) {
|
||||
return (
|
||||
action.availableOn?.includes(viewType) ||
|
||||
action.availableOn?.includes(ActionViewType.GLOBAL)
|
||||
);
|
||||
}
|
||||
|
||||
return action.availableOn?.includes(ActionViewType.GLOBAL);
|
||||
});
|
||||
|
||||
const actions = actionsToRegister
|
||||
.filter((action) => {
|
||||
if (
|
||||
isDefined(action.requiredPermissionFlag) &&
|
||||
!permissionMap[action.requiredPermissionFlag]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return action.shouldBeRegistered(shouldBeRegisteredParams);
|
||||
})
|
||||
.sort((a, b) => a.position - b.position);
|
||||
|
||||
return actions;
|
||||
};
|
||||
@@ -1,104 +0,0 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import {
|
||||
ActionViewType,
|
||||
CoreObjectNameSingular,
|
||||
AppPath,
|
||||
} from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
IconFileExport,
|
||||
IconHeart,
|
||||
IconTrash,
|
||||
IconUser,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const createMockActionMenuActions = ({
|
||||
deleteMock = () => {},
|
||||
addToFavoritesMock = () => {},
|
||||
exportMock = () => {},
|
||||
}: {
|
||||
deleteMock?: () => void;
|
||||
addToFavoritesMock?: () => void;
|
||||
exportMock?: () => void;
|
||||
}): ActionConfig[] => [
|
||||
{
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
label: msg`Add to favorites`,
|
||||
shortLabel: msg`Add to favorites`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
Icon: IconHeart,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <Action onClick={addToFavoritesMock} />,
|
||||
},
|
||||
{
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 4,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
|
||||
component: <Action onClick={exportMock} />,
|
||||
},
|
||||
{
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.DELETE,
|
||||
label: msg`Delete`,
|
||||
shortLabel: msg`Delete`,
|
||||
position: 7,
|
||||
Icon: IconTrash,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <Action onClick={deleteMock} />,
|
||||
},
|
||||
{
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
label: msg`Go to People`,
|
||||
shortLabel: msg`People`,
|
||||
position: 19,
|
||||
Icon: IconUser,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Person ||
|
||||
viewType === ActionViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Person }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'P'],
|
||||
},
|
||||
];
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { type PositionType } from '@/action-menu/types/PositionType';
|
||||
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
|
||||
|
||||
export const recordIndexActionMenuDropdownPositionComponentState =
|
||||
createAtomComponentState<PositionType>({
|
||||
key: 'recordIndexActionMenuDropdownPositionComponentState',
|
||||
defaultValue: {
|
||||
x: null,
|
||||
y: null,
|
||||
},
|
||||
componentInstanceContext: ActionMenuComponentInstanceContext,
|
||||
});
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
||||
|
||||
describe('getActionMenuDropdownIdFromActionMenuId', () => {
|
||||
it('should return the correct action menu dropdown id', () => {
|
||||
expect(getActionMenuDropdownIdFromActionMenuId('action-menu-id')).toBe(
|
||||
'action-menu-dropdown-action-menu-id',
|
||||
);
|
||||
});
|
||||
});
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
|
||||
|
||||
describe('getActionMenuIdFromRecordIndexId', () => {
|
||||
it('should return the correct action menu id', () => {
|
||||
expect(getActionMenuIdFromRecordIndexId('record-index-id')).toBe(
|
||||
'action-menu-record-index-record-index-id',
|
||||
);
|
||||
});
|
||||
});
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { getSidePanelActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getSidePanelActionMenuDropdownIdFromActionMenuId';
|
||||
|
||||
describe('getSidePanelActionMenuDropdownIdFromActionMenuId', () => {
|
||||
it('should return the side panel action menu dropdown id', () => {
|
||||
expect(
|
||||
getSidePanelActionMenuDropdownIdFromActionMenuId('action-menu-id'),
|
||||
).toBe('side-panel-action-menu-dropdown-action-menu-id');
|
||||
});
|
||||
});
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
export const getActionMenuDropdownIdFromActionMenuId = (
|
||||
actionMenuId: string,
|
||||
) => {
|
||||
return `action-menu-dropdown-${actionMenuId}`;
|
||||
};
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export const getActionMenuIdFromRecordIndexId = (recordIndexId: string) => {
|
||||
return `action-menu-record-index-${recordIndexId}`;
|
||||
};
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
export const getSidePanelActionMenuDropdownIdFromActionMenuId = (
|
||||
actionMenuId: string,
|
||||
) => {
|
||||
return `side-panel-action-menu-dropdown-${actionMenuId}`;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AgentChatProvider } from '@/ai/components/AgentChatProvider';
|
||||
import { ActionMenuConfirmationModalManager } from '@/action-menu/confirmation-modal/components/ActionMenuConfirmationModalManager';
|
||||
import { CommandMenuConfirmationModalManager } from '@/command-menu-item/confirmation-modal/components/CommandMenuConfirmationModalManager';
|
||||
import { ApolloProvider } from '@/apollo/components/ApolloProvider';
|
||||
import { MetadataGater } from '@/metadata-store/components/MetadataGater';
|
||||
import { IsAppMetadataReadyEffect } from '@/metadata-store/effect-components/IsAppMetadataReadyEffect';
|
||||
@@ -72,7 +72,7 @@ export const AppRouterProviders = () => {
|
||||
<PageFavicon />
|
||||
<Outlet />
|
||||
<GlobalFilePreviewModal />
|
||||
<ActionMenuConfirmationModalManager />
|
||||
<CommandMenuConfirmationModalManager />
|
||||
<HeadlessFrontComponentMountRoot />
|
||||
</StrictMode>
|
||||
</DialogManager>
|
||||
|
||||
+8
-6
@@ -1,5 +1,5 @@
|
||||
import { ActionComponent } from '@/action-menu/actions/display/components/ActionComponent';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { styled } from '@linaria/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useContext } from 'react';
|
||||
@@ -11,10 +11,12 @@ const StyledActionContainer = styled(motion.div)`
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const PageHeaderActionMenuButtons = () => {
|
||||
export const PageHeaderCommandMenuButtons = () => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const { actions } = useContext(ActionMenuContext);
|
||||
const pinnedActions = actions.filter((entry) => entry.isPinned).toReversed();
|
||||
const { commandMenuItems } = useContext(CommandMenuContext);
|
||||
const pinnedActions = commandMenuItems
|
||||
.filter((entry) => entry.isPinned)
|
||||
.toReversed();
|
||||
|
||||
const actionsWithPositionForAnimation = pinnedActions.map(
|
||||
(action, index) => ({
|
||||
@@ -37,7 +39,7 @@ export const PageHeaderActionMenuButtons = () => {
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
>
|
||||
<ActionComponent action={action} />
|
||||
<CommandMenuItemComponent action={action} />
|
||||
</StyledActionContainer>
|
||||
))}
|
||||
</>
|
||||
+12
-12
@@ -1,12 +1,12 @@
|
||||
import { PageHeaderActionMenuButtons } from '@/action-menu/components/PageHeaderActionMenuButtons';
|
||||
import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { PageHeaderCommandMenuButtons } from '@/command-menu-item/components/PageHeaderCommandMenuButtons';
|
||||
import { RecordIndexCommandMenuDropdown } from '@/command-menu-item/components/RecordIndexCommandMenuDropdown';
|
||||
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
export const RecordIndexActionMenu = () => {
|
||||
export const RecordIndexCommandMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
@@ -18,20 +18,20 @@ export const RecordIndexActionMenu = () => {
|
||||
<>
|
||||
{contextStoreCurrentObjectMetadataItemId && (
|
||||
<>
|
||||
<ActionMenuContextProvider
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={false}
|
||||
displayType="button"
|
||||
actionMenuType="index-page-action-menu"
|
||||
containerType="index-page-header"
|
||||
>
|
||||
{!isMobile && <PageHeaderActionMenuButtons />}
|
||||
</ActionMenuContextProvider>
|
||||
<ActionMenuContextProvider
|
||||
{!isMobile && <PageHeaderCommandMenuButtons />}
|
||||
</CommandMenuContextProvider>
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={false}
|
||||
displayType="dropdownItem"
|
||||
actionMenuType="index-page-action-menu-dropdown"
|
||||
containerType="index-page-dropdown"
|
||||
>
|
||||
<RecordIndexActionMenuDropdown />
|
||||
</ActionMenuContextProvider>
|
||||
<RecordIndexCommandMenuDropdown />
|
||||
</CommandMenuContextProvider>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
+22
-22
@@ -1,11 +1,11 @@
|
||||
import { ActionComponent } from '@/action-menu/actions/display/components/ActionComponent';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
||||
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { COMMAND_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/command-menu-item/constants/CommandMenuDropdownClickOutsideId';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { recordIndexCommandMenuDropdownPositionComponentState } from '@/command-menu-item/states/recordIndexCommandMenuDropdownPositionComponentState';
|
||||
import { getCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getCommandMenuDropdownIdFromCommandMenuId';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
@@ -32,25 +32,25 @@ const StyledDropdownMenuContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const RecordIndexActionMenuDropdown = () => {
|
||||
export const RecordIndexCommandMenuDropdown = () => {
|
||||
const { t } = useLingui();
|
||||
const { actions } = useContext(ActionMenuContext);
|
||||
const { commandMenuItems } = useContext(CommandMenuContext);
|
||||
|
||||
const recordIndexActions = actions.filter(
|
||||
const recordIndexActions = commandMenuItems.filter(
|
||||
(action) =>
|
||||
action.type === ActionType.Standard &&
|
||||
action.scope === ActionScope.RecordSelection,
|
||||
action.type === CommandMenuItemType.Standard &&
|
||||
action.scope === CommandMenuItemScope.RecordSelection,
|
||||
);
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
ActionMenuComponentInstanceContext,
|
||||
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
CommandMenuComponentInstanceContext,
|
||||
);
|
||||
|
||||
const dropdownId = getActionMenuDropdownIdFromActionMenuId(actionMenuId);
|
||||
const dropdownId = getCommandMenuDropdownIdFromCommandMenuId(commandMenuId);
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const recordIndexActionMenuDropdownPosition = useAtomComponentStateValue(
|
||||
recordIndexActionMenuDropdownPositionComponentState,
|
||||
const recordIndexCommandMenuDropdownPosition = useAtomComponentStateValue(
|
||||
recordIndexCommandMenuDropdownPositionComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
@@ -73,13 +73,13 @@ export const RecordIndexActionMenuDropdown = () => {
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownStrategy="absolute"
|
||||
dropdownOffset={{
|
||||
x: recordIndexActionMenuDropdownPosition.x ?? 0,
|
||||
y: recordIndexActionMenuDropdownPosition.y ?? 0,
|
||||
x: recordIndexCommandMenuDropdownPosition.x ?? 0,
|
||||
y: recordIndexCommandMenuDropdownPosition.y ?? 0,
|
||||
}}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuContainer
|
||||
data-click-outside-id={ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID}
|
||||
data-click-outside-id={COMMAND_MENU_DROPDOWN_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
@@ -88,7 +88,7 @@ export const RecordIndexActionMenuDropdown = () => {
|
||||
selectableListInstanceId={dropdownId}
|
||||
>
|
||||
{recordIndexActions.map((action) => (
|
||||
<ActionComponent action={action} key={action.key} />
|
||||
<CommandMenuItemComponent action={action} key={action.key} />
|
||||
))}
|
||||
<SelectableListItem
|
||||
itemId="more-actions"
|
||||
+7
-7
@@ -1,9 +1,9 @@
|
||||
import { CommandMenuActionMenuDropdown } from '@/action-menu/components/CommandMenuActionMenuDropdown';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { RecordPageSidePanelCommandMenuDropdown } from '@/command-menu-item/components/RecordPageSidePanelCommandMenuDropdown';
|
||||
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
export const RecordShowSidePanelActionMenu = () => {
|
||||
export const RecordPageSidePanelCommandMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
@@ -11,13 +11,13 @@ export const RecordShowSidePanelActionMenu = () => {
|
||||
return (
|
||||
<>
|
||||
{contextStoreCurrentObjectMetadataItemId && (
|
||||
<ActionMenuContextProvider
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={true}
|
||||
displayType="dropdownItem"
|
||||
actionMenuType="command-menu-show-page-action-menu-dropdown"
|
||||
containerType="command-menu-show-page-dropdown"
|
||||
>
|
||||
<CommandMenuActionMenuDropdown />
|
||||
</ActionMenuContextProvider>
|
||||
<RecordPageSidePanelCommandMenuDropdown />
|
||||
</CommandMenuContextProvider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { getSidePanelCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getSidePanelCommandMenuDropdownIdFromCommandMenuId';
|
||||
import { OptionsDropdownMenu } from '@/ui/layout/dropdown/components/OptionsDropdownMenu';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const RecordPageSidePanelCommandMenuDropdown = () => {
|
||||
const { commandMenuItems } = useContext(CommandMenuContext);
|
||||
|
||||
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
CommandMenuComponentInstanceContext,
|
||||
);
|
||||
|
||||
const dropdownId =
|
||||
getSidePanelCommandMenuDropdownIdFromCommandMenuId(commandMenuId);
|
||||
|
||||
const recordSelectionActions = commandMenuItems.filter(
|
||||
(action) => action.scope === CommandMenuItemScope.RecordSelection,
|
||||
);
|
||||
|
||||
const selectableItemIdArray = recordSelectionActions.map(
|
||||
(action) => action.key,
|
||||
);
|
||||
|
||||
return (
|
||||
<OptionsDropdownMenu
|
||||
dropdownId={dropdownId}
|
||||
selectableListId={commandMenuId}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
>
|
||||
{recordSelectionActions.map((action) => (
|
||||
<CommandMenuItemComponent action={action} key={action.key} />
|
||||
))}
|
||||
</OptionsDropdownMenu>
|
||||
);
|
||||
};
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
import { PageHeaderActionMenuButtons } from '@/action-menu/components/PageHeaderActionMenuButtons';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { PageHeaderCommandMenuButtons } from '@/command-menu-item/components/PageHeaderCommandMenuButtons';
|
||||
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
export const RecordShowActionMenu = () => {
|
||||
export const RecordShowCommandMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
@@ -26,13 +26,13 @@ export const RecordShowActionMenu = () => {
|
||||
return (
|
||||
<>
|
||||
{hasSelectedRecord && contextStoreCurrentObjectMetadataItemId && (
|
||||
<ActionMenuContextProvider
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={false}
|
||||
displayType="button"
|
||||
actionMenuType="show-page-action-menu"
|
||||
containerType="show-page-header"
|
||||
>
|
||||
{!isMobile && <PageHeaderActionMenuButtons />}
|
||||
</ActionMenuContextProvider>
|
||||
{!isMobile && <PageHeaderCommandMenuButtons />}
|
||||
</CommandMenuContextProvider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { getSidePanelActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getSidePanelActionMenuDropdownIdFromActionMenuId';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { getSidePanelCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getSidePanelCommandMenuDropdownIdFromCommandMenuId';
|
||||
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
|
||||
@@ -75,8 +75,8 @@ export const RecordShowSidePanelOpenRecordButton = ({
|
||||
|
||||
const navigate = useNavigateApp();
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
ActionMenuComponentInstanceContext,
|
||||
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
CommandMenuComponentInstanceContext,
|
||||
);
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
@@ -109,12 +109,12 @@ export const RecordShowSidePanelOpenRecordButton = ({
|
||||
});
|
||||
|
||||
closeDropdown(
|
||||
getSidePanelActionMenuDropdownIdFromActionMenuId(actionMenuId),
|
||||
getSidePanelCommandMenuDropdownIdFromCommandMenuId(commandMenuId),
|
||||
);
|
||||
|
||||
closeSidePanelMenu();
|
||||
}, [
|
||||
actionMenuId,
|
||||
commandMenuId,
|
||||
activeTabId,
|
||||
closeSidePanelMenu,
|
||||
closeDropdown,
|
||||
+21
-21
@@ -3,11 +3,11 @@ import { Provider as JotaiProvider } from 'jotai';
|
||||
import * as test from 'storybook/test';
|
||||
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
||||
|
||||
import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||
import { RecordIndexCommandMenuDropdown } from '@/command-menu-item/components/RecordIndexCommandMenuDropdown';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { recordIndexCommandMenuDropdownPositionComponentState } from '@/command-menu-item/states/recordIndexCommandMenuDropdownPositionComponentState';
|
||||
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
@@ -18,35 +18,35 @@ const deleteMock = test.fn();
|
||||
const addToFavoritesMock = test.fn();
|
||||
const exportMock = test.fn();
|
||||
|
||||
const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
|
||||
title: 'Modules/ActionMenu/RecordIndexActionMenuDropdown',
|
||||
component: RecordIndexActionMenuDropdown,
|
||||
const meta: Meta<typeof RecordIndexCommandMenuDropdown> = {
|
||||
title: 'Modules/CommandMenu/RecordIndexCommandMenuDropdown',
|
||||
component: RecordIndexCommandMenuDropdown,
|
||||
decorators: [
|
||||
(Story) => {
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: 'action-menu-dropdown-story-action-menu',
|
||||
instanceId: 'command-menu-dropdown-story-command-menu',
|
||||
}),
|
||||
true,
|
||||
);
|
||||
jotaiStore.set(
|
||||
recordIndexActionMenuDropdownPositionComponentState.atomFamily({
|
||||
instanceId: 'action-menu-dropdown-story',
|
||||
recordIndexCommandMenuDropdownPositionComponentState.atomFamily({
|
||||
instanceId: 'command-menu-dropdown-story',
|
||||
}),
|
||||
{ x: 10, y: 10 },
|
||||
);
|
||||
|
||||
return (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
<CommandMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-command-menu' }}
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: true,
|
||||
displayType: 'dropdownItem',
|
||||
actionMenuType: 'index-page-action-menu-dropdown',
|
||||
actions: createMockActionMenuActions({
|
||||
containerType: 'index-page-dropdown',
|
||||
commandMenuItems: createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
exportMock,
|
||||
@@ -54,8 +54,8 @@ const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
</CommandMenuComponentInstanceContext.Provider>
|
||||
</JotaiProvider>
|
||||
);
|
||||
},
|
||||
@@ -66,17 +66,17 @@ const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof RecordIndexActionMenuDropdown>;
|
||||
type Story = StoryObj<typeof RecordIndexCommandMenuDropdown>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story',
|
||||
commandMenuId: 'story',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithInteractions: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story',
|
||||
commandMenuId: 'story',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement.ownerDocument.body);
|
||||
+18
-18
@@ -3,10 +3,10 @@ import { Provider as JotaiProvider } from 'jotai';
|
||||
import * as test from 'storybook/test';
|
||||
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
||||
|
||||
import { CommandMenuActionMenuDropdown } from '@/action-menu/components/CommandMenuActionMenuDropdown';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { RecordPageSidePanelCommandMenuDropdown } from '@/command-menu-item/components/RecordPageSidePanelCommandMenuDropdown';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
@@ -19,9 +19,9 @@ const deleteMock = test.fn();
|
||||
const addToFavoritesMock = test.fn();
|
||||
const exportMock = test.fn();
|
||||
|
||||
const meta: Meta<typeof CommandMenuActionMenuDropdown> = {
|
||||
title: 'Modules/ActionMenu/CommandMenuActionMenuDropdown',
|
||||
component: CommandMenuActionMenuDropdown,
|
||||
const meta: Meta<typeof RecordPageSidePanelCommandMenuDropdown> = {
|
||||
title: 'Modules/CommandMenu/RecordPageSidePanelCommandMenuDropdown',
|
||||
component: RecordPageSidePanelCommandMenuDropdown,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
@@ -35,15 +35,15 @@ const meta: Meta<typeof CommandMenuActionMenuDropdown> = {
|
||||
}}
|
||||
contextStoreNumberOfSelectedRecords={1}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
<CommandMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-command-menu' }}
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: true,
|
||||
displayType: 'dropdownItem',
|
||||
actionMenuType: 'command-menu-show-page-action-menu-dropdown',
|
||||
actions: createMockActionMenuActions({
|
||||
containerType: 'command-menu-show-page-dropdown',
|
||||
commandMenuItems: createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
exportMock,
|
||||
@@ -51,8 +51,8 @@ const meta: Meta<typeof CommandMenuActionMenuDropdown> = {
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
</CommandMenuComponentInstanceContext.Provider>
|
||||
</JestContextStoreSetter>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JotaiProvider>
|
||||
@@ -64,23 +64,23 @@ const meta: Meta<typeof CommandMenuActionMenuDropdown> = {
|
||||
RouterDecorator,
|
||||
],
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
commandMenuId: 'story-command-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CommandMenuActionMenuDropdown>;
|
||||
type Story = StoryObj<typeof RecordPageSidePanelCommandMenuDropdown>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
commandMenuId: 'story-command-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithButtonClicks: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
commandMenuId: 'story-command-menu',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement.ownerDocument.body);
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalResultBrowserEventName';
|
||||
import { COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalId';
|
||||
import { commandMenuItemConfirmationModalConfigState } from '@/command-menu-item/confirmation-modal/states/commandMenuItemConfirmationModalState';
|
||||
import {
|
||||
type CommandMenuConfirmationModalResult,
|
||||
type CommandMenuConfirmationModalResultBrowserEventDetail,
|
||||
} from '@/command-menu-item/confirmation-modal/types/CommandMenuConfirmationModalResultBrowserEventDetail';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const CommandMenuConfirmationModalManager = () => {
|
||||
const commandMenuItemConfirmationModalConfig = useAtomStateValue(
|
||||
commandMenuItemConfirmationModalConfigState,
|
||||
);
|
||||
const isModalOpened = useAtomComponentStateValue(
|
||||
isModalOpenedComponentState,
|
||||
COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID,
|
||||
);
|
||||
const setCommandMenuItemConfirmationModalConfig = useSetAtomState(
|
||||
commandMenuItemConfirmationModalConfigState,
|
||||
);
|
||||
|
||||
const callerId = commandMenuItemConfirmationModalConfig?.frontComponentId;
|
||||
|
||||
const emitConfirmationResult = (
|
||||
confirmationResult: CommandMenuConfirmationModalResult,
|
||||
) => {
|
||||
if (!callerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent<CommandMenuConfirmationModalResultBrowserEventDetail>(
|
||||
COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME,
|
||||
{
|
||||
detail: {
|
||||
frontComponentId: callerId,
|
||||
confirmationResult,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
setCommandMenuItemConfirmationModalConfig(null);
|
||||
};
|
||||
|
||||
if (!commandMenuItemConfirmationModalConfig || !isModalOpened) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
modalInstanceId={COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID}
|
||||
title={commandMenuItemConfirmationModalConfig.title}
|
||||
subtitle={commandMenuItemConfirmationModalConfig.subtitle}
|
||||
onConfirmClick={() => emitConfirmationResult('confirm')}
|
||||
onClose={() => emitConfirmationResult('cancel')}
|
||||
confirmButtonText={
|
||||
commandMenuItemConfirmationModalConfig.confirmButtonText
|
||||
}
|
||||
confirmButtonAccent={
|
||||
commandMenuItemConfirmationModalConfig.confirmButtonAccent
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID =
|
||||
'command-menu-item-confirmation-modal';
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME =
|
||||
'command-menu-item-confirmation-modal-result';
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalId';
|
||||
import {
|
||||
type CommandMenuItemConfirmationModalConfig,
|
||||
commandMenuItemConfirmationModalConfigState,
|
||||
} from '@/command-menu-item/confirmation-modal/states/commandMenuItemConfirmationModalState';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const useCommandMenuConfirmationModal = () => {
|
||||
const store = useStore();
|
||||
const setCommandMenuItemConfirmationModalConfig = useSetAtomState(
|
||||
commandMenuItemConfirmationModalConfigState,
|
||||
);
|
||||
const { openModal } = useModal();
|
||||
|
||||
const openConfirmationModal = useCallback(
|
||||
(config: CommandMenuItemConfirmationModalConfig) => {
|
||||
const existingCommandMenuItemConfirmationModalConfig = store.get(
|
||||
commandMenuItemConfirmationModalConfigState.atom,
|
||||
);
|
||||
const isCommandMenuItemConfirmationModalOpened = store.get(
|
||||
isModalOpenedComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
existingCommandMenuItemConfirmationModalConfig !== null ||
|
||||
isCommandMenuItemConfirmationModalOpened
|
||||
) {
|
||||
throw new Error(
|
||||
'Command menu item confirmation modal is already active for another front component',
|
||||
);
|
||||
}
|
||||
|
||||
setCommandMenuItemConfirmationModalConfig(config);
|
||||
|
||||
openModal(COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID);
|
||||
},
|
||||
[store, setCommandMenuItemConfirmationModalConfig, openModal],
|
||||
);
|
||||
|
||||
return { openConfirmationModal };
|
||||
};
|
||||
+4
-4
@@ -3,7 +3,7 @@ import { type ReactNode } from 'react';
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
import { type ButtonAccent } from 'twenty-ui/input';
|
||||
|
||||
export type ActionMenuConfirmationModalConfig = {
|
||||
export type CommandMenuItemConfirmationModalConfig = {
|
||||
frontComponentId: string;
|
||||
title: string;
|
||||
subtitle: ReactNode;
|
||||
@@ -11,8 +11,8 @@ export type ActionMenuConfirmationModalConfig = {
|
||||
confirmButtonAccent?: ButtonAccent;
|
||||
};
|
||||
|
||||
export const actionMenuConfirmationModalConfigState =
|
||||
createAtomState<ActionMenuConfirmationModalConfig | null>({
|
||||
key: 'actionMenuConfirmationModalConfigState',
|
||||
export const commandMenuItemConfirmationModalConfigState =
|
||||
createAtomState<CommandMenuItemConfirmationModalConfig | null>({
|
||||
key: 'commandMenuItemConfirmationModalConfigState',
|
||||
defaultValue: null,
|
||||
});
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export type CommandMenuConfirmationModalResult = 'confirm' | 'cancel';
|
||||
|
||||
export type CommandMenuConfirmationModalResultBrowserEventDetail = {
|
||||
frontComponentId: string;
|
||||
confirmationResult: CommandMenuConfirmationModalResult;
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const COMMAND_MENU_DROPDOWN_CLICK_OUTSIDE_ID = 'command-menu-dropdown';
|
||||
@@ -0,0 +1,6 @@
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const CommandConfigContext = createContext<CommandMenuItemConfig | null>(
|
||||
null,
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { type CommandMenuItemContainerType } from '@/command-menu-item/types/CommandMenuItemContainerType';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export type CommandMenuContextType = {
|
||||
isInSidePanel: boolean;
|
||||
displayType: 'button' | 'listItem' | 'dropdownItem';
|
||||
containerType: CommandMenuItemContainerType;
|
||||
commandMenuItems: CommandMenuItemConfig[];
|
||||
};
|
||||
|
||||
export const CommandMenuContext = createContext<CommandMenuContextType>({
|
||||
isInSidePanel: false,
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'button',
|
||||
commandMenuItems: [],
|
||||
});
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
import { type ActionMenuContextType } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { ActionMenuContextProviderDefault } from '@/action-menu/contexts/ActionMenuContextProviderDefault';
|
||||
import { ActionMenuContextProviderWorkflowObjects } from '@/action-menu/contexts/ActionMenuContextProviderWorkflowObjects';
|
||||
import { type CommandMenuContextType } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { CommandMenuContextProviderDefault } from '@/command-menu-item/contexts/CommandMenuContextProviderDefault';
|
||||
import { CommandMenuContextProviderWorkflowObjects } from '@/command-menu-item/contexts/CommandMenuContextProviderWorkflowObjects';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
@@ -9,13 +9,13 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ActionMenuContextProvider = ({
|
||||
export const CommandMenuContextProvider = ({
|
||||
children,
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
containerType,
|
||||
objectMetadataItemOverride,
|
||||
}: Omit<ActionMenuContextType, 'actions'> & {
|
||||
}: Omit<CommandMenuContextType, 'commandMenuItems'> & {
|
||||
children: React.ReactNode;
|
||||
objectMetadataItemOverride?: ObjectMetadataItem;
|
||||
}) => {
|
||||
@@ -45,25 +45,25 @@ export const ActionMenuContextProvider = ({
|
||||
|
||||
if (isWorkflowObject) {
|
||||
return (
|
||||
<ActionMenuContextProviderWorkflowObjects
|
||||
<CommandMenuContextProviderWorkflowObjects
|
||||
isInSidePanel={isInSidePanel}
|
||||
displayType={displayType}
|
||||
actionMenuType={actionMenuType}
|
||||
containerType={containerType}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContextProviderWorkflowObjects>
|
||||
</CommandMenuContextProviderWorkflowObjects>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionMenuContextProviderDefault
|
||||
<CommandMenuContextProviderDefault
|
||||
isInSidePanel={isInSidePanel}
|
||||
displayType={displayType}
|
||||
actionMenuType={actionMenuType}
|
||||
containerType={containerType}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContextProviderDefault>
|
||||
</CommandMenuContextProviderDefault>
|
||||
);
|
||||
};
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { useRunWorkflowRecordCommands } from '@/command-menu-item/record/workflow/hooks/useRunWorkflowRecordCommands';
|
||||
import { useRunWorkflowRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/workflow/hooks/useRunWorkflowRecordAgnosticCommands';
|
||||
import {
|
||||
CommandMenuContext,
|
||||
type CommandMenuContextType,
|
||||
} from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { useRegisteredCommandMenuItems } from '@/command-menu-item/hooks/useRegisteredCommandMenuItems';
|
||||
import { useShouldCommandMenuItemBeRegisteredParams } from '@/command-menu-item/hooks/useShouldCommandMenuItemBeRegisteredParams';
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
export const CommandMenuContextProviderDefault = ({
|
||||
objectMetadataItem,
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
containerType,
|
||||
children,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
isInSidePanel: CommandMenuContextType['isInSidePanel'];
|
||||
displayType: CommandMenuContextType['displayType'];
|
||||
containerType: CommandMenuContextType['containerType'];
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const params = useShouldCommandMenuItemBeRegisteredParams({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const shouldBeRegisteredParams = {
|
||||
...params,
|
||||
};
|
||||
|
||||
const commandMenuItems = useRegisteredCommandMenuItems(
|
||||
shouldBeRegisteredParams,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const isRecordSelection =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length > 0;
|
||||
|
||||
const runWorkflowRecordCommands = useRunWorkflowRecordCommands({
|
||||
objectMetadataItem,
|
||||
skip: !isRecordSelection,
|
||||
});
|
||||
|
||||
const runWorkflowRecordAgnosticCommands =
|
||||
useRunWorkflowRecordAgnosticCommands();
|
||||
|
||||
const commandMenuContextApi = useCommandMenuContextApi();
|
||||
|
||||
const commandMenuItemFrontComponentActions =
|
||||
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
|
||||
|
||||
return (
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
containerType,
|
||||
commandMenuItems: [
|
||||
...commandMenuItems,
|
||||
...runWorkflowRecordCommands,
|
||||
...runWorkflowRecordAgnosticCommands,
|
||||
...commandMenuItemFrontComponentActions,
|
||||
],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CommandMenuContext.Provider>
|
||||
);
|
||||
};
|
||||
+50
-46
@@ -1,11 +1,11 @@
|
||||
import { useRunWorkflowRecordAgnosticActions } from '@/action-menu/actions/record-agnostic-actions/run-workflow-actions/hooks/useRunWorkflowRecordAgnosticActions';
|
||||
import { useRunWorkflowRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/workflow/hooks/useRunWorkflowRecordAgnosticCommands';
|
||||
import {
|
||||
ActionMenuContext,
|
||||
type ActionMenuContextType,
|
||||
} from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { useRegisteredActions } from '@/action-menu/hooks/useRegisteredActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { useCommandMenuContextApi } from '@/action-menu/hooks/useCommandMenuContextApi';
|
||||
CommandMenuContext,
|
||||
type CommandMenuContextType,
|
||||
} from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { useRegisteredCommandMenuItems } from '@/command-menu-item/hooks/useRegisteredCommandMenuItems';
|
||||
import { useShouldCommandMenuItemBeRegisteredParams } from '@/command-menu-item/hooks/useShouldCommandMenuItemBeRegisteredParams';
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
@@ -16,25 +16,25 @@ import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithC
|
||||
import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ActionMenuContextProviderWorkflowObjectsProps = {
|
||||
type CommandMenuContextProviderWorkflowObjectsProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
isInSidePanel: ActionMenuContextType['isInSidePanel'];
|
||||
displayType: ActionMenuContextType['displayType'];
|
||||
actionMenuType: ActionMenuContextType['actionMenuType'];
|
||||
isInSidePanel: CommandMenuContextType['isInSidePanel'];
|
||||
displayType: CommandMenuContextType['displayType'];
|
||||
containerType: CommandMenuContextType['containerType'];
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const ActionMenuContextProviderWorkflowObjectsContent = ({
|
||||
const CommandMenuContextProviderWorkflowObjectsContent = ({
|
||||
objectMetadataItem,
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
containerType,
|
||||
children,
|
||||
selectedRecordId,
|
||||
}: ActionMenuContextProviderWorkflowObjectsProps & {
|
||||
}: CommandMenuContextProviderWorkflowObjectsProps & {
|
||||
selectedRecordId: string;
|
||||
}) => {
|
||||
const params = useShouldActionBeRegisteredParams({
|
||||
const params = useShouldCommandMenuItemBeRegisteredParams({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
@@ -46,10 +46,12 @@ const ActionMenuContextProviderWorkflowObjectsContent = ({
|
||||
workflowWithCurrentVersion,
|
||||
};
|
||||
|
||||
const actions = useRegisteredActions(shouldBeRegisteredParams);
|
||||
const commandMenuItems = useRegisteredCommandMenuItems(
|
||||
shouldBeRegisteredParams,
|
||||
);
|
||||
|
||||
const runWorkflowRecordAgnosticActions =
|
||||
useRunWorkflowRecordAgnosticActions();
|
||||
const runWorkflowRecordAgnosticCommands =
|
||||
useRunWorkflowRecordAgnosticCommands();
|
||||
|
||||
const commandMenuContextApi = useCommandMenuContextApi();
|
||||
|
||||
@@ -57,33 +59,33 @@ const ActionMenuContextProviderWorkflowObjectsContent = ({
|
||||
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
|
||||
|
||||
return (
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
actions: [
|
||||
...actions,
|
||||
...runWorkflowRecordAgnosticActions,
|
||||
containerType,
|
||||
commandMenuItems: [
|
||||
...commandMenuItems,
|
||||
...runWorkflowRecordAgnosticCommands,
|
||||
...commandMenuItemFrontComponentActions,
|
||||
],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const ActionMenuContextProviderWorkflowObjectsWithoutWorkflow = ({
|
||||
const CommandMenuContextProviderWorkflowObjectsWithoutWorkflow = ({
|
||||
objectMetadataItem,
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
containerType,
|
||||
children,
|
||||
}: ActionMenuContextProviderWorkflowObjectsProps & {
|
||||
}: CommandMenuContextProviderWorkflowObjectsProps & {
|
||||
workflowWithCurrentVersion: WorkflowWithCurrentVersion | undefined;
|
||||
}) => {
|
||||
const params = useShouldActionBeRegisteredParams({
|
||||
const params = useShouldCommandMenuItemBeRegisteredParams({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
@@ -92,10 +94,12 @@ const ActionMenuContextProviderWorkflowObjectsWithoutWorkflow = ({
|
||||
workflowWithCurrentVersion: undefined,
|
||||
};
|
||||
|
||||
const actions = useRegisteredActions(shouldBeRegisteredParams);
|
||||
const commandMenuItems = useRegisteredCommandMenuItems(
|
||||
shouldBeRegisteredParams,
|
||||
);
|
||||
|
||||
const runWorkflowRecordAgnosticActions =
|
||||
useRunWorkflowRecordAgnosticActions();
|
||||
const runWorkflowRecordAgnosticCommands =
|
||||
useRunWorkflowRecordAgnosticCommands();
|
||||
|
||||
const commandMenuContextApi = useCommandMenuContextApi();
|
||||
|
||||
@@ -103,30 +107,30 @@ const ActionMenuContextProviderWorkflowObjectsWithoutWorkflow = ({
|
||||
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
|
||||
|
||||
return (
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
actions: [
|
||||
...actions,
|
||||
...runWorkflowRecordAgnosticActions,
|
||||
containerType,
|
||||
commandMenuItems: [
|
||||
...commandMenuItems,
|
||||
...runWorkflowRecordAgnosticCommands,
|
||||
...commandMenuItemFrontComponentActions,
|
||||
],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const ActionMenuContextProviderWorkflowObjects = ({
|
||||
export const CommandMenuContextProviderWorkflowObjects = ({
|
||||
objectMetadataItem,
|
||||
isInSidePanel,
|
||||
displayType,
|
||||
actionMenuType,
|
||||
containerType,
|
||||
children,
|
||||
}: ActionMenuContextProviderWorkflowObjectsProps) => {
|
||||
}: CommandMenuContextProviderWorkflowObjectsProps) => {
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
@@ -143,27 +147,27 @@ export const ActionMenuContextProviderWorkflowObjects = ({
|
||||
|
||||
if (isDefined(selectedRecord?.id)) {
|
||||
return (
|
||||
<ActionMenuContextProviderWorkflowObjectsContent
|
||||
<CommandMenuContextProviderWorkflowObjectsContent
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
isInSidePanel={isInSidePanel}
|
||||
displayType={displayType}
|
||||
actionMenuType={actionMenuType}
|
||||
containerType={containerType}
|
||||
selectedRecordId={selectedRecord.id}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContextProviderWorkflowObjectsContent>
|
||||
</CommandMenuContextProviderWorkflowObjectsContent>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionMenuContextProviderWorkflowObjectsWithoutWorkflow
|
||||
<CommandMenuContextProviderWorkflowObjectsWithoutWorkflow
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
isInSidePanel={isInSidePanel}
|
||||
displayType={displayType}
|
||||
actionMenuType={actionMenuType}
|
||||
containerType={containerType}
|
||||
workflowWithCurrentVersion={undefined}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuContextProviderWorkflowObjectsWithoutWorkflow>
|
||||
</CommandMenuContextProviderWorkflowObjectsWithoutWorkflow>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const Command = ({
|
||||
onClick,
|
||||
closeSidePanelOnShowPageOptionsExecution = false,
|
||||
closeSidePanelOnCommandMenuListExecution = true,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
closeSidePanelOnShowPageOptionsExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListExecution?: boolean;
|
||||
}) => {
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
|
||||
const { closeCommandMenu } = useCloseCommandMenu({
|
||||
closeSidePanelOnShowPageOptionsExecution,
|
||||
closeSidePanelOnCommandMenuListExecution,
|
||||
});
|
||||
|
||||
if (!commandMenuItemConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
closeCommandMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <CommandMenuItemDisplay onClick={handleClick} />;
|
||||
};
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
import { type ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { type CommandMenuItemDisplayProps } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
@@ -9,12 +9,12 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
export const ActionDropdownItem = ({
|
||||
export const CommandDropdownItem = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
action: CommandMenuItemDisplayProps;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
@@ -44,7 +44,7 @@ export const ActionDropdownItem = ({
|
||||
key={action.key}
|
||||
LeftIcon={action.Icon}
|
||||
onClick={handleClick}
|
||||
text={getActionLabel(action.label)}
|
||||
text={getCommandMenuItemLabel(action.label)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { type PathParam } from 'react-router-dom';
|
||||
import { type AppPath } from 'twenty-shared/types';
|
||||
import { getAppPath } from 'twenty-shared/utils';
|
||||
|
||||
export const CommandLink = <T extends AppPath>({
|
||||
to,
|
||||
params,
|
||||
queryParams,
|
||||
}: {
|
||||
to: T;
|
||||
params?: { [key in PathParam<T>]: string | null };
|
||||
queryParams?: Record<string, any>;
|
||||
}) => {
|
||||
const { closeCommandMenu } = useCloseCommandMenu();
|
||||
|
||||
const path = getAppPath(to, params, queryParams);
|
||||
|
||||
return <CommandMenuItemDisplay onClick={closeCommandMenu} to={path} />;
|
||||
};
|
||||
+6
-6
@@ -1,18 +1,18 @@
|
||||
import { type ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { type CommandMenuItemDisplayProps } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Loader } from 'twenty-ui/feedback';
|
||||
|
||||
export const ActionListItem = ({
|
||||
export const CommandListItem = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
disabled = false,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
action: CommandMenuItemDisplayProps;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
disabled?: boolean;
|
||||
@@ -35,8 +35,8 @@ export const ActionListItem = ({
|
||||
<CommandMenuItem
|
||||
id={action.key}
|
||||
Icon={action.Icon}
|
||||
label={getActionLabel(action.label)}
|
||||
description={getActionLabel(action.description ?? '')}
|
||||
label={getCommandMenuItemLabel(action.label)}
|
||||
description={getCommandMenuItemLabel(action.description ?? '')}
|
||||
to={to}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
hotKeys={action.hotKeys}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { CommandMenuButton } from '@/command-menu/components/CommandMenuButton';
|
||||
import { type CommandMenuItemDisplayProps } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
|
||||
export const CommandMenuItemButton = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: CommandMenuItemDisplayProps;
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
return <CommandMenuButton command={action} to={to} onClick={onClick} />;
|
||||
};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
|
||||
export const CommandMenuItemComponent = ({
|
||||
action,
|
||||
}: {
|
||||
action: CommandMenuItemConfig;
|
||||
}) => {
|
||||
return (
|
||||
<CommandConfigContext.Provider value={action}>
|
||||
{action.component}
|
||||
</CommandConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
+12
-12
@@ -1,15 +1,15 @@
|
||||
import { ActionButton } from '@/action-menu/actions/display/components/ActionButton';
|
||||
import { ActionDropdownItem } from '@/action-menu/actions/display/components/ActionDropdownItem';
|
||||
import { ActionListItem } from '@/action-menu/actions/display/components/ActionListItem';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { CommandMenuItemButton } from '@/command-menu-item/display/components/CommandMenuItemButton';
|
||||
import { CommandDropdownItem } from '@/command-menu-item/display/components/CommandDropdownItem';
|
||||
import { CommandListItem } from '@/command-menu-item/display/components/CommandListItem';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { useContext } from 'react';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { type MenuItemAccent } from 'twenty-ui/navigation';
|
||||
|
||||
export type ActionDisplayProps = {
|
||||
export type CommandMenuItemDisplayProps = {
|
||||
key: string;
|
||||
label: MessageDescriptor | string;
|
||||
shortLabel?: MessageDescriptor | string;
|
||||
@@ -20,7 +20,7 @@ export type ActionDisplayProps = {
|
||||
hotKeys?: string[];
|
||||
};
|
||||
|
||||
export const ActionDisplay = ({
|
||||
export const CommandMenuItemDisplay = ({
|
||||
onClick,
|
||||
to,
|
||||
disabled,
|
||||
@@ -29,20 +29,20 @@ export const ActionDisplay = ({
|
||||
to?: string;
|
||||
disabled?: boolean;
|
||||
}) => {
|
||||
const action = useContext(ActionConfigContext);
|
||||
const { displayType } = useContext(ActionMenuContext);
|
||||
const action = useContext(CommandConfigContext);
|
||||
const { displayType } = useContext(CommandMenuContext);
|
||||
|
||||
if (!action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (displayType === 'button') {
|
||||
return <ActionButton action={action} onClick={onClick} to={to} />;
|
||||
return <CommandMenuItemButton action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
if (displayType === 'listItem') {
|
||||
return (
|
||||
<ActionListItem
|
||||
<CommandListItem
|
||||
action={action}
|
||||
onClick={onClick}
|
||||
to={to}
|
||||
@@ -52,7 +52,7 @@ export const ActionDisplay = ({
|
||||
}
|
||||
|
||||
if (displayType === 'dropdownItem') {
|
||||
return <ActionDropdownItem action={action} onClick={onClick} to={to} />;
|
||||
return <CommandDropdownItem action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
return assertUnreachable(displayType, 'Unsupported display type');
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
@@ -9,7 +9,7 @@ import { useContext } from 'react';
|
||||
import { type SidePanelPages } from 'twenty-shared/types';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
|
||||
export const ActionOpenSidePanelPage = ({
|
||||
export const CommandMenuItemOpenSidePanelPage = ({
|
||||
page,
|
||||
pageTitle,
|
||||
pageIcon,
|
||||
@@ -22,7 +22,7 @@ export const ActionOpenSidePanelPage = ({
|
||||
onClick?: () => void;
|
||||
shouldResetSearchState?: boolean;
|
||||
}) => {
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
const actionConfig = useContext(CommandConfigContext);
|
||||
|
||||
const { navigateSidePanel } = useNavigateSidePanel();
|
||||
|
||||
@@ -46,5 +46,5 @@ export const ActionOpenSidePanelPage = ({
|
||||
}
|
||||
};
|
||||
|
||||
return <ActionDisplay onClick={handleClick} />;
|
||||
return <CommandMenuItemDisplay onClick={handleClick} />;
|
||||
};
|
||||
+20
-20
@@ -1,60 +1,60 @@
|
||||
import { type ReactNode, useContext } from 'react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { type ButtonAccent } from 'twenty-ui/input';
|
||||
|
||||
export type ActionModalProps = {
|
||||
export type CommandModalProps = {
|
||||
title: string;
|
||||
subtitle: ReactNode;
|
||||
onConfirmClick: () => void | Promise<void>;
|
||||
confirmButtonText?: string;
|
||||
confirmButtonAccent?: ButtonAccent;
|
||||
isLoading?: boolean;
|
||||
closeSidePanelOnShowPageOptionsActionExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListActionExecution?: boolean;
|
||||
closeSidePanelOnShowPageOptionsExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListExecution?: boolean;
|
||||
};
|
||||
|
||||
export const ActionModal = ({
|
||||
export const CommandModal = ({
|
||||
title,
|
||||
subtitle,
|
||||
onConfirmClick,
|
||||
confirmButtonText = t`Confirm`,
|
||||
confirmButtonAccent = 'danger',
|
||||
isLoading = false,
|
||||
closeSidePanelOnShowPageOptionsActionExecution,
|
||||
closeSidePanelOnCommandMenuListActionExecution,
|
||||
}: ActionModalProps) => {
|
||||
closeSidePanelOnShowPageOptionsExecution,
|
||||
closeSidePanelOnCommandMenuListExecution,
|
||||
}: CommandModalProps) => {
|
||||
const { openModal } = useModal();
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu({
|
||||
closeSidePanelOnShowPageOptionsActionExecution,
|
||||
closeSidePanelOnCommandMenuListActionExecution,
|
||||
const { closeCommandMenu } = useCloseCommandMenu({
|
||||
closeSidePanelOnShowPageOptionsExecution,
|
||||
closeSidePanelOnCommandMenuListExecution,
|
||||
});
|
||||
|
||||
const handleConfirmClick = async () => {
|
||||
await onConfirmClick();
|
||||
closeActionMenu();
|
||||
closeCommandMenu();
|
||||
};
|
||||
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
const { actionMenuType } = useContext(ActionMenuContext);
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
const { containerType } = useContext(CommandMenuContext);
|
||||
|
||||
const modalId = `${actionConfig?.key}-action-modal-${actionMenuType}`;
|
||||
const modalId = `${commandMenuItemConfig?.key}-command-menu-item-modal-${containerType}`;
|
||||
|
||||
const isModalOpened = useAtomComponentStateValue(
|
||||
isModalOpenedComponentState,
|
||||
modalId,
|
||||
);
|
||||
|
||||
if (!actionConfig) {
|
||||
if (!commandMenuItemConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export const ActionModal = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionDisplay onClick={handleClick} />
|
||||
<CommandMenuItemDisplay onClick={handleClick} />
|
||||
{isModalOpened && (
|
||||
<ConfirmationModal
|
||||
modalInstanceId={modalId}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { isHeadlessFrontComponentMountedFamilySelector } from '@/front-components/selectors/isHeadlessFrontComponentMountedFamilySelector';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CommandMenuItemDisplay } from './CommandMenuItemDisplay';
|
||||
|
||||
export const HeadlessFrontComponentCommandMenuItem = ({
|
||||
frontComponentId,
|
||||
onClick,
|
||||
}: {
|
||||
frontComponentId: string;
|
||||
onClick: () => void;
|
||||
}) => {
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
|
||||
const { closeCommandMenu } = useCloseCommandMenu({
|
||||
closeSidePanelOnShowPageOptionsExecution: false,
|
||||
closeSidePanelOnCommandMenuListExecution: false,
|
||||
});
|
||||
|
||||
const isMounted = useAtomFamilySelectorValue(
|
||||
isHeadlessFrontComponentMountedFamilySelector,
|
||||
frontComponentId,
|
||||
);
|
||||
|
||||
if (!commandMenuItemConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if (isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeCommandMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <CommandMenuItemDisplay onClick={handleClick} disabled={isMounted} />;
|
||||
};
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { CommandMenuItemButton } from '@/command-menu-item/display/components/CommandMenuItemButton';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const meta: Meta<typeof CommandMenuItemButton> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandMenuItemButton',
|
||||
component: CommandMenuItemButton,
|
||||
decorators: [ComponentDecorator, RouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CommandMenuItemButton>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(
|
||||
addToFavoritesCommandMenuItem?.shortLabel ?? '',
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleCommandMenuItem,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const menuItem = await canvas.findByText(
|
||||
getCommandMenuItemLabel(goToPeopleCommandMenuItem?.shortLabel ?? ''),
|
||||
);
|
||||
expect(menuItem).toBeVisible();
|
||||
expect(canvas.getByRole('link')).toHaveAttribute('href', '/objects/people');
|
||||
},
|
||||
};
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const mockActions = createMockCommandMenuItems({});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
if (!addToFavoritesCommandMenuItem) {
|
||||
throw new Error('Add to favorites action not found');
|
||||
}
|
||||
|
||||
const meta: Meta<typeof CommandMenuItemComponent> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandMenuItemComponent',
|
||||
component: CommandMenuItemComponent,
|
||||
decorators: [
|
||||
ComponentDecorator,
|
||||
(Story) => (
|
||||
<CommandMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
containerType: 'index-page-header',
|
||||
displayType: 'button',
|
||||
commandMenuItems: [addToFavoritesCommandMenuItem],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</CommandMenuContext.Provider>
|
||||
</CommandMenuComponentInstanceContext.Provider>
|
||||
),
|
||||
],
|
||||
args: {
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
},
|
||||
parameters: {
|
||||
container: {
|
||||
width: 'auto',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CommandMenuItemComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
expect(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(
|
||||
addToFavoritesCommandMenuItem?.shortLabel ?? '',
|
||||
),
|
||||
),
|
||||
).toBeVisible();
|
||||
},
|
||||
};
|
||||
+34
-32
@@ -1,40 +1,40 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
type Story = StoryObj<typeof ActionDisplay>;
|
||||
type Story = StoryObj<typeof CommandMenuItemDisplay>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockActionMenuActions({
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesAction = mockActions.find(
|
||||
(action) => action.key === SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
if (!addToFavoritesAction) {
|
||||
throw new Error('addToFavoritesAction not found');
|
||||
if (!addToFavoritesCommandMenuItem) {
|
||||
throw new Error('addToFavoritesCommandMenuItem not found');
|
||||
}
|
||||
|
||||
const meta: Meta<typeof ActionDisplay> = {
|
||||
title: 'Modules/ActionMenu/Actions/Display/ActionDisplay',
|
||||
component: ActionDisplay,
|
||||
const meta: Meta<typeof CommandMenuItemDisplay> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandMenuItemDisplay',
|
||||
component: CommandMenuItemDisplay,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<ActionConfigContext.Provider value={addToFavoritesAction}>
|
||||
<CommandConfigContext.Provider value={addToFavoritesCommandMenuItem}>
|
||||
<Story />
|
||||
</ActionConfigContext.Provider>
|
||||
</CommandConfigContext.Provider>
|
||||
),
|
||||
ComponentDecorator,
|
||||
RouterDecorator,
|
||||
@@ -49,23 +49,25 @@ export const AsButton: Story = {
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
actionMenuType: 'command-menu',
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'button',
|
||||
actions: [],
|
||||
commandMenuItems: [],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
),
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.shortLabel ?? ''),
|
||||
getCommandMenuItemLabel(
|
||||
addToFavoritesCommandMenuItem?.shortLabel ?? '',
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
@@ -85,23 +87,23 @@ export const AsListItem: Story = {
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
),
|
||||
(Story) => (
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
actionMenuType: 'command-menu',
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'listItem',
|
||||
actions: [],
|
||||
commandMenuItems: [],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
),
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.label ?? ''),
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
@@ -121,23 +123,23 @@ export const AsDropdownItem: Story = {
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
),
|
||||
(Story) => (
|
||||
<ActionMenuContext.Provider
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
actionMenuType: 'command-menu',
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'dropdownItem',
|
||||
actions: [],
|
||||
commandMenuItems: [],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</CommandMenuContext.Provider>
|
||||
),
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.label ?? ''),
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
+18
-18
@@ -1,16 +1,16 @@
|
||||
import { ActionDropdownItem } from '@/action-menu/actions/display/components/ActionDropdownItem';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { CommandDropdownItem } from '@/command-menu-item/display/components/CommandDropdownItem';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const meta: Meta<typeof ActionDropdownItem> = {
|
||||
title: 'Modules/ActionMenu/Actions/Display/ActionDropdownItem',
|
||||
component: ActionDropdownItem,
|
||||
const meta: Meta<typeof CommandDropdownItem> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandDropdownItem',
|
||||
component: CommandDropdownItem,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
@@ -26,34 +26,34 @@ const meta: Meta<typeof ActionDropdownItem> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ActionDropdownItem>;
|
||||
type Story = StoryObj<typeof CommandDropdownItem>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockActionMenuActions({
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesAction = mockActions.find(
|
||||
(action) => action.key === SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleAction = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
const goToPeopleCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesAction,
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.label ?? ''),
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
@@ -62,13 +62,13 @@ export const Default: Story = {
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleAction,
|
||||
action: goToPeopleCommandMenuItem,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const dropdownItem = await canvas.findByText(
|
||||
getActionLabel(goToPeopleAction?.label ?? ''),
|
||||
getCommandMenuItemLabel(goToPeopleCommandMenuItem?.label ?? ''),
|
||||
);
|
||||
expect(dropdownItem).toBeVisible();
|
||||
},
|
||||
+18
-18
@@ -1,34 +1,34 @@
|
||||
import { ActionListItem } from '@/action-menu/actions/display/components/ActionListItem';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { CommandListItem } from '@/command-menu-item/display/components/CommandListItem';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
type Story = StoryObj<typeof ActionListItem>;
|
||||
type Story = StoryObj<typeof CommandListItem>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockActionMenuActions({
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesAction = mockActions.find(
|
||||
(action) => action.key === SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleAction = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
const goToPeopleCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
const meta: Meta<typeof ActionListItem> = {
|
||||
title: 'Modules/ActionMenu/Actions/Display/ActionListItem',
|
||||
component: ActionListItem,
|
||||
const meta: Meta<typeof CommandListItem> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandListItem',
|
||||
component: CommandListItem,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
@@ -46,14 +46,14 @@ export default meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesAction,
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getActionLabel(addToFavoritesAction?.label ?? ''),
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
@@ -62,13 +62,13 @@ export const Default: Story = {
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleAction,
|
||||
action: goToPeopleCommandMenuItem,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const listItem = await canvas.findByText(
|
||||
getActionLabel(goToPeopleAction?.label ?? ''),
|
||||
getCommandMenuItemLabel(goToPeopleCommandMenuItem?.label ?? ''),
|
||||
);
|
||||
expect(listItem).toBeVisible();
|
||||
},
|
||||
@@ -0,0 +1,60 @@
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { getCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getCommandMenuDropdownIdFromCommandMenuId';
|
||||
import { getSidePanelCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getSidePanelCommandMenuDropdownIdFromCommandMenuId';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useCloseCommandMenu = ({
|
||||
closeSidePanelOnShowPageOptionsExecution = false,
|
||||
closeSidePanelOnCommandMenuListExecution = true,
|
||||
}: {
|
||||
closeSidePanelOnShowPageOptionsExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListExecution?: boolean;
|
||||
} = {}) => {
|
||||
const { containerType, isInSidePanel } = useContext(CommandMenuContext);
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
CommandMenuComponentInstanceContext,
|
||||
);
|
||||
|
||||
const dropdownId = isInSidePanel
|
||||
? getSidePanelCommandMenuDropdownIdFromCommandMenuId(commandMenuId)
|
||||
: getCommandMenuDropdownIdFromCommandMenuId(commandMenuId);
|
||||
|
||||
const closeCommandMenu = () => {
|
||||
if (containerType === 'command-menu-list') {
|
||||
if (
|
||||
isDefined(closeSidePanelOnCommandMenuListExecution) &&
|
||||
!closeSidePanelOnCommandMenuListExecution
|
||||
) {
|
||||
return;
|
||||
}
|
||||
closeSidePanelMenu();
|
||||
}
|
||||
|
||||
if (
|
||||
containerType === 'index-page-dropdown' ||
|
||||
containerType === 'command-menu-show-page-dropdown'
|
||||
) {
|
||||
closeDropdown(dropdownId);
|
||||
}
|
||||
|
||||
if (
|
||||
containerType === 'command-menu-show-page-dropdown' &&
|
||||
isDefined(closeSidePanelOnShowPageOptionsExecution) &&
|
||||
closeSidePanelOnShowPageOptionsExecution
|
||||
) {
|
||||
closeSidePanelMenu();
|
||||
}
|
||||
};
|
||||
|
||||
return { closeCommandMenu };
|
||||
};
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
@@ -30,7 +30,7 @@ import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
export const useCommandMenuContextApi = (): CommandMenuContextApi => {
|
||||
const store = useStore();
|
||||
|
||||
const { isInSidePanel } = useContext(ActionMenuContext);
|
||||
const { isInSidePanel } = useContext(CommandMenuContext);
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
+22
-22
@@ -1,8 +1,8 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { HeadlessFrontComponentAction } from '@/action-menu/actions/display/components/HeadlessFrontComponentAction';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { HeadlessFrontComponentCommandMenuItem } from '@/command-menu-item/display/components/HeadlessFrontComponentCommandMenuItem';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { useOpenFrontComponentInSidePanel } from '@/side-panel/hooks/useOpenFrontComponentInSidePanel';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
@@ -34,9 +34,9 @@ type CommandMenuItemWithFrontComponent = CommandMenuItemFieldsFragment & {
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
};
|
||||
|
||||
type BuildActionFromItemParams = {
|
||||
type BuildCommandMenuItemFromFrontComponentParams = {
|
||||
item: CommandMenuItemWithFrontComponent;
|
||||
scope: ActionScope;
|
||||
scope: CommandMenuItemScope;
|
||||
index: number;
|
||||
isPinned: boolean;
|
||||
getIcon: ReturnType<typeof useIcons>['getIcon'];
|
||||
@@ -59,7 +59,7 @@ type BuildActionFromItemParams = {
|
||||
|
||||
// TODO: we should remove this backward compatibility logic in the future
|
||||
// once we have migrated all command menu items
|
||||
const buildActionFromItem = ({
|
||||
const buildCommandMenuItemFromFrontComponent = ({
|
||||
item,
|
||||
scope,
|
||||
index,
|
||||
@@ -69,7 +69,7 @@ const buildActionFromItem = ({
|
||||
mountHeadlessFrontComponent,
|
||||
mountContext,
|
||||
commandMenuContextApi,
|
||||
}: BuildActionFromItemParams) => {
|
||||
}: BuildCommandMenuItemFromFrontComponentParams) => {
|
||||
const displayLabel = item.label;
|
||||
|
||||
const Icon = getIcon(item.icon, COMMAND_MENU_DEFAULT_ICON);
|
||||
@@ -95,7 +95,7 @@ const buildActionFromItem = ({
|
||||
};
|
||||
|
||||
return {
|
||||
type: ActionType.FrontComponent,
|
||||
type: CommandMenuItemType.FrontComponent,
|
||||
key: `command-menu-item-front-component-${item.id}`,
|
||||
scope,
|
||||
label: displayLabel,
|
||||
@@ -109,12 +109,12 @@ const buildActionFromItem = ({
|
||||
commandMenuContextApi,
|
||||
),
|
||||
component: isHeadless ? (
|
||||
<HeadlessFrontComponentAction
|
||||
<HeadlessFrontComponentCommandMenuItem
|
||||
frontComponentId={item.frontComponentId}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
) : (
|
||||
<Action onClick={handleClick} />
|
||||
<Command onClick={handleClick} />
|
||||
),
|
||||
};
|
||||
};
|
||||
@@ -130,7 +130,7 @@ export const useCommandMenuItemFrontComponentActions = (
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
|
||||
const { actionMenuType } = useContext(ActionMenuContext);
|
||||
const { containerType } = useContext(CommandMenuContext);
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
@@ -166,8 +166,8 @@ export const useCommandMenuItemFrontComponentActions = (
|
||||
const { data } = useFindManyCommandMenuItemsQuery({
|
||||
skip:
|
||||
!isCommandMenuItemEnabled ||
|
||||
(actionMenuType !== 'command-menu' &&
|
||||
actionMenuType !== 'command-menu-show-page-action-menu-dropdown'),
|
||||
(containerType !== 'command-menu-list' &&
|
||||
containerType !== 'command-menu-show-page-dropdown'),
|
||||
});
|
||||
|
||||
const frontComponentItems =
|
||||
@@ -200,10 +200,10 @@ export const useCommandMenuItemFrontComponentActions = (
|
||||
);
|
||||
});
|
||||
|
||||
const globalActions = globalItems.map((item, index) =>
|
||||
buildActionFromItem({
|
||||
const globalCommandMenuItems = globalItems.map((item, index) =>
|
||||
buildCommandMenuItemFromFrontComponent({
|
||||
item,
|
||||
scope: ActionScope.Global,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
index,
|
||||
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
|
||||
getIcon,
|
||||
@@ -213,10 +213,10 @@ export const useCommandMenuItemFrontComponentActions = (
|
||||
}),
|
||||
);
|
||||
|
||||
const recordScopedActions = recordScopedItems.map((item, index) =>
|
||||
buildActionFromItem({
|
||||
const recordScopedCommandMenuItems = recordScopedItems.map((item, index) =>
|
||||
buildCommandMenuItemFromFrontComponent({
|
||||
item,
|
||||
scope: ActionScope.RecordSelection,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
index,
|
||||
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
|
||||
getIcon,
|
||||
@@ -227,5 +227,5 @@ export const useCommandMenuItemFrontComponentActions = (
|
||||
}),
|
||||
);
|
||||
|
||||
return [...globalActions, ...recordScopedActions];
|
||||
return [...globalCommandMenuItems, ...recordScopedCommandMenuItems];
|
||||
};
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import { useRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/hooks/useRecordAgnosticCommands';
|
||||
import { useRelatedRecordCommands } from '@/command-menu-item/record-agnostic/hooks/useRelatedRecordCommands';
|
||||
import { CommandMenuItemViewType } from 'twenty-shared/types';
|
||||
import { type ShouldBeRegisteredFunctionParams } from '@/command-menu-item/types/ShouldBeRegisteredFunctionParams';
|
||||
import { getCommandMenuItemConfig } from '@/command-menu-item/utils/getCommandMenuItemConfig';
|
||||
import { getCommandMenuItemViewType } from '@/command-menu-item/utils/getCommandMenuItemViewType';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
export const useRegisteredCommandMenuItems = (
|
||||
shouldBeRegisteredParams: ShouldBeRegisteredFunctionParams,
|
||||
) => {
|
||||
const { objectMetadataItem } = shouldBeRegisteredParams;
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useAtomComponentStateValue(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
);
|
||||
|
||||
const contextStoreIsPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
|
||||
const viewType = getCommandMenuItemViewType(
|
||||
contextStoreCurrentViewType,
|
||||
contextStoreTargetedRecordsRule,
|
||||
);
|
||||
|
||||
const recordCommandMenuItemsConfig = getCommandMenuItemConfig({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const relatedRecordCommandMenuItemsConfig = useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon,
|
||||
startPosition: Object.keys(recordCommandMenuItemsConfig).length + 1,
|
||||
});
|
||||
|
||||
const recordAgnosticCommandMenuItemsConfig = useRecordAgnosticCommands();
|
||||
|
||||
const commandMenuItemsConfig = {
|
||||
...recordCommandMenuItemsConfig,
|
||||
...relatedRecordCommandMenuItemsConfig,
|
||||
...recordAgnosticCommandMenuItemsConfig,
|
||||
};
|
||||
|
||||
const permissionMap = usePermissionFlagMap();
|
||||
|
||||
const commandMenuItemsToRegister = Object.values(
|
||||
commandMenuItemsConfig,
|
||||
).filter((commandMenuItem) => {
|
||||
if (contextStoreIsPageInEditMode) {
|
||||
return (
|
||||
isDefined(commandMenuItem.availableOn) &&
|
||||
commandMenuItem.availableOn.includes(
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(viewType)) {
|
||||
return (
|
||||
commandMenuItem.availableOn?.includes(viewType) ||
|
||||
commandMenuItem.availableOn?.includes(CommandMenuItemViewType.GLOBAL)
|
||||
);
|
||||
}
|
||||
|
||||
return commandMenuItem.availableOn?.includes(
|
||||
CommandMenuItemViewType.GLOBAL,
|
||||
);
|
||||
});
|
||||
|
||||
const commandMenuItems = commandMenuItemsToRegister
|
||||
.filter((commandMenuItem) => {
|
||||
if (
|
||||
isDefined(commandMenuItem.requiredPermissionFlag) &&
|
||||
!permissionMap[commandMenuItem.requiredPermissionFlag]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return commandMenuItem.shouldBeRegistered(shouldBeRegisteredParams);
|
||||
})
|
||||
.sort((a, b) => a.position - b.position);
|
||||
|
||||
return commandMenuItems;
|
||||
};
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams';
|
||||
import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { type ShouldBeRegisteredFunctionParams } from '@/command-menu-item/types/ShouldBeRegisteredFunctionParams';
|
||||
import { getCommandMenuItemViewType } from '@/command-menu-item/utils/getCommandMenuItemViewType';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
@@ -25,7 +25,7 @@ import { useCallback, useContext, useMemo } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useShouldActionBeRegisteredParams = ({
|
||||
export const useShouldCommandMenuItemBeRegisteredParams = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem?: ObjectMetadataItem;
|
||||
@@ -84,7 +84,7 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note ||
|
||||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task;
|
||||
|
||||
const { isInSidePanel } = useContext(ActionMenuContext);
|
||||
const { isInSidePanel } = useContext(CommandMenuContext);
|
||||
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
@@ -105,7 +105,7 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
);
|
||||
|
||||
const viewType = getActionViewType(
|
||||
const viewType = getCommandMenuItemViewType(
|
||||
contextStoreCurrentViewType,
|
||||
contextStoreTargetedRecordsRule,
|
||||
);
|
||||
@@ -0,0 +1,104 @@
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { CommandLink } from '@/command-menu-item/display/components/CommandLink';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import {
|
||||
CommandMenuItemViewType,
|
||||
CoreObjectNameSingular,
|
||||
AppPath,
|
||||
} from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
IconFileExport,
|
||||
IconHeart,
|
||||
IconTrash,
|
||||
IconUser,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const createMockCommandMenuItems = ({
|
||||
deleteMock = () => {},
|
||||
addToFavoritesMock = () => {},
|
||||
exportMock = () => {},
|
||||
}: {
|
||||
deleteMock?: () => void;
|
||||
addToFavoritesMock?: () => void;
|
||||
exportMock?: () => void;
|
||||
}): CommandMenuItemConfig[] => [
|
||||
{
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
label: msg`Add to favorites`,
|
||||
shortLabel: msg`Add to favorites`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
Icon: IconHeart,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <Command onClick={addToFavoritesMock} />,
|
||||
},
|
||||
{
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 4,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
|
||||
component: <Command onClick={exportMock} />,
|
||||
},
|
||||
{
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.DELETE,
|
||||
label: msg`Delete`,
|
||||
shortLabel: msg`Delete`,
|
||||
position: 7,
|
||||
Icon: IconTrash,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <Command onClick={deleteMock} />,
|
||||
},
|
||||
{
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
label: msg`Go to People`,
|
||||
shortLabel: msg`People`,
|
||||
position: 19,
|
||||
Icon: IconUser,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Person ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Person }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'P'],
|
||||
},
|
||||
];
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import { CommandMenuItemOpenSidePanelPage } from '@/command-menu-item/display/components/CommandMenuItemOpenSidePanelPage';
|
||||
import { RecordAgnosticCommandKeys } from '@/command-menu-item/record-agnostic/types/RecordAgnosticCommandKeys';
|
||||
import { EditNavigationSidebarNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/EditNavigationSidebarNoSelectionRecordCommand';
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuItemViewType, SidePanelPages } from 'twenty-shared/types';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
IconHistory,
|
||||
IconLayout,
|
||||
IconSearch,
|
||||
IconSparkles,
|
||||
} from 'twenty-ui/display';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG: Record<
|
||||
string,
|
||||
CommandMenuItemConfig
|
||||
> = {
|
||||
[RecordAgnosticCommandKeys.SEARCH_RECORDS]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: RecordAgnosticCommandKeys.SEARCH_RECORDS,
|
||||
label: msg`Search records`,
|
||||
shortLabel: msg`Search`,
|
||||
position: 0,
|
||||
isPinned: false,
|
||||
Icon: IconSearch,
|
||||
availableOn: [CommandMenuItemViewType.GLOBAL],
|
||||
component: (
|
||||
<CommandMenuItemOpenSidePanelPage
|
||||
page={SidePanelPages.SearchRecords}
|
||||
pageTitle={msg`Search`}
|
||||
pageIcon={IconSearch}
|
||||
shouldResetSearchState={true}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['/'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticCommandKeys.SEARCH_RECORDS_FALLBACK]: {
|
||||
type: CommandMenuItemType.Fallback,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: RecordAgnosticCommandKeys.SEARCH_RECORDS_FALLBACK,
|
||||
label: msg`Search records`,
|
||||
shortLabel: msg`Search`,
|
||||
position: 1,
|
||||
isPinned: false,
|
||||
Icon: IconSearch,
|
||||
availableOn: [CommandMenuItemViewType.GLOBAL],
|
||||
component: (
|
||||
<CommandMenuItemOpenSidePanelPage
|
||||
page={SidePanelPages.SearchRecords}
|
||||
pageTitle={msg`Search`}
|
||||
pageIcon={IconSearch}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['/'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticCommandKeys.ASK_AI]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: RecordAgnosticCommandKeys.ASK_AI,
|
||||
label: msg`Ask AI`,
|
||||
shortLabel: msg`Ask AI`,
|
||||
position: 2,
|
||||
isPinned: false,
|
||||
Icon: IconSparkles,
|
||||
availableOn: [CommandMenuItemViewType.GLOBAL],
|
||||
component: (
|
||||
<CommandMenuItemOpenSidePanelPage
|
||||
page={SidePanelPages.AskAI}
|
||||
pageTitle={msg`Ask AI`}
|
||||
pageIcon={IconSparkles}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['@'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticCommandKeys.VIEW_PREVIOUS_AI_CHATS]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: RecordAgnosticCommandKeys.VIEW_PREVIOUS_AI_CHATS,
|
||||
label: msg`View Previous AI Chats`,
|
||||
shortLabel: msg`Previous AI Chats`,
|
||||
position: 3,
|
||||
isPinned: false,
|
||||
Icon: IconHistory,
|
||||
availableOn: [CommandMenuItemViewType.GLOBAL],
|
||||
component: (
|
||||
<CommandMenuItemOpenSidePanelPage
|
||||
page={SidePanelPages.ViewPreviousAIChats}
|
||||
pageTitle={msg`View Previous AI Chats`}
|
||||
pageIcon={IconSparkles}
|
||||
/>
|
||||
),
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticCommandKeys.EDIT_NAVIGATION_SIDEBAR]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: RecordAgnosticCommandKeys.EDIT_NAVIGATION_SIDEBAR,
|
||||
label: msg`Edit navigation sidebar`,
|
||||
shortLabel: msg`Edit sidebar`,
|
||||
position: 4,
|
||||
Icon: IconLayout,
|
||||
isPinned: false,
|
||||
availableOn: [CommandMenuItemViewType.GLOBAL],
|
||||
shouldBeRegistered: ({ isFeatureFlagEnabled }) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
|
||||
),
|
||||
component: <EditNavigationSidebarNoSelectionRecordCommand />,
|
||||
},
|
||||
};
|
||||
+8
-8
@@ -1,7 +1,7 @@
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useRelatedRecordActions } from '@/action-menu/actions/record-agnostic-actions/hooks/useRelatedRecordActions';
|
||||
import { useRelatedRecordCommands } from '@/command-menu-item/record-agnostic/hooks/useRelatedRecordCommands';
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useObjectMetadataItems', () => ({
|
||||
useObjectMetadataItems: () => ({
|
||||
@@ -22,7 +22,7 @@ jest.mock('@/object-metadata/hooks/useObjectMetadataItems', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useRelatedRecordActions', () => {
|
||||
describe('useRelatedRecordCommands', () => {
|
||||
const mockGetIcon = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -37,7 +37,7 @@ describe('useRelatedRecordActions', () => {
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
@@ -51,7 +51,7 @@ describe('useRelatedRecordActions', () => {
|
||||
const objectMetadataItem = undefined as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
@@ -95,7 +95,7 @@ describe('useRelatedRecordActions', () => {
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
@@ -145,7 +145,7 @@ describe('useRelatedRecordActions', () => {
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
@@ -191,7 +191,7 @@ describe('useRelatedRecordActions', () => {
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
@@ -237,7 +237,7 @@ describe('useRelatedRecordActions', () => {
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
useRelatedRecordCommands({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG } from '@/command-menu-item/record-agnostic/constants/RecordAgnosticCommandMenuItemsConfig';
|
||||
import { RecordAgnosticCommandKeys } from '@/command-menu-item/record-agnostic/types/RecordAgnosticCommandKeys';
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useRecordAgnosticCommands = () => {
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const commandMenuItems: Record<string, CommandMenuItemConfig> = {
|
||||
[RecordAgnosticCommandKeys.SEARCH_RECORDS]:
|
||||
RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG[
|
||||
RecordAgnosticCommandKeys.SEARCH_RECORDS
|
||||
],
|
||||
[RecordAgnosticCommandKeys.SEARCH_RECORDS_FALLBACK]:
|
||||
RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG[
|
||||
RecordAgnosticCommandKeys.SEARCH_RECORDS_FALLBACK
|
||||
],
|
||||
[RecordAgnosticCommandKeys.EDIT_NAVIGATION_SIDEBAR]:
|
||||
RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG[
|
||||
RecordAgnosticCommandKeys.EDIT_NAVIGATION_SIDEBAR
|
||||
],
|
||||
};
|
||||
|
||||
if (isAiEnabled) {
|
||||
commandMenuItems[RecordAgnosticCommandKeys.ASK_AI] =
|
||||
RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG[
|
||||
RecordAgnosticCommandKeys.ASK_AI
|
||||
];
|
||||
commandMenuItems[RecordAgnosticCommandKeys.VIEW_PREVIOUS_AI_CHATS] =
|
||||
RECORD_AGNOSTIC_COMMAND_MENU_ITEMS_CONFIG[
|
||||
RecordAgnosticCommandKeys.VIEW_PREVIOUS_AI_CHATS
|
||||
];
|
||||
}
|
||||
|
||||
return commandMenuItems;
|
||||
};
|
||||
+23
-17
@@ -1,8 +1,11 @@
|
||||
import { CreateRelatedRecordAction } from '@/action-menu/actions/record-actions/single-record/components/CreateRelatedRecordAction';
|
||||
import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType, CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { CreateRelatedRecordCommand } from '@/command-menu-item/record/single-record/components/CreateRelatedRecordCommand';
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import {
|
||||
CommandMenuItemViewType,
|
||||
CoreObjectNameSingular,
|
||||
} from 'twenty-shared/types';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField';
|
||||
@@ -12,23 +15,26 @@ import React from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type IconComponent, IconPlus } from 'twenty-ui/display';
|
||||
|
||||
interface GenerateRelatedRecordActionsParams {
|
||||
interface GenerateRelatedRecordCommandsParams {
|
||||
sourceObjectMetadataItem?: ObjectMetadataItem;
|
||||
getIcon: (iconKey: string) => IconComponent;
|
||||
startPosition: number;
|
||||
}
|
||||
|
||||
export const useRelatedRecordActions = ({
|
||||
export const useRelatedRecordCommands = ({
|
||||
sourceObjectMetadataItem,
|
||||
getIcon,
|
||||
startPosition,
|
||||
}: GenerateRelatedRecordActionsParams): Record<string, ActionConfig> => {
|
||||
const relatedActions: Record<string, ActionConfig> = {};
|
||||
}: GenerateRelatedRecordCommandsParams): Record<
|
||||
string,
|
||||
CommandMenuItemConfig
|
||||
> => {
|
||||
const relatedCommands: Record<string, CommandMenuItemConfig> = {};
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
if (!sourceObjectMetadataItem?.fields) {
|
||||
return relatedActions;
|
||||
return relatedCommands;
|
||||
}
|
||||
|
||||
const oneToManyFields = sourceObjectMetadataItem.readableFields.filter(
|
||||
@@ -67,9 +73,9 @@ export const useRelatedRecordActions = ({
|
||||
|
||||
const actionKey = `create-related-${targetObjectLabelSingular}`;
|
||||
|
||||
relatedActions[actionKey] = {
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.CreateRelatedRecord,
|
||||
relatedCommands[actionKey] = {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.CreateRelatedRecord,
|
||||
key: actionKey,
|
||||
label: msg`Create ${targetObjectLabelSingular}`,
|
||||
shortLabel: msg`Create ${targetObjectLabelSingular}`,
|
||||
@@ -103,10 +109,10 @@ export const useRelatedRecordActions = ({
|
||||
)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: React.createElement(CreateRelatedRecordAction, {
|
||||
component: React.createElement(CreateRelatedRecordCommand, {
|
||||
targetFieldMetadataItemRelation: field.relation,
|
||||
}),
|
||||
};
|
||||
@@ -114,5 +120,5 @@ export const useRelatedRecordActions = ({
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
return relatedActions;
|
||||
return relatedCommands;
|
||||
};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export enum RecordAgnosticActionsKeys {
|
||||
export enum RecordAgnosticCommandKeys {
|
||||
SEARCH_RECORDS = 'search-records',
|
||||
SEARCH_RECORDS_FALLBACK = 'search-records-fallback',
|
||||
ASK_AI = 'ask-ai',
|
||||
+12
-12
@@ -1,7 +1,7 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||
@@ -11,20 +11,20 @@ import { useContext } from 'react';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
export const useRunWorkflowRecordAgnosticActions = () => {
|
||||
export const useRunWorkflowRecordAgnosticCommands = () => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const contextStoreIsPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
|
||||
const { actionMenuType } = useContext(ActionMenuContext);
|
||||
const { containerType } = useContext(CommandMenuContext);
|
||||
|
||||
const { records: activeWorkflowVersions } =
|
||||
useActiveWorkflowVersionsWithManualTrigger({
|
||||
skip:
|
||||
actionMenuType !== 'command-menu' &&
|
||||
actionMenuType !== 'command-menu-show-page-action-menu-dropdown',
|
||||
containerType !== 'command-menu-list' &&
|
||||
containerType !== 'command-menu-show-page-dropdown',
|
||||
});
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
@@ -43,9 +43,9 @@ export const useRunWorkflowRecordAgnosticActions = () => {
|
||||
);
|
||||
|
||||
return {
|
||||
type: ActionType.WorkflowRun,
|
||||
type: CommandMenuItemType.WorkflowRun,
|
||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||
scope: ActionScope.Global,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
label: name,
|
||||
shortLabel: name,
|
||||
position: index,
|
||||
@@ -55,14 +55,14 @@ export const useRunWorkflowRecordAgnosticActions = () => {
|
||||
Icon,
|
||||
shouldBeRegistered: () => true,
|
||||
component: (
|
||||
<Action
|
||||
<Command
|
||||
onClick={() => {
|
||||
runWorkflowVersion({
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
workflowId: activeWorkflowVersion.workflowId,
|
||||
});
|
||||
}}
|
||||
closeSidePanelOnCommandMenuListActionExecution={false}
|
||||
closeSidePanelOnCommandMenuListExecution={false}
|
||||
/>
|
||||
),
|
||||
};
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
import { MultipleRecordsCommandKeys } from '@/command-menu-item/record/multiple-records/types/MultipleRecordsCommandKeys';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { CancelDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/CancelDashboardSingleRecordCommand';
|
||||
import { DuplicateDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/DuplicateDashboardSingleRecordCommand';
|
||||
import { EditDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/EditDashboardSingleRecordCommand';
|
||||
import { SaveDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/SaveDashboardSingleRecordCommand';
|
||||
import { DashboardSingleRecordCommandKeys } from '@/command-menu-item/record/single-record/dashboard/types/DashboardSingleRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { inheritCommandMenuItemsFromDefaultConfig } from '@/command-menu-item/record/utils/inheritCommandMenuItemsFromDefaultConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuItemViewType } from 'twenty-shared/types';
|
||||
import { PageLayoutSingleRecordActionKeys } from '@/page-layout/actions/PageLayoutSingleRecordActionKeys';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconCancel,
|
||||
IconCopyPlus,
|
||||
IconDeviceFloppy,
|
||||
IconPencil,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const DASHBOARD_COMMAND_MENU_ITEMS_CONFIG =
|
||||
inheritCommandMenuItemsFromDefaultConfig({
|
||||
config: {
|
||||
[PageLayoutSingleRecordActionKeys.EDIT_LAYOUT]: {
|
||||
key: PageLayoutSingleRecordActionKeys.EDIT_LAYOUT,
|
||||
label: msg`Edit Dashboard`,
|
||||
shortLabel: msg`Edit`,
|
||||
isPinned: true,
|
||||
position: 3,
|
||||
Icon: IconPencil,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
isDefined(selectedRecord?.pageLayoutId) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <EditDashboardSingleRecordCommand />,
|
||||
},
|
||||
[PageLayoutSingleRecordActionKeys.SAVE_LAYOUT]: {
|
||||
key: PageLayoutSingleRecordActionKeys.SAVE_LAYOUT,
|
||||
label: msg`Save Dashboard`,
|
||||
shortLabel: msg`Save`,
|
||||
isPinned: true,
|
||||
isPrimaryCTA: true,
|
||||
position: 4,
|
||||
Icon: IconDeviceFloppy,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
isDefined(selectedRecord?.pageLayoutId) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
|
||||
component: <SaveDashboardSingleRecordCommand />,
|
||||
},
|
||||
[PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION]: {
|
||||
key: PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION,
|
||||
label: msg`Cancel Edition`,
|
||||
shortLabel: msg`Cancel`,
|
||||
isPinned: true,
|
||||
position: 5,
|
||||
Icon: IconCancel,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
isDefined(selectedRecord?.pageLayoutId) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
|
||||
component: <CancelDashboardSingleRecordCommand />,
|
||||
},
|
||||
[DashboardSingleRecordCommandKeys.DUPLICATE_DASHBOARD]: {
|
||||
key: DashboardSingleRecordCommandKeys.DUPLICATE_DASHBOARD,
|
||||
label: msg`Duplicate Dashboard`,
|
||||
shortLabel: msg`Duplicate`,
|
||||
isPinned: false,
|
||||
position: 6,
|
||||
Icon: IconCopyPlus,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions }) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <DuplicateDashboardSingleRecordCommand />,
|
||||
},
|
||||
},
|
||||
commandKeys: [
|
||||
NoSelectionRecordCommandKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordCommandKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordCommandKeys.DELETE,
|
||||
SingleRecordCommandKeys.DESTROY,
|
||||
SingleRecordCommandKeys.RESTORE,
|
||||
MultipleRecordsCommandKeys.DELETE,
|
||||
MultipleRecordsCommandKeys.DESTROY,
|
||||
MultipleRecordsCommandKeys.RESTORE,
|
||||
NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordCommandKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 0,
|
||||
label: msg`Navigate to next dashboard`,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 1,
|
||||
label: msg`Navigate to previous dashboard`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.CREATE_NEW_RECORD]: {
|
||||
position: 2,
|
||||
label: msg`Create new dashboard`,
|
||||
},
|
||||
[SingleRecordCommandKeys.DELETE]: {
|
||||
position: 7,
|
||||
label: msg`Delete dashboard`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.DELETE]: {
|
||||
position: 12,
|
||||
label: msg`Delete dashboards`,
|
||||
},
|
||||
[SingleRecordCommandKeys.ADD_TO_FAVORITES]: {
|
||||
position: 8,
|
||||
isPinned: true,
|
||||
},
|
||||
[SingleRecordCommandKeys.REMOVE_FROM_FAVORITES]: {
|
||||
position: 9,
|
||||
isPinned: true,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 10,
|
||||
label: msg`Export dashboard`,
|
||||
},
|
||||
[SingleRecordCommandKeys.DESTROY]: {
|
||||
position: 11,
|
||||
label: msg`Permanently destroy dashboard`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.DESTROY]: {
|
||||
position: 13,
|
||||
label: msg`Permanently destroy dashboards`,
|
||||
},
|
||||
[SingleRecordCommandKeys.RESTORE]: {
|
||||
position: 14,
|
||||
label: msg`Restore dashboard`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.RESTORE]: {
|
||||
position: 15,
|
||||
label: msg`Restore dashboards`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 22,
|
||||
label: msg`See deleted dashboards`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 23,
|
||||
label: msg`Hide deleted dashboards`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 24,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_PEOPLE]: {
|
||||
position: 25,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_COMPANIES]: {
|
||||
position: 26,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 27,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_SETTINGS]: {
|
||||
position: 28,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_TASKS]: {
|
||||
position: 29,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_NOTES]: {
|
||||
position: 30,
|
||||
},
|
||||
},
|
||||
});
|
||||
+843
@@ -0,0 +1,843 @@
|
||||
import { CommandLink } from '@/command-menu-item/display/components/CommandLink';
|
||||
import { DeleteMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/DeleteMultipleRecordsCommand';
|
||||
import { DestroyMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/DestroyMultipleRecordsCommand';
|
||||
import { ExportMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/ExportMultipleRecordsCommand';
|
||||
import { MergeMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/MergeMultipleRecordsCommand';
|
||||
import { RestoreMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/RestoreMultipleRecordsCommand';
|
||||
import { UpdateMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/UpdateMultipleRecordsCommand';
|
||||
import { MultipleRecordsCommandKeys } from '@/command-menu-item/record/multiple-records/types/MultipleRecordsCommandKeys';
|
||||
import { CreateNewIndexRecordNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/CreateNewIndexRecordNoSelectionRecordCommand';
|
||||
import { CreateNewViewNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/CreateNewViewNoSelectionRecordCommand';
|
||||
import { HideDeletedRecordsNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/HideDeletedRecordsNoSelectionRecordCommand';
|
||||
import { ImportRecordsNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/ImportRecordsNoSelectionRecordCommand';
|
||||
import { SeeDeletedRecordsNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/SeeDeletedRecordsNoSelectionRecordCommand';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { AddToFavoritesSingleRecordCommand } from '@/command-menu-item/record/single-record/components/AddToFavoritesSingleRecordCommand';
|
||||
import { DeleteSingleRecordCommand } from '@/command-menu-item/record/single-record/components/DeleteSingleRecordCommand';
|
||||
import { DestroySingleRecordCommand } from '@/command-menu-item/record/single-record/components/DestroySingleRecordCommand';
|
||||
import { ExportNoteSingleRecordCommand } from '@/command-menu-item/record/single-record/components/ExportNoteSingleRecordCommand';
|
||||
import { ExportSingleRecordCommand } from '@/command-menu-item/record/single-record/components/ExportSingleRecordCommand';
|
||||
import { NavigateToNextRecordSingleRecordCommand } from '@/command-menu-item/record/single-record/components/NavigateToNextRecordSingleRecordCommand';
|
||||
import { NavigateToPreviousRecordSingleRecordCommand } from '@/command-menu-item/record/single-record/components/NavigateToPreviousRecordSingleRecordCommand';
|
||||
import { RemoveFromFavoritesSingleRecordCommand } from '@/command-menu-item/record/single-record/components/RemoveFromFavoritesSingleRecordCommand';
|
||||
import { RestoreSingleRecordCommand } from '@/command-menu-item/record/single-record/components/RestoreSingleRecordCommand';
|
||||
import { CancelRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/CancelRecordPageLayoutSingleRecordCommand';
|
||||
import { EditRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/EditRecordPageLayoutSingleRecordCommand';
|
||||
import { SaveRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/SaveRecordPageLayoutSingleRecordCommand';
|
||||
import { RecordPageLayoutSingleRecordCommandKeys } from '@/command-menu-item/record/single-record/record-page-layout/types/RecordPageLayoutSingleRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import {
|
||||
CommandMenuItemViewType,
|
||||
CoreObjectNameSingular,
|
||||
AppPath,
|
||||
SettingsPath,
|
||||
} from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import {
|
||||
BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
MUTATION_MAX_MERGE_RECORDS,
|
||||
} from 'twenty-shared/constants';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import {
|
||||
IconArrowMerge,
|
||||
IconBuildingSkyscraper,
|
||||
IconCancel,
|
||||
IconCheckbox,
|
||||
IconChevronDown,
|
||||
IconChevronUp,
|
||||
IconDeviceFloppy,
|
||||
IconEdit,
|
||||
IconEyeOff,
|
||||
IconFileExport,
|
||||
IconFileImport,
|
||||
IconHeart,
|
||||
IconHeartOff,
|
||||
IconLayout,
|
||||
IconLayoutDashboard,
|
||||
IconPencil,
|
||||
IconPlus,
|
||||
IconRefresh,
|
||||
IconRotate2,
|
||||
IconSettings,
|
||||
IconSettingsAutomation,
|
||||
IconTargetArrow,
|
||||
IconTrash,
|
||||
IconTrashX,
|
||||
IconUser,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
PermissionFlagType,
|
||||
FeatureFlagKey,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const DEFAULT_RECORD_COMMAND_MENU_ITEMS_CONFIG: Record<
|
||||
| NoSelectionRecordCommandKeys
|
||||
| SingleRecordCommandKeys
|
||||
| MultipleRecordsCommandKeys
|
||||
| RecordPageLayoutSingleRecordCommandKeys,
|
||||
CommandMenuItemConfig
|
||||
> = {
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
label: msg`Navigate to next record`,
|
||||
position: 0,
|
||||
isPinned: true,
|
||||
Icon: IconChevronDown,
|
||||
shouldBeRegistered: ({ isInSidePanel }) => !isInSidePanel,
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <NavigateToNextRecordSingleRecordCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
label: msg`Navigate to previous record`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
Icon: IconChevronUp,
|
||||
shouldBeRegistered: ({ isInSidePanel }) => !isInSidePanel,
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <NavigateToPreviousRecordSingleRecordCommand />,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.CREATE_NEW_RECORD]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Object,
|
||||
key: NoSelectionRecordCommandKeys.CREATE_NEW_RECORD,
|
||||
label: msg`Create new record`,
|
||||
shortLabel: msg`New record`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
Icon: IconPlus,
|
||||
shouldBeRegistered: ({ objectPermissions, hasAnySoftDeleteFilterOnView }) =>
|
||||
(objectPermissions.canUpdateObjectRecords &&
|
||||
!hasAnySoftDeleteFilterOnView) ??
|
||||
false,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <CreateNewIndexRecordNoSelectionRecordCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.DELETE]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.DELETE,
|
||||
label: msg`Delete`,
|
||||
shortLabel: msg`Delete`,
|
||||
position: 3,
|
||||
Icon: IconTrash,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
objectPermissions,
|
||||
}) =>
|
||||
(isDefined(selectedRecord) &&
|
||||
!selectedRecord.isRemote &&
|
||||
!hasAnySoftDeleteFilterOnView &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isDefined(selectedRecord?.deletedAt)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <DeleteSingleRecordCommand />,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.DELETE]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: MultipleRecordsCommandKeys.DELETE,
|
||||
label: msg`Delete records`,
|
||||
shortLabel: msg`Delete`,
|
||||
position: 4,
|
||||
Icon: IconTrash,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
(objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
!hasAnySoftDeleteFilterOnView &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <DeleteMultipleRecordsCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.RESTORE]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.RESTORE,
|
||||
label: msg`Restore record`,
|
||||
shortLabel: msg`Restore`,
|
||||
position: 5,
|
||||
Icon: IconRefresh,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
isShowPage,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
}) =>
|
||||
(!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
((isDefined(isShowPage) && isShowPage) ||
|
||||
(isDefined(hasAnySoftDeleteFilterOnView) &&
|
||||
hasAnySoftDeleteFilterOnView))) ??
|
||||
false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <RestoreSingleRecordCommand />,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.RESTORE]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: MultipleRecordsCommandKeys.RESTORE,
|
||||
label: msg`Restore records`,
|
||||
shortLabel: msg`Restore`,
|
||||
position: 6,
|
||||
Icon: IconRefresh,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
(objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(hasAnySoftDeleteFilterOnView) &&
|
||||
hasAnySoftDeleteFilterOnView &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <RestoreMultipleRecordsCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.DESTROY]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.DESTROY,
|
||||
label: msg`Permanently destroy record`,
|
||||
shortLabel: msg`Destroy`,
|
||||
position: 7,
|
||||
Icon: IconTrashX,
|
||||
accent: 'danger',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions, isRemote }) =>
|
||||
(objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <DestroySingleRecordCommand />,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.DESTROY]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: MultipleRecordsCommandKeys.DESTROY,
|
||||
label: msg`Permanently destroy records`,
|
||||
shortLabel: msg`Destroy`,
|
||||
position: 8,
|
||||
Icon: IconTrashX,
|
||||
accent: 'danger',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({
|
||||
objectPermissions,
|
||||
isRemote,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
(objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(hasAnySoftDeleteFilterOnView) &&
|
||||
hasAnySoftDeleteFilterOnView &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <DestroyMultipleRecordsCommand />,
|
||||
},
|
||||
|
||||
[SingleRecordCommandKeys.ADD_TO_FAVORITES]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
label: msg`Add to favorites`,
|
||||
shortLabel: msg`Add to favorites`,
|
||||
position: 9,
|
||||
isPinned: true,
|
||||
Icon: IconHeart,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
isFavorite,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
}) =>
|
||||
!selectedRecord?.isRemote &&
|
||||
!isFavorite &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <AddToFavoritesSingleRecordCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.REMOVE_FROM_FAVORITES]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.REMOVE_FROM_FAVORITES,
|
||||
label: msg`Remove from favorites`,
|
||||
shortLabel: msg`Remove from favorites`,
|
||||
isPinned: true,
|
||||
position: 10,
|
||||
Icon: IconHeartOff,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
isFavorite,
|
||||
hasAnySoftDeleteFilterOnView,
|
||||
}) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
isDefined(isFavorite) &&
|
||||
isFavorite &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
component: <RemoveFromFavoritesSingleRecordCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_NOTE_TO_PDF]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.EXPORT_NOTE_TO_PDF,
|
||||
label: msg`Export to PDF`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 11,
|
||||
isPinned: false,
|
||||
Icon: IconFileExport,
|
||||
shouldBeRegistered: ({ selectedRecord, isNoteOrTask }) =>
|
||||
isDefined(isNoteOrTask) &&
|
||||
isNoteOrTask &&
|
||||
isNonEmptyString(selectedRecord?.bodyV2?.blocknote),
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <ExportNoteSingleRecordCommand />,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 12,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
|
||||
component: <ExportMultipleRecordsCommand />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 13,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <ExportSingleRecordCommand />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.UPDATE]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: MultipleRecordsCommandKeys.UPDATE,
|
||||
label: msg`Update records`,
|
||||
shortLabel: msg`Update`,
|
||||
position: 14,
|
||||
Icon: IconEdit,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ objectPermissions, isRemote }) =>
|
||||
objectPermissions.canUpdateObjectRecords && !isRemote,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <UpdateMultipleRecordsCommand />,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.MERGE]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: MultipleRecordsCommandKeys.MERGE,
|
||||
label: msg`Merge records`,
|
||||
shortLabel: msg`Merge`,
|
||||
position: 15,
|
||||
Icon: IconArrowMerge,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
numberOfSelectedRecords,
|
||||
objectPermissions,
|
||||
}) =>
|
||||
isDefined(objectMetadataItem?.duplicateCriteria) &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
Boolean(objectPermissions.canUpdateObjectRecords) &&
|
||||
Boolean(objectPermissions.canDestroyObjectRecords) &&
|
||||
numberOfSelectedRecords <= MUTATION_MAX_MERGE_RECORDS,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <MergeMultipleRecordsCommand />,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.EXPORT]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
key: MultipleRecordsCommandKeys.EXPORT,
|
||||
label: msg`Export records`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 16,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <ExportMultipleRecordsCommand />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.IMPORT_RECORDS]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Object,
|
||||
key: NoSelectionRecordCommandKeys.IMPORT_RECORDS,
|
||||
label: msg`Import records`,
|
||||
shortLabel: msg`Import`,
|
||||
position: 17,
|
||||
Icon: IconFileImport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <ImportRecordsNoSelectionRecordCommand />,
|
||||
requiredPermissionFlag: PermissionFlagType.IMPORT_CSV,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.EXPORT_VIEW]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Object,
|
||||
key: NoSelectionRecordCommandKeys.EXPORT_VIEW,
|
||||
label: msg`Export view`,
|
||||
shortLabel: msg`Export`,
|
||||
position: 18,
|
||||
Icon: IconFileExport,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <ExportMultipleRecordsCommand />,
|
||||
requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Object,
|
||||
key: NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS,
|
||||
label: msg`See deleted records`,
|
||||
shortLabel: msg`Deleted records`,
|
||||
position: 19,
|
||||
Icon: IconRotate2,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <SeeDeletedRecordsNoSelectionRecordCommand />,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.CREATE_NEW_VIEW]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Object,
|
||||
key: NoSelectionRecordCommandKeys.CREATE_NEW_VIEW,
|
||||
label: msg`Create View`,
|
||||
shortLabel: msg`Create View`,
|
||||
position: 20,
|
||||
Icon: IconLayout,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <CreateNewViewNoSelectionRecordCommand />,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS]: {
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.Object,
|
||||
key: NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS,
|
||||
label: msg`Hide deleted records`,
|
||||
shortLabel: msg`Hide deleted`,
|
||||
position: 21,
|
||||
Icon: IconEyeOff,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
isDefined(hasAnySoftDeleteFilterOnView) && hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <HideDeletedRecordsNoSelectionRecordCommand />,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS,
|
||||
label: msg`Go to Workflows`,
|
||||
shortLabel: msg`See Workflows`,
|
||||
position: 22,
|
||||
Icon: IconSettingsAutomation,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Workflow) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Workflow ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Workflow }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'W'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_PEOPLE]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
label: msg`Go to People`,
|
||||
shortLabel: msg`People`,
|
||||
position: 23,
|
||||
Icon: IconUser,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Person) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Person ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Person }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'P'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_COMPANIES]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_COMPANIES,
|
||||
label: msg`Go to Companies`,
|
||||
shortLabel: msg`Companies`,
|
||||
position: 24,
|
||||
Icon: IconBuildingSkyscraper,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Company) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Company ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Company }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'C'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS,
|
||||
label: msg`Go to Dashboards`,
|
||||
shortLabel: msg`Dashboards`,
|
||||
position: 25,
|
||||
Icon: IconLayoutDashboard,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Dashboard) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Dashboard }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'D'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES,
|
||||
label: msg`Go to Opportunities`,
|
||||
shortLabel: msg`Opportunities`,
|
||||
position: 26,
|
||||
Icon: IconTargetArrow,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Opportunity) &&
|
||||
(objectMetadataItem?.nameSingular !==
|
||||
CoreObjectNameSingular.Opportunity ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Opportunity }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'O'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_SETTINGS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_SETTINGS,
|
||||
label: msg`Go to Settings`,
|
||||
shortLabel: msg`Settings`,
|
||||
position: 27,
|
||||
Icon: IconSettings,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.SettingsCatchAll}
|
||||
params={{
|
||||
'*': SettingsPath.ProfilePage,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'S'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_TASKS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_TASKS,
|
||||
label: msg`Go to Tasks`,
|
||||
shortLabel: msg`Tasks`,
|
||||
position: 28,
|
||||
Icon: IconCheckbox,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Task) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Task ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Task }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'T'],
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_NOTES]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_NOTES,
|
||||
label: msg`Go to Notes`,
|
||||
shortLabel: msg`Notes`,
|
||||
position: 29,
|
||||
Icon: IconCheckbox,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.PAGE_EDIT_MODE,
|
||||
],
|
||||
shouldBeRegistered: ({
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
getTargetObjectReadPermission,
|
||||
}) =>
|
||||
getTargetObjectReadPermission(CoreObjectNameSingular.Note) &&
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Note ||
|
||||
viewType === CommandMenuItemViewType.SHOW_PAGE),
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Note }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'N'],
|
||||
},
|
||||
|
||||
[RecordPageLayoutSingleRecordCommandKeys.EDIT_RECORD_PAGE_LAYOUT]: {
|
||||
key: RecordPageLayoutSingleRecordCommandKeys.EDIT_RECORD_PAGE_LAYOUT,
|
||||
label: msg`Edit Page Layout`,
|
||||
shortLabel: msg`Edit Layout`,
|
||||
isPinned: true,
|
||||
position: 30,
|
||||
Icon: IconPencil,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
isFeatureFlagEnabled,
|
||||
}) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
|
||||
) &&
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <EditRecordPageLayoutSingleRecordCommand />,
|
||||
},
|
||||
[RecordPageLayoutSingleRecordCommandKeys.SAVE_RECORD_PAGE_LAYOUT]: {
|
||||
key: RecordPageLayoutSingleRecordCommandKeys.SAVE_RECORD_PAGE_LAYOUT,
|
||||
label: msg`Save Page Layout`,
|
||||
shortLabel: msg`Save`,
|
||||
isPinned: true,
|
||||
isPrimaryCTA: true,
|
||||
position: 31,
|
||||
Icon: IconDeviceFloppy,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
isFeatureFlagEnabled,
|
||||
}) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
|
||||
) &&
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
|
||||
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
|
||||
component: <SaveRecordPageLayoutSingleRecordCommand />,
|
||||
},
|
||||
[RecordPageLayoutSingleRecordCommandKeys.CANCEL_RECORD_PAGE_LAYOUT_EDITION]: {
|
||||
key: RecordPageLayoutSingleRecordCommandKeys.CANCEL_RECORD_PAGE_LAYOUT_EDITION,
|
||||
label: msg`Cancel Edition`,
|
||||
shortLabel: msg`Cancel`,
|
||||
isPinned: true,
|
||||
position: 32,
|
||||
Icon: IconCancel,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
|
||||
shouldBeRegistered: ({
|
||||
selectedRecord,
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
isFeatureFlagEnabled,
|
||||
}) =>
|
||||
isFeatureFlagEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
|
||||
) &&
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord?.isRemote &&
|
||||
!isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
|
||||
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
|
||||
component: <CancelRecordPageLayoutSingleRecordCommand />,
|
||||
},
|
||||
};
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
import { CommandLink } from '@/command-menu-item/display/components/CommandLink';
|
||||
import { MultipleRecordsCommandKeys } from '@/command-menu-item/record/multiple-records/types/MultipleRecordsCommandKeys';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { NoSelectionWorkflowRecordCommandKeys } from '@/command-menu-item/record/no-selection/workflow/types/NoSelectionWorkflowRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { ActivateWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/ActivateWorkflowSingleRecordCommand';
|
||||
import { AddNodeWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/AddNodeWorkflowSingleRecordCommand';
|
||||
import { DeactivateWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/DeactivateWorkflowSingleRecordCommand';
|
||||
import { DiscardDraftWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/DiscardDraftWorkflowSingleRecordCommand';
|
||||
import { DuplicateWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/DuplicateWorkflowSingleRecordCommand';
|
||||
import { SeeActiveVersionWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/SeeActiveVersionWorkflowSingleRecordCommand';
|
||||
import { SeeRunsWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/SeeRunsWorkflowSingleRecordCommand';
|
||||
import { SeeVersionsWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/SeeVersionsWorkflowSingleRecordCommand';
|
||||
import { TestWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/TestWorkflowSingleRecordCommand';
|
||||
import { TidyUpWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/TidyUpWorkflowSingleRecordCommand';
|
||||
import { WorkflowSingleRecordCommandKeys } from '@/command-menu-item/record/single-record/workflow/types/WorkflowSingleRecordCommandKeys';
|
||||
import { inheritCommandMenuItemsFromDefaultConfig } from '@/command-menu-item/record/utils/inheritCommandMenuItemsFromDefaultConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuItemViewType, AppPath } from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import {
|
||||
type WorkflowStep,
|
||||
type WorkflowTrigger,
|
||||
type WorkflowWithCurrentVersion,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconCopy,
|
||||
IconHistoryToggle,
|
||||
IconNoteOff,
|
||||
IconPlayerPause,
|
||||
IconPlayerPlay,
|
||||
IconPlus,
|
||||
IconPower,
|
||||
IconReorder,
|
||||
IconVersions,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
const areWorkflowTriggerAndStepsDefined = (
|
||||
workflowWithCurrentVersion: WorkflowWithCurrentVersion | undefined,
|
||||
): workflowWithCurrentVersion is WorkflowWithCurrentVersion & {
|
||||
currentVersion: {
|
||||
trigger: WorkflowTrigger;
|
||||
steps: Array<WorkflowStep>;
|
||||
};
|
||||
} => {
|
||||
return (
|
||||
isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) &&
|
||||
isDefined(workflowWithCurrentVersion.currentVersion?.steps) &&
|
||||
workflowWithCurrentVersion.currentVersion.steps.length > 0
|
||||
);
|
||||
};
|
||||
|
||||
export const WORKFLOW_COMMAND_MENU_ITEMS_CONFIG =
|
||||
inheritCommandMenuItemsFromDefaultConfig({
|
||||
config: {
|
||||
[WorkflowSingleRecordCommandKeys.ACTIVATE]: {
|
||||
key: WorkflowSingleRecordCommandKeys.ACTIVATE,
|
||||
label: msg`Activate Workflow`,
|
||||
shortLabel: msg`Activate`,
|
||||
isPinned: true,
|
||||
isPrimaryCTA: true,
|
||||
position: 3,
|
||||
Icon: IconPower,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
(workflowWithCurrentVersion.currentVersion.status === 'DRAFT' ||
|
||||
!workflowWithCurrentVersion.versions?.some(
|
||||
(version) => version.status === 'ACTIVE',
|
||||
)) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <ActivateWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.DEACTIVATE]: {
|
||||
key: WorkflowSingleRecordCommandKeys.DEACTIVATE,
|
||||
label: msg`Deactivate Workflow`,
|
||||
shortLabel: msg`Deactivate`,
|
||||
isPinned: true,
|
||||
position: 4,
|
||||
Icon: IconPlayerPause,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE' &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <DeactivateWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.DISCARD_DRAFT]: {
|
||||
key: WorkflowSingleRecordCommandKeys.DISCARD_DRAFT,
|
||||
label: msg`Discard Draft`,
|
||||
shortLabel: msg`Discard Draft`,
|
||||
isPinned: true,
|
||||
position: 5,
|
||||
Icon: IconNoteOff,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.versions.length > 1 &&
|
||||
workflowWithCurrentVersion.currentVersion.status === 'DRAFT' &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <DiscardDraftWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.TEST]: {
|
||||
key: WorkflowSingleRecordCommandKeys.TEST,
|
||||
label: msg`Test Workflow`,
|
||||
shortLabel: msg`Test`,
|
||||
isPinned: true,
|
||||
position: 6,
|
||||
Icon: IconPlayerPlay,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
((workflowWithCurrentVersion.currentVersion.trigger.type ===
|
||||
'MANUAL' &&
|
||||
!isDefined(
|
||||
workflowWithCurrentVersion.currentVersion.trigger.settings
|
||||
.objectType,
|
||||
)) ||
|
||||
workflowWithCurrentVersion.currentVersion.trigger.type ===
|
||||
'WEBHOOK' ||
|
||||
workflowWithCurrentVersion.currentVersion.trigger.type ===
|
||||
'CRON') &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <TestWorkflowSingleRecordCommand />,
|
||||
},
|
||||
|
||||
[WorkflowSingleRecordCommandKeys.SEE_ACTIVE_VERSION]: {
|
||||
key: WorkflowSingleRecordCommandKeys.SEE_ACTIVE_VERSION,
|
||||
label: msg`See active version`,
|
||||
shortLabel: msg`See active version`,
|
||||
isPinned: false,
|
||||
position: 7,
|
||||
Icon: IconVersions,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion, selectedRecord }) =>
|
||||
(workflowWithCurrentVersion?.statuses?.includes('ACTIVE') || false) &&
|
||||
(workflowWithCurrentVersion?.statuses?.includes('DRAFT') || false) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeActiveVersionWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.SEE_RUNS]: {
|
||||
key: WorkflowSingleRecordCommandKeys.SEE_RUNS,
|
||||
label: msg`See runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
isPinned: true,
|
||||
position: 8,
|
||||
Icon: IconHistoryToggle,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeRunsWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.SEE_VERSIONS]: {
|
||||
key: WorkflowSingleRecordCommandKeys.SEE_VERSIONS,
|
||||
label: msg`See versions history`,
|
||||
shortLabel: msg`See versions`,
|
||||
isPinned: false,
|
||||
position: 9,
|
||||
Icon: IconVersions,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeVersionsWorkflowSingleRecordCommand />,
|
||||
},
|
||||
|
||||
[WorkflowSingleRecordCommandKeys.ADD_NODE]: {
|
||||
key: WorkflowSingleRecordCommandKeys.ADD_NODE,
|
||||
label: msg`Add a node`,
|
||||
shortLabel: msg`Add a node`,
|
||||
isPinned: true,
|
||||
position: 10,
|
||||
Icon: IconPlus,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <AddNodeWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.TIDY_UP]: {
|
||||
key: WorkflowSingleRecordCommandKeys.TIDY_UP,
|
||||
label: msg`Tidy up workflow`,
|
||||
shortLabel: msg`Tidy up`,
|
||||
isPinned: false,
|
||||
position: 11,
|
||||
Icon: IconReorder,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
areWorkflowTriggerAndStepsDefined(workflowWithCurrentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
|
||||
component: <TidyUpWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowSingleRecordCommandKeys.DUPLICATE_WORKFLOW]: {
|
||||
key: WorkflowSingleRecordCommandKeys.DUPLICATE_WORKFLOW,
|
||||
label: msg`Duplicate Workflow`,
|
||||
shortLabel: msg`Duplicate`,
|
||||
isPinned: false,
|
||||
position: 12,
|
||||
Icon: IconCopy,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
isDefined(workflowWithCurrentVersion.currentVersion) &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <DuplicateWorkflowSingleRecordCommand />,
|
||||
},
|
||||
[NoSelectionWorkflowRecordCommandKeys.GO_TO_RUNS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionWorkflowRecordCommandKeys.GO_TO_RUNS,
|
||||
label: msg`Go to runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
position: 22,
|
||||
Icon: IconHistoryToggle,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
|
||||
!hasAnySoftDeleteFilterOnView,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
commandKeys: [
|
||||
NoSelectionRecordCommandKeys.CREATE_NEW_RECORD,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordCommandKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordCommandKeys.DELETE,
|
||||
SingleRecordCommandKeys.DESTROY,
|
||||
SingleRecordCommandKeys.RESTORE,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsCommandKeys.DELETE,
|
||||
MultipleRecordsCommandKeys.DESTROY,
|
||||
MultipleRecordsCommandKeys.RESTORE,
|
||||
MultipleRecordsCommandKeys.EXPORT,
|
||||
NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordCommandKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 0,
|
||||
label: msg`Navigate to next workflow`,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 1,
|
||||
label: msg`Navigate to previous workflow`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.CREATE_NEW_RECORD]: {
|
||||
position: 2,
|
||||
label: msg`Create new workflow`,
|
||||
},
|
||||
[SingleRecordCommandKeys.DELETE]: {
|
||||
position: 12,
|
||||
label: msg`Delete workflow`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.DELETE]: {
|
||||
position: 13,
|
||||
label: msg`Delete workflows`,
|
||||
},
|
||||
[SingleRecordCommandKeys.ADD_TO_FAVORITES]: {
|
||||
position: 14,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordCommandKeys.REMOVE_FROM_FAVORITES]: {
|
||||
position: 15,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordCommandKeys.DESTROY]: {
|
||||
position: 16,
|
||||
label: msg`Permanently destroy workflow`,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 17,
|
||||
label: msg`Export workflow`,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 18,
|
||||
label: msg`Export workflow`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.EXPORT]: {
|
||||
position: 19,
|
||||
label: msg`Export workflows`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.EXPORT_VIEW]: {
|
||||
position: 20,
|
||||
label: msg`Export view`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.DESTROY]: {
|
||||
position: 21,
|
||||
label: msg`Permanently destroy workflows`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 22,
|
||||
label: msg`See deleted workflows`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 23,
|
||||
label: msg`Hide deleted workflows`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.IMPORT_RECORDS]: {
|
||||
position: 24,
|
||||
label: msg`Import workflows`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_PEOPLE]: {
|
||||
position: 25,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_COMPANIES]: {
|
||||
position: 26,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 27,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 28,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_SETTINGS]: {
|
||||
position: 29,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_TASKS]: {
|
||||
position: 30,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_NOTES]: {
|
||||
position: 31,
|
||||
},
|
||||
},
|
||||
});
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
import { MultipleRecordsCommandKeys } from '@/command-menu-item/record/multiple-records/types/MultipleRecordsCommandKeys';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { SeeVersionWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/SeeVersionWorkflowRunSingleRecordCommand';
|
||||
import { SeeWorkflowWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/SeeWorkflowWorkflowRunSingleRecordCommand';
|
||||
import { StopWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/StopWorkflowRunSingleRecordCommand';
|
||||
import { WorkflowRunSingleRecordCommandKeys } from '@/command-menu-item/record/single-record/workflow-runs/types/WorkflowRunSingleRecordCommandKeys';
|
||||
import { inheritCommandMenuItemsFromDefaultConfig } from '@/command-menu-item/record/utils/inheritCommandMenuItemsFromDefaultConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuItemViewType } from 'twenty-shared/types';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
IconPlayerStop,
|
||||
IconSettingsAutomation,
|
||||
IconVersions,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const WORKFLOW_RUNS_COMMAND_MENU_ITEMS_CONFIG =
|
||||
inheritCommandMenuItemsFromDefaultConfig({
|
||||
config: {
|
||||
[WorkflowRunSingleRecordCommandKeys.SEE_VERSION]: {
|
||||
key: WorkflowRunSingleRecordCommandKeys.SEE_VERSION,
|
||||
label: msg`See version`,
|
||||
shortLabel: msg`See version`,
|
||||
position: 0,
|
||||
isPinned: true,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconVersions,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeVersionWorkflowRunSingleRecordCommand />,
|
||||
},
|
||||
|
||||
[WorkflowRunSingleRecordCommandKeys.SEE_WORKFLOW]: {
|
||||
key: WorkflowRunSingleRecordCommandKeys.SEE_WORKFLOW,
|
||||
label: msg`See workflow`,
|
||||
shortLabel: msg`See workflow`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconSettingsAutomation,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeWorkflowWorkflowRunSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowRunSingleRecordCommandKeys.STOP]: {
|
||||
key: WorkflowRunSingleRecordCommandKeys.STOP,
|
||||
label: msg`Stop`,
|
||||
shortLabel: msg`Stop`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconPlayerStop,
|
||||
shouldBeRegistered: ({ selectedRecord, isSelectAll }) => {
|
||||
if (isSelectAll === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const stoppableStatuses = ['NOT_STARTED', 'ENQUEUED', 'RUNNING'];
|
||||
return stoppableStatuses.includes(selectedRecord?.status);
|
||||
},
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
],
|
||||
component: <StopWorkflowRunSingleRecordCommand />,
|
||||
},
|
||||
},
|
||||
commandKeys: [
|
||||
SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordCommandKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsCommandKeys.EXPORT,
|
||||
NoSelectionRecordCommandKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordCommandKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordCommandKeys.ADD_TO_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 3,
|
||||
},
|
||||
[SingleRecordCommandKeys.REMOVE_FROM_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 4,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 5,
|
||||
label: msg`Export run`,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 5,
|
||||
label: msg`Export run`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.EXPORT]: {
|
||||
position: 6,
|
||||
label: msg`Export runs`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.EXPORT_VIEW]: {
|
||||
position: 7,
|
||||
label: msg`Export view`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 8,
|
||||
label: msg`See deleted runs`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 9,
|
||||
label: msg`Hide deleted runs`,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 10,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 11,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 12,
|
||||
isPinned: true,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_PEOPLE]: {
|
||||
position: 13,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_COMPANIES]: {
|
||||
position: 14,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 15,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 16,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_SETTINGS]: {
|
||||
position: 17,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_TASKS]: {
|
||||
position: 18,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_NOTES]: {
|
||||
position: 19,
|
||||
},
|
||||
},
|
||||
});
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
import { CommandLink } from '@/command-menu-item/display/components/CommandLink';
|
||||
import { MultipleRecordsCommandKeys } from '@/command-menu-item/record/multiple-records/types/MultipleRecordsCommandKeys';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { NoSelectionWorkflowRecordCommandKeys } from '@/command-menu-item/record/no-selection/workflow/types/NoSelectionWorkflowRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { SeeRunsWorkflowVersionSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-versions/components/SeeRunsWorkflowVersionSingleRecordCommand';
|
||||
import { SeeVersionsWorkflowVersionSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-versions/components/SeeVersionsWorkflowVersionSingleRecordCommand';
|
||||
import { SeeWorkflowWorkflowVersionSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-versions/components/SeeWorkflowWorkflowVersionSingleRecordCommand';
|
||||
import { UseAsDraftWorkflowVersionSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-versions/components/UseAsDraftWorkflowVersionSingleRecordCommand';
|
||||
import { WorkflowVersionSingleRecordCommandKeys } from '@/command-menu-item/record/single-record/workflow-versions/types/WorkflowVersionSingleRecordCommandKeys';
|
||||
import { inheritCommandMenuItemsFromDefaultConfig } from '@/command-menu-item/record/utils/inheritCommandMenuItemsFromDefaultConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuItemViewType, AppPath } from 'twenty-shared/types';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconHistoryToggle,
|
||||
IconPencil,
|
||||
IconSettingsAutomation,
|
||||
IconVersions,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const WORKFLOW_VERSIONS_COMMAND_MENU_ITEMS_CONFIG =
|
||||
inheritCommandMenuItemsFromDefaultConfig({
|
||||
config: {
|
||||
[WorkflowVersionSingleRecordCommandKeys.SEE_RUNS]: {
|
||||
key: WorkflowVersionSingleRecordCommandKeys.SEE_RUNS,
|
||||
label: msg`See runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconHistoryToggle,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeRunsWorkflowVersionSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordCommandKeys.SEE_WORKFLOW]: {
|
||||
key: WorkflowVersionSingleRecordCommandKeys.SEE_WORKFLOW,
|
||||
label: msg`See workflow`,
|
||||
shortLabel: msg`See workflow`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconSettingsAutomation,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord?.workflow?.id),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeWorkflowWorkflowVersionSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordCommandKeys.USE_AS_DRAFT]: {
|
||||
key: WorkflowVersionSingleRecordCommandKeys.USE_AS_DRAFT,
|
||||
label: msg`Use as draft`,
|
||||
shortLabel: msg`Use as draft`,
|
||||
position: 3,
|
||||
isPinned: true,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconPencil,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && selectedRecord.status !== 'DRAFT',
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <UseAsDraftWorkflowVersionSingleRecordCommand />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordCommandKeys.SEE_VERSIONS]: {
|
||||
key: WorkflowVersionSingleRecordCommandKeys.SEE_VERSIONS,
|
||||
label: msg`See versions history`,
|
||||
shortLabel: msg`See versions`,
|
||||
position: 4,
|
||||
type: CommandMenuItemType.Standard,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
Icon: IconVersions,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
component: <SeeVersionsWorkflowVersionSingleRecordCommand />,
|
||||
},
|
||||
[NoSelectionWorkflowRecordCommandKeys.GO_TO_RUNS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionWorkflowRecordCommandKeys.GO_TO_RUNS,
|
||||
label: msg`Go to runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
position: 14,
|
||||
Icon: IconHistoryToggle,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
commandKeys: [
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordCommandKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsCommandKeys.EXPORT,
|
||||
NoSelectionRecordCommandKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordCommandKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordCommandKeys.ADD_TO_FAVORITES]: {
|
||||
position: 5,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordCommandKeys.REMOVE_FROM_FAVORITES]: {
|
||||
position: 6,
|
||||
isPinned: false,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 7,
|
||||
label: msg`Export version`,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 7,
|
||||
label: msg`Export version`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.EXPORT]: {
|
||||
position: 8,
|
||||
label: msg`Export versions`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.EXPORT_VIEW]: {
|
||||
position: 9,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 10,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 11,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 12,
|
||||
label: msg`Navigate to previous version`,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 13,
|
||||
label: msg`Navigate to next version`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 15,
|
||||
isPinned: true,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_PEOPLE]: {
|
||||
position: 16,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_COMPANIES]: {
|
||||
position: 17,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 18,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 19,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_SETTINGS]: {
|
||||
position: 20,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_TASKS]: {
|
||||
position: 21,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_NOTES]: {
|
||||
position: 22,
|
||||
},
|
||||
},
|
||||
});
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
import { CommandLink } from '@/command-menu-item/display/components/CommandLink';
|
||||
import { MultipleRecordsCommandKeys } from '@/command-menu-item/record/multiple-records/types/MultipleRecordsCommandKeys';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { inheritCommandMenuItemsFromDefaultConfig } from '@/command-menu-item/record/utils/inheritCommandMenuItemsFromDefaultConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import {
|
||||
CommandMenuItemViewType,
|
||||
AppPath,
|
||||
SettingsPath,
|
||||
} from 'twenty-shared/types';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { IconSettings } from 'twenty-ui/display';
|
||||
|
||||
export const WORKSPACE_MEMBERS_COMMAND_MENU_ITEMS_CONFIG =
|
||||
inheritCommandMenuItemsFromDefaultConfig({
|
||||
config: {
|
||||
[NoSelectionRecordCommandKeys.GO_TO_SETTINGS]: {
|
||||
type: CommandMenuItemType.Navigation,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
key: NoSelectionRecordCommandKeys.GO_TO_SETTINGS,
|
||||
label: msg`Manage members in settings`,
|
||||
shortLabel: msg`Manage in settings`,
|
||||
position: 14,
|
||||
Icon: IconSettings,
|
||||
isPinned: true,
|
||||
availableOn: [
|
||||
CommandMenuItemViewType.INDEX_PAGE_NO_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
CommandMenuItemViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
CommandMenuItemViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
component: (
|
||||
<CommandLink
|
||||
to={AppPath.SettingsCatchAll}
|
||||
params={{
|
||||
'*': SettingsPath.WorkspaceMembersPage,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'S'],
|
||||
},
|
||||
},
|
||||
commandKeys: [
|
||||
SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
SingleRecordCommandKeys.REMOVE_FROM_FAVORITES,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX,
|
||||
SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW,
|
||||
MultipleRecordsCommandKeys.EXPORT,
|
||||
NoSelectionRecordCommandKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordCommandKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordCommandKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordCommandKeys.ADD_TO_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 0,
|
||||
},
|
||||
[SingleRecordCommandKeys.REMOVE_FROM_FAVORITES]: {
|
||||
isPinned: false,
|
||||
position: 1,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||
position: 2,
|
||||
label: msg`Export member`,
|
||||
},
|
||||
[SingleRecordCommandKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||
position: 2,
|
||||
label: msg`Export member`,
|
||||
},
|
||||
[MultipleRecordsCommandKeys.EXPORT]: {
|
||||
position: 3,
|
||||
label: msg`Export members`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.EXPORT_VIEW]: {
|
||||
position: 4,
|
||||
label: msg`Export view`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.SEE_DELETED_RECORDS]: {
|
||||
position: 5,
|
||||
label: msg`See deleted members`,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.HIDE_DELETED_RECORDS]: {
|
||||
position: 6,
|
||||
label: msg`Hide deleted members`,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
position: 7,
|
||||
},
|
||||
[SingleRecordCommandKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 8,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 9,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_PEOPLE]: {
|
||||
position: 10,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_COMPANIES]: {
|
||||
position: 11,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 12,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_DASHBOARDS]: {
|
||||
position: 13,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_TASKS]: {
|
||||
position: 15,
|
||||
},
|
||||
[NoSelectionRecordCommandKeys.GO_TO_NOTES]: {
|
||||
position: 16,
|
||||
},
|
||||
},
|
||||
});
|
||||
+13
-11
@@ -1,7 +1,7 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { computeProgressText } from '@/action-menu/utils/computeProgressText';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { computeProgressText } from '@/command-menu-item/utils/computeProgressText';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
|
||||
@@ -18,7 +18,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const DeleteMultipleRecordsAction = () => {
|
||||
export const DeleteMultipleRecordsCommand = () => {
|
||||
const { recordIndexId, objectMetadataItem } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
@@ -70,15 +70,17 @@ export const DeleteMultipleRecordsAction = () => {
|
||||
delayInMsBetweenMutations: 50,
|
||||
});
|
||||
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
const actionConfig = useContext(CommandConfigContext);
|
||||
|
||||
if (!isDefined(actionConfig)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalLabel = getActionLabel(actionConfig.label);
|
||||
const originalLabel = getCommandMenuItemLabel(actionConfig.label);
|
||||
|
||||
const originalShortLabel = getActionLabel(actionConfig.shortLabel ?? '');
|
||||
const originalShortLabel = getCommandMenuItemLabel(
|
||||
actionConfig.shortLabel ?? '',
|
||||
);
|
||||
|
||||
const progressText = computeProgressText(progress);
|
||||
|
||||
@@ -95,8 +97,8 @@ export const DeleteMultipleRecordsAction = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionConfigContext.Provider value={actionConfigWithProgress}>
|
||||
<Action onClick={handleDeleteClick} />
|
||||
</ActionConfigContext.Provider>
|
||||
<CommandConfigContext.Provider value={actionConfigWithProgress}>
|
||||
<Command onClick={handleDeleteClick} />
|
||||
</CommandConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
+13
-11
@@ -1,7 +1,7 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { computeProgressText } from '@/action-menu/utils/computeProgressText';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { CommandModal } from '@/command-menu-item/display/components/CommandModal';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { computeProgressText } from '@/command-menu-item/utils/computeProgressText';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
|
||||
@@ -20,7 +20,7 @@ import { useContext } from 'react';
|
||||
import { type RecordGqlOperationFilter } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const DestroyMultipleRecordsAction = () => {
|
||||
export const DestroyMultipleRecordsCommand = () => {
|
||||
const { recordIndexId, objectMetadataItem } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
@@ -77,15 +77,17 @@ export const DestroyMultipleRecordsAction = () => {
|
||||
delayInMsBetweenMutations: 50,
|
||||
});
|
||||
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
const actionConfig = useContext(CommandConfigContext);
|
||||
|
||||
if (!isDefined(actionConfig)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalLabel = getActionLabel(actionConfig.label);
|
||||
const originalLabel = getCommandMenuItemLabel(actionConfig.label);
|
||||
|
||||
const originalShortLabel = getActionLabel(actionConfig.shortLabel ?? '');
|
||||
const originalShortLabel = getCommandMenuItemLabel(
|
||||
actionConfig.shortLabel ?? '',
|
||||
);
|
||||
|
||||
const progressText = computeProgressText(progress);
|
||||
|
||||
@@ -102,13 +104,13 @@ export const DestroyMultipleRecordsAction = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionConfigContext.Provider value={actionConfigWithProgress}>
|
||||
<ActionModal
|
||||
<CommandConfigContext.Provider value={actionConfigWithProgress}>
|
||||
<CommandModal
|
||||
title={t`Permanently Destroy Records`}
|
||||
subtitle={t`Are you sure you want to destroy these records? They won't be recoverable anymore.`}
|
||||
onConfirmClick={handleDestroyClick}
|
||||
confirmButtonText={t`Destroy Records`}
|
||||
/>
|
||||
</ActionConfigContext.Provider>
|
||||
</CommandConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
+17
-15
@@ -1,8 +1,8 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { computeProgressText } from '@/action-menu/utils/computeProgressText';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { computeProgressText } from '@/command-menu-item/utils/computeProgressText';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useRecordIndexExportRecords } from '@/object-record/record-index/export/hooks/useRecordIndexExportRecords';
|
||||
@@ -11,7 +11,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ExportMultipleRecordsAction = () => {
|
||||
export const ExportMultipleRecordsCommand = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useAtomComponentStateValue(
|
||||
@@ -32,7 +32,7 @@ export const ExportMultipleRecordsAction = () => {
|
||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||
});
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu({});
|
||||
const { closeCommandMenu } = useCloseCommandMenu({});
|
||||
|
||||
const exportProgress = isDefined(progress)
|
||||
? {
|
||||
@@ -41,15 +41,17 @@ export const ExportMultipleRecordsAction = () => {
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
const actionConfig = useContext(CommandConfigContext);
|
||||
|
||||
if (!isDefined(actionConfig)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalLabel = getActionLabel(actionConfig.label);
|
||||
const originalLabel = getCommandMenuItemLabel(actionConfig.label);
|
||||
|
||||
const originalShortLabel = getActionLabel(actionConfig.shortLabel ?? '');
|
||||
const originalShortLabel = getCommandMenuItemLabel(
|
||||
actionConfig.shortLabel ?? '',
|
||||
);
|
||||
|
||||
const progressText = computeProgressText(exportProgress);
|
||||
|
||||
@@ -62,16 +64,16 @@ export const ExportMultipleRecordsAction = () => {
|
||||
const handleClick = async () => {
|
||||
try {
|
||||
await download();
|
||||
closeActionMenu();
|
||||
closeCommandMenu();
|
||||
} catch (error) {
|
||||
closeActionMenu();
|
||||
closeCommandMenu();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionConfigContext.Provider value={actionConfigWithProgress}>
|
||||
<ActionDisplay onClick={handleClick} />
|
||||
</ActionConfigContext.Provider>
|
||||
<CommandConfigContext.Provider value={actionConfigWithProgress}>
|
||||
<CommandMenuItemDisplay onClick={handleClick} />
|
||||
</CommandConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { useSelectedRecordIds } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIds';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { useSelectedRecordIds } from '@/command-menu-item/record/single-record/hooks/useSelectedRecordIds';
|
||||
import { useOpenMergeRecordsPageInSidePanel } from '@/side-panel/hooks/useOpenMergeRecordsPageInSidePanel';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
export const MergeMultipleRecordsAction = () => {
|
||||
export const MergeMultipleRecordsCommand = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useAtomComponentStateValue(
|
||||
@@ -27,5 +27,5 @@ export const MergeMultipleRecordsAction = () => {
|
||||
openMergeRecordsPageInSidePanel();
|
||||
};
|
||||
|
||||
return <ActionDisplay onClick={handleClick} />;
|
||||
return <CommandMenuItemDisplay onClick={handleClick} />;
|
||||
};
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { CommandModal } from '@/command-menu-item/display/components/CommandModal';
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
|
||||
@@ -16,7 +16,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type RecordGqlOperationFilter } from 'twenty-shared/types';
|
||||
|
||||
export const RestoreMultipleRecordsAction = () => {
|
||||
export const RestoreMultipleRecordsCommand = () => {
|
||||
const { recordIndexId, objectMetadataItem } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
@@ -90,7 +90,7 @@ export const RestoreMultipleRecordsAction = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionModal
|
||||
<CommandModal
|
||||
title={t`Restore Records`}
|
||||
subtitle={t`Are you sure you want to restore these records?`}
|
||||
onConfirmClick={handleRestoreClick}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user