9260 refactor multiple record actions and no selection actions (#9314)
Closes #9260 - Refactored multiple record actions and no selection record actions to use config file - Simplified actions registration logic - Updated tests
This commit is contained in:
@ -1,13 +1,10 @@
|
|||||||
import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
|
import { RegisterRecordActionEffect } from '@/action-menu/actions/record-actions/components/RegisterRecordActionEffect';
|
||||||
import { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
|
|
||||||
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
|
|
||||||
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
import { getActionConfig } from '@/action-menu/actions/utils/getActionConfig';
|
||||||
|
import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
|
||||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
|
||||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
@ -19,33 +16,13 @@ export const RecordActionMenuEntriesSetter = () => {
|
|||||||
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
|
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
|
||||||
contextStoreCurrentObjectMetadataIdComponentState,
|
contextStoreCurrentObjectMetadataIdComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
const objectMetadataItem = objectMetadataItems.find(
|
const objectMetadataItem = objectMetadataItems.find(
|
||||||
(item) => item.id === contextStoreCurrentObjectMetadataId,
|
(item) => item.id === contextStoreCurrentObjectMetadataId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
|
||||||
!isDefined(contextStoreCurrentObjectMetadataId) ||
|
|
||||||
!isDefined(objectMetadataItem)
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ActionEffects objectMetadataItemId={contextStoreCurrentObjectMetadataId} />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ActionEffects = ({
|
|
||||||
objectMetadataItemId,
|
|
||||||
}: {
|
|
||||||
objectMetadataItemId: string;
|
|
||||||
}) => {
|
|
||||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
|
||||||
objectId: objectMetadataItemId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
contextStoreTargetedRecordsRuleComponentState,
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
);
|
);
|
||||||
@ -58,43 +35,52 @@ const ActionEffects = ({
|
|||||||
FeatureFlagKey.IsWorkflowEnabled,
|
FeatureFlagKey.IsWorkflowEnabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isPageHeaderV2Enabled = useIsFeatureEnabled(
|
||||||
|
FeatureFlagKey.IsPageHeaderV2Enabled,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isDefined(contextStoreCurrentObjectMetadataId) ||
|
||||||
|
!isDefined(objectMetadataItem)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewType = getActionViewType(
|
||||||
|
contextStoreCurrentViewType,
|
||||||
|
contextStoreTargetedRecordsRule,
|
||||||
|
);
|
||||||
|
|
||||||
|
const actionConfig = getActionConfig(
|
||||||
|
objectMetadataItem,
|
||||||
|
isPageHeaderV2Enabled,
|
||||||
|
);
|
||||||
|
|
||||||
|
const actionsToRegister = isDefined(viewType)
|
||||||
|
? Object.values(actionConfig ?? {}).filter((action) =>
|
||||||
|
action.availableOn?.includes(viewType),
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
{actionsToRegister.map((action) => (
|
||||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0 && (
|
<RegisterRecordActionEffect
|
||||||
<NoSelectionActionMenuEntrySetterEffect
|
key={action.key}
|
||||||
|
action={action}
|
||||||
|
objectMetadataItem={objectMetadataItem}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{isWorkflowEnabled &&
|
||||||
|
!(
|
||||||
|
contextStoreTargetedRecordsRule?.mode === 'selection' &&
|
||||||
|
contextStoreTargetedRecordsRule?.selectedRecordIds.length === 0
|
||||||
|
) && (
|
||||||
|
<WorkflowRunRecordActionMenuEntrySetterEffect
|
||||||
objectMetadataItem={objectMetadataItem}
|
objectMetadataItem={objectMetadataItem}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
|
||||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
|
|
||||||
<>
|
|
||||||
{contextStoreCurrentViewType === ContextStoreViewType.ShowPage && (
|
|
||||||
<SingleRecordActionMenuEntrySetterEffect
|
|
||||||
objectMetadataItem={objectMetadataItem}
|
|
||||||
viewType={ActionViewType.SHOW_PAGE}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(contextStoreCurrentViewType === ContextStoreViewType.Table ||
|
|
||||||
contextStoreCurrentViewType === ContextStoreViewType.Kanban) && (
|
|
||||||
<SingleRecordActionMenuEntrySetterEffect
|
|
||||||
objectMetadataItem={objectMetadataItem}
|
|
||||||
viewType={ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isWorkflowEnabled && (
|
|
||||||
<WorkflowRunRecordActionMenuEntrySetterEffect
|
|
||||||
objectMetadataItem={objectMetadataItem}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{(contextStoreTargetedRecordsRule.mode === 'exclusion' ||
|
|
||||||
contextStoreTargetedRecordsRule.selectedRecordIds.length > 1) && (
|
|
||||||
<MultipleRecordsActionMenuEntrySetterEffect
|
|
||||||
objectMetadataItem={objectMetadataItem}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||||
|
import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
|
||||||
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { useContext, useEffect } from 'react';
|
||||||
|
|
||||||
|
type RegisterRecordActionEffectProps = {
|
||||||
|
action: ActionMenuEntry & {
|
||||||
|
actionHook: ActionHook;
|
||||||
|
};
|
||||||
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RegisterRecordActionEffect = ({
|
||||||
|
action,
|
||||||
|
objectMetadataItem,
|
||||||
|
}: RegisterRecordActionEffectProps) => {
|
||||||
|
const { shouldBeRegistered, onClick, ConfirmationModal } = action.actionHook({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { onActionStartedCallback, onActionExecutedCallback } =
|
||||||
|
useContext(ActionMenuContext);
|
||||||
|
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const wrappedAction = wrapActionInCallbacks({
|
||||||
|
action: {
|
||||||
|
...action,
|
||||||
|
onClick,
|
||||||
|
ConfirmationModal,
|
||||||
|
},
|
||||||
|
onActionStartedCallback,
|
||||||
|
onActionExecutedCallback,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldBeRegistered) {
|
||||||
|
addActionMenuEntry(wrappedAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
removeActionMenuEntry(wrappedAction.key);
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
addActionMenuEntry,
|
||||||
|
removeActionMenuEntry,
|
||||||
|
shouldBeRegistered,
|
||||||
|
wrappedAction,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@ -1,20 +1,29 @@
|
|||||||
|
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
||||||
|
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||||
|
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||||
|
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
|
||||||
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||||
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
||||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||||
|
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||||
import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
|
|
||||||
import {
|
import {
|
||||||
ActionMenuEntry,
|
ActionMenuEntry,
|
||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
ActionMenuEntryType,
|
ActionMenuEntryType,
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
import { IconHeart, IconHeartOff, IconTrash } from 'twenty-ui';
|
import {
|
||||||
|
IconDatabaseExport,
|
||||||
|
IconHeart,
|
||||||
|
IconHeartOff,
|
||||||
|
IconTrash,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
|
||||||
export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
|
export const DEFAULT_ACTIONS_CONFIG_V1: Record<
|
||||||
string,
|
string,
|
||||||
ActionMenuEntry & {
|
ActionMenuEntry & {
|
||||||
actionHook: SingleRecordActionHook;
|
actionHook: ActionHook;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
addToFavoritesSingleRecord: {
|
addToFavoritesSingleRecord: {
|
||||||
@ -58,4 +67,43 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
|
|||||||
],
|
],
|
||||||
actionHook: useDeleteSingleRecordAction,
|
actionHook: useDeleteSingleRecordAction,
|
||||||
},
|
},
|
||||||
|
deleteMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.DELETE,
|
||||||
|
label: 'Delete records',
|
||||||
|
shortLabel: 'Delete',
|
||||||
|
position: 3,
|
||||||
|
Icon: IconTrash,
|
||||||
|
accent: 'danger',
|
||||||
|
isPinned: true,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useDeleteMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.EXPORT,
|
||||||
|
label: 'Export records',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 4,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportView: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
|
label: 'Export view',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 5,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
||||||
|
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||||
|
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||||
|
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
|
||||||
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||||
import { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
|
import { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
|
||||||
@ -6,8 +10,8 @@ import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions
|
|||||||
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
||||||
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
||||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||||
|
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||||
import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
|
|
||||||
import {
|
import {
|
||||||
ActionMenuEntry,
|
ActionMenuEntry,
|
||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
@ -16,6 +20,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconChevronUp,
|
IconChevronUp,
|
||||||
|
IconDatabaseExport,
|
||||||
IconFileExport,
|
IconFileExport,
|
||||||
IconHeart,
|
IconHeart,
|
||||||
IconHeartOff,
|
IconHeartOff,
|
||||||
@ -23,10 +28,10 @@ import {
|
|||||||
IconTrashX,
|
IconTrashX,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
|
export const DEFAULT_ACTIONS_CONFIG_V2: Record<
|
||||||
string,
|
string,
|
||||||
ActionMenuEntry & {
|
ActionMenuEntry & {
|
||||||
actionHook: SingleRecordActionHook;
|
actionHook: ActionHook;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
exportNoteToPdf: {
|
exportNoteToPdf: {
|
||||||
@ -87,13 +92,52 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
|
|||||||
],
|
],
|
||||||
actionHook: useDeleteSingleRecordAction,
|
actionHook: useDeleteSingleRecordAction,
|
||||||
},
|
},
|
||||||
|
deleteMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.DELETE,
|
||||||
|
label: 'Delete records',
|
||||||
|
shortLabel: 'Delete',
|
||||||
|
position: 4,
|
||||||
|
Icon: IconTrash,
|
||||||
|
accent: 'danger',
|
||||||
|
isPinned: true,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useDeleteMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.EXPORT,
|
||||||
|
label: 'Export records',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 5,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportView: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
|
label: 'Export view',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 6,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
destroySingleRecord: {
|
destroySingleRecord: {
|
||||||
type: ActionMenuEntryType.Standard,
|
type: ActionMenuEntryType.Standard,
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
key: SingleRecordActionKeys.DESTROY,
|
key: SingleRecordActionKeys.DESTROY,
|
||||||
label: 'Permanently destroy record',
|
label: 'Permanently destroy record',
|
||||||
shortLabel: 'Destroy',
|
shortLabel: 'Destroy',
|
||||||
position: 4,
|
position: 7,
|
||||||
Icon: IconTrashX,
|
Icon: IconTrashX,
|
||||||
accent: 'danger',
|
accent: 'danger',
|
||||||
isPinned: true,
|
isPinned: true,
|
||||||
@ -109,7 +153,7 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
|
|||||||
key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||||
label: 'Navigate to previous record',
|
label: 'Navigate to previous record',
|
||||||
shortLabel: '',
|
shortLabel: '',
|
||||||
position: 5,
|
position: 8,
|
||||||
isPinned: true,
|
isPinned: true,
|
||||||
Icon: IconChevronUp,
|
Icon: IconChevronUp,
|
||||||
availableOn: [ActionViewType.SHOW_PAGE],
|
availableOn: [ActionViewType.SHOW_PAGE],
|
||||||
@ -121,7 +165,7 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
|
|||||||
key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||||
label: 'Navigate to next record',
|
label: 'Navigate to next record',
|
||||||
shortLabel: '',
|
shortLabel: '',
|
||||||
position: 6,
|
position: 9,
|
||||||
isPinned: true,
|
isPinned: true,
|
||||||
Icon: IconChevronDown,
|
Icon: IconChevronDown,
|
||||||
availableOn: [ActionViewType.SHOW_PAGE],
|
availableOn: [ActionViewType.SHOW_PAGE],
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
||||||
|
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||||
|
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||||
|
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
|
||||||
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||||
import { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
|
import { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
|
||||||
@ -14,8 +18,8 @@ import { useSeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/reco
|
|||||||
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
||||||
import { useTestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction';
|
import { useTestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction';
|
||||||
import { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys';
|
import { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys';
|
||||||
|
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||||
import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
|
|
||||||
import {
|
import {
|
||||||
ActionMenuEntry,
|
ActionMenuEntry,
|
||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
@ -24,6 +28,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconChevronUp,
|
IconChevronUp,
|
||||||
|
IconDatabaseExport,
|
||||||
IconHeart,
|
IconHeart,
|
||||||
IconHeartOff,
|
IconHeartOff,
|
||||||
IconHistory,
|
IconHistory,
|
||||||
@ -35,10 +40,10 @@ import {
|
|||||||
IconTrashX,
|
IconTrashX,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
export const WORKFLOW_ACTIONS_CONFIG: Record<
|
||||||
string,
|
string,
|
||||||
ActionMenuEntry & {
|
ActionMenuEntry & {
|
||||||
actionHook: SingleRecordActionHook;
|
actionHook: ActionHook;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
activateWorkflowDraftSingleRecord: {
|
activateWorkflowDraftSingleRecord: {
|
||||||
@ -229,13 +234,26 @@ export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
],
|
],
|
||||||
actionHook: useDeleteSingleRecordAction,
|
actionHook: useDeleteSingleRecordAction,
|
||||||
},
|
},
|
||||||
|
deleteMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.DELETE,
|
||||||
|
label: 'Delete records',
|
||||||
|
shortLabel: 'Delete',
|
||||||
|
position: 14,
|
||||||
|
Icon: IconTrash,
|
||||||
|
accent: 'danger',
|
||||||
|
isPinned: true,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useDeleteMultipleRecordsAction,
|
||||||
|
},
|
||||||
destroySingleRecord: {
|
destroySingleRecord: {
|
||||||
type: ActionMenuEntryType.Standard,
|
type: ActionMenuEntryType.Standard,
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
key: SingleRecordActionKeys.DESTROY,
|
key: SingleRecordActionKeys.DESTROY,
|
||||||
label: 'Permanently destroy record',
|
label: 'Permanently destroy record',
|
||||||
shortLabel: 'Destroy',
|
shortLabel: 'Destroy',
|
||||||
position: 14,
|
position: 15,
|
||||||
Icon: IconTrashX,
|
Icon: IconTrashX,
|
||||||
accent: 'danger',
|
accent: 'danger',
|
||||||
isPinned: false,
|
isPinned: false,
|
||||||
@ -245,4 +263,30 @@ export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
],
|
],
|
||||||
actionHook: useDestroySingleRecordAction,
|
actionHook: useDestroySingleRecordAction,
|
||||||
},
|
},
|
||||||
|
exportMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.EXPORT,
|
||||||
|
label: 'Export records',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 16,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportView: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
|
label: 'Export view',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 17,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
@ -1,10 +1,13 @@
|
|||||||
|
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||||
|
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||||
|
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
|
||||||
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
|
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
|
||||||
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
||||||
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
||||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||||
|
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||||
import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
|
|
||||||
import {
|
import {
|
||||||
ActionMenuEntry,
|
ActionMenuEntry,
|
||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
@ -13,14 +16,15 @@ import {
|
|||||||
import {
|
import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconChevronUp,
|
IconChevronUp,
|
||||||
|
IconDatabaseExport,
|
||||||
IconHeart,
|
IconHeart,
|
||||||
IconHeartOff,
|
IconHeartOff,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
export const WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
export const WORKFLOW_RUNS_ACTIONS_CONFIG: Record<
|
||||||
string,
|
string,
|
||||||
ActionMenuEntry & {
|
ActionMenuEntry & {
|
||||||
actionHook: SingleRecordActionHook;
|
actionHook: ActionHook;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
addToFavoritesSingleRecord: {
|
addToFavoritesSingleRecord: {
|
||||||
@ -77,4 +81,30 @@ export const WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
availableOn: [ActionViewType.SHOW_PAGE],
|
availableOn: [ActionViewType.SHOW_PAGE],
|
||||||
actionHook: useNavigateToNextRecordSingleRecordAction,
|
actionHook: useNavigateToNextRecordSingleRecordAction,
|
||||||
},
|
},
|
||||||
|
exportMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.EXPORT,
|
||||||
|
label: 'Export records',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 4,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportView: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
|
label: 'Export view',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 5,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||||
|
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||||
|
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
|
||||||
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
|
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
|
||||||
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
||||||
@ -7,8 +10,8 @@ import { useSeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actio
|
|||||||
import { useSeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction';
|
import { useSeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction';
|
||||||
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
|
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
|
||||||
import { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys';
|
import { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys';
|
||||||
|
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||||
import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
|
|
||||||
import {
|
import {
|
||||||
ActionMenuEntry,
|
ActionMenuEntry,
|
||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
@ -17,6 +20,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconChevronUp,
|
IconChevronUp,
|
||||||
|
IconDatabaseExport,
|
||||||
IconHeart,
|
IconHeart,
|
||||||
IconHeartOff,
|
IconHeartOff,
|
||||||
IconHistory,
|
IconHistory,
|
||||||
@ -24,10 +28,10 @@ import {
|
|||||||
IconPencil,
|
IconPencil,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
export const WORKFLOW_VERSIONS_ACTIONS_CONFIG: Record<
|
||||||
string,
|
string,
|
||||||
ActionMenuEntry & {
|
ActionMenuEntry & {
|
||||||
actionHook: SingleRecordActionHook;
|
actionHook: ActionHook;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
useAsDraftWorkflowVersionSingleRecord: {
|
useAsDraftWorkflowVersionSingleRecord: {
|
||||||
@ -125,4 +129,30 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
],
|
],
|
||||||
actionHook: useRemoveFromFavoritesSingleRecordAction,
|
actionHook: useRemoveFromFavoritesSingleRecordAction,
|
||||||
},
|
},
|
||||||
|
exportMultipleRecords: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: MultipleRecordsActionKeys.EXPORT,
|
||||||
|
label: 'Export records',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 8,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
|
exportView: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
|
label: 'Export view',
|
||||||
|
shortLabel: 'Export',
|
||||||
|
position: 9,
|
||||||
|
Icon: IconDatabaseExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||||
|
actionHook: useExportMultipleRecordsAction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
@ -1,24 +0,0 @@
|
|||||||
import { useMultipleRecordsActions } from '@/action-menu/actions/record-actions/multiple-records/hooks/useMultipleRecordsActions';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export const MultipleRecordsActionMenuEntrySetterEffect = ({
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const { registerMultipleRecordsActions, unregisterMultipleRecordsActions } =
|
|
||||||
useMultipleRecordsActions({
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
registerMultipleRecordsActions();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unregisterMultipleRecordsActions();
|
|
||||||
};
|
|
||||||
}, [registerMultipleRecordsActions, unregisterMultipleRecordsActions]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
@ -1,132 +1,87 @@
|
|||||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { expect } from '@storybook/test';
|
|
||||||
import { renderHook } from '@testing-library/react';
|
|
||||||
import { act } from 'react';
|
import { act } from 'react';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
import { useDeleteMultipleRecordsAction } from '../useDeleteMultipleRecordsAction';
|
import { useDeleteMultipleRecordsAction } from '../useDeleteMultipleRecordsAction';
|
||||||
|
|
||||||
|
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'person',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const peopleMock = getPeopleMock();
|
||||||
|
|
||||||
|
const deleteManyRecordsMock = jest.fn();
|
||||||
|
const resetTableRowSelectionMock = jest.fn();
|
||||||
|
|
||||||
jest.mock('@/object-record/hooks/useDeleteManyRecords', () => ({
|
jest.mock('@/object-record/hooks/useDeleteManyRecords', () => ({
|
||||||
useDeleteManyRecords: () => ({
|
useDeleteManyRecords: () => ({
|
||||||
deleteManyRecords: jest.fn(),
|
deleteManyRecords: deleteManyRecordsMock,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
|
||||||
useDeleteFavorite: () => ({
|
jest.mock('@/object-record/hooks/useLazyFetchAllRecords', () => ({
|
||||||
deleteFavorite: jest.fn(),
|
useLazyFetchAllRecords: () => {
|
||||||
}),
|
return {
|
||||||
}));
|
fetchAllRecords: () => [peopleMock[0], peopleMock[1]],
|
||||||
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
};
|
||||||
useFavorites: () => ({
|
},
|
||||||
sortedFavorites: [],
|
|
||||||
}),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||||
useRecordTable: () => ({
|
useRecordTable: () => ({
|
||||||
resetTableRowSelection: jest.fn(),
|
resetTableRowSelection: resetTableRowSelectionMock,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
(item) => item.nameSingular === 'company',
|
|
||||||
)!;
|
|
||||||
|
|
||||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
|
||||||
apolloMocks: [],
|
apolloMocks: [],
|
||||||
onInitializeRecoilSnapshot: ({ set }) => {
|
componentInstanceId: '1',
|
||||||
set(
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
personMockObjectMetadataItem.nameSingular,
|
||||||
instanceId: '1',
|
contextStoreTargetedRecordsRule: {
|
||||||
}),
|
mode: 'selection',
|
||||||
3,
|
selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
|
||||||
);
|
},
|
||||||
|
contextStoreNumberOfSelectedRecords: 2,
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('useDeleteMultipleRecordsAction', () => {
|
describe('useDeleteMultipleRecordsAction', () => {
|
||||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
it('should call deleteManyRecords on click', async () => {
|
||||||
<JestMetadataAndApolloMocksWrapper>
|
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
|
||||||
value={{
|
|
||||||
instanceId: '1',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
|
||||||
value={{
|
|
||||||
instanceId: '1',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
|
||||||
</JestMetadataAndApolloMocksWrapper>
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should register delete action', () => {
|
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => {
|
() =>
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
useDeleteMultipleRecordsAction({
|
||||||
actionMenuEntriesComponentState,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
);
|
}),
|
||||||
|
{
|
||||||
return {
|
wrapper,
|
||||||
actionMenuEntries,
|
|
||||||
useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
{ wrapper },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
act(() => {
|
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(false);
|
||||||
result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
|
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
|
||||||
expect(
|
|
||||||
result.current.actionMenuEntries.get('delete-multiple-records'),
|
|
||||||
).toBeDefined();
|
|
||||||
expect(
|
|
||||||
result.current.actionMenuEntries.get('delete-multiple-records')?.position,
|
|
||||||
).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unregister delete action', () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => {
|
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
|
||||||
actionMenuEntriesComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
actionMenuEntries,
|
|
||||||
useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{ wrapper },
|
|
||||||
);
|
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
|
result.current.onClick();
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(true);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.useDeleteMultipleRecordsAction.unregisterDeleteMultipleRecordsAction();
|
result.current.ConfirmationModal?.props?.onConfirmClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
await waitFor(() => {
|
||||||
|
expect(resetTableRowSelectionMock).toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(deleteManyRecordsMock).toHaveBeenCalledWith([
|
||||||
|
peopleMock[0].id,
|
||||||
|
peopleMock[1].id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,105 +1,59 @@
|
|||||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { expect } from '@storybook/test';
|
|
||||||
import { renderHook } from '@testing-library/react';
|
|
||||||
import { act } from 'react';
|
import { act } from 'react';
|
||||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
import { useExportMultipleRecordsAction } from '../useExportMultipleRecordsAction';
|
import { useExportMultipleRecordsAction } from '../useExportMultipleRecordsAction';
|
||||||
|
|
||||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
(item) => item.nameSingular === 'company',
|
(item) => item.nameSingular === 'person',
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
const peopleMock = getPeopleMock();
|
||||||
|
|
||||||
|
const downloadMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/object-record/record-index/export/hooks/useExportRecords', () => ({
|
||||||
|
useExportRecords: () => ({
|
||||||
|
download: downloadMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
apolloMocks: [],
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
personMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
|
||||||
|
},
|
||||||
|
contextStoreNumberOfSelectedRecords: 2,
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('useExportMultipleRecordsAction', () => {
|
describe('useExportMultipleRecordsAction', () => {
|
||||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
it('should call exportManyRecords on click', async () => {
|
||||||
<JestMetadataAndApolloMocksWrapper>
|
|
||||||
<JestObjectMetadataItemSetter>
|
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
|
||||||
value={{
|
|
||||||
instanceId: '1',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
|
||||||
value={{
|
|
||||||
instanceId: '1',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
|
||||||
</JestObjectMetadataItemSetter>
|
|
||||||
</JestMetadataAndApolloMocksWrapper>
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should register export multiple records action', () => {
|
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => {
|
() =>
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
useExportMultipleRecordsAction({
|
||||||
actionMenuEntriesComponentState,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
);
|
}),
|
||||||
|
{
|
||||||
return {
|
wrapper,
|
||||||
actionMenuEntries,
|
|
||||||
useExportMultipleRecordsAction: useExportMultipleRecordsAction({
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
{ wrapper },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
|
result.current.onClick();
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
await waitFor(() => {
|
||||||
expect(
|
expect(downloadMock).toHaveBeenCalled();
|
||||||
result.current.actionMenuEntries.get('export-multiple-records'),
|
|
||||||
).toBeDefined();
|
|
||||||
expect(
|
|
||||||
result.current.actionMenuEntries.get('export-multiple-records')?.position,
|
|
||||||
).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unregister export multiple records action', () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => {
|
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
|
||||||
actionMenuEntriesComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
actionMenuEntries,
|
|
||||||
useExportMultipleRecordsAction: useExportMultipleRecordsAction({
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{ wrapper },
|
|
||||||
);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
|
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.useExportMultipleRecordsAction.unregisterExportMultipleRecordsAction();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,15 +1,9 @@
|
|||||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
|
||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||||
import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
|
import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
|
||||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
@ -18,140 +12,101 @@ import { FilterOperand } from '@/object-record/object-filter-dropdown/types/Filt
|
|||||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useCallback, useContext, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { IconTrash, isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDeleteMultipleRecordsAction = ({
|
export const useDeleteMultipleRecordsAction: ActionHookWithObjectMetadataItem =
|
||||||
objectMetadataItem,
|
({ objectMetadataItem }) => {
|
||||||
}: {
|
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
useState(false);
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
const { resetTableRowSelection } = useRecordTable({
|
||||||
useState(false);
|
recordTableId: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const { resetTableRowSelection } = useRecordTable({
|
const { deleteManyRecords } = useDeleteManyRecords({
|
||||||
recordTableId: objectMetadataItem.namePlural,
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { deleteManyRecords } = useDeleteManyRecords({
|
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
contextStoreNumberOfSelectedRecordsComponentState,
|
||||||
});
|
);
|
||||||
|
|
||||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
contextStoreNumberOfSelectedRecordsComponentState,
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
const contextStoreFilters = useRecoilComponentValueV2(
|
||||||
contextStoreTargetedRecordsRuleComponentState,
|
contextStoreFiltersComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const contextStoreFilters = useRecoilComponentValueV2(
|
const { filterValueDependencies } = useFilterValueDependencies();
|
||||||
contextStoreFiltersComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { filterValueDependencies } = useFilterValueDependencies();
|
const graphqlFilter = computeContextStoreFilters(
|
||||||
|
contextStoreTargetedRecordsRule,
|
||||||
|
contextStoreFilters,
|
||||||
|
objectMetadataItem,
|
||||||
|
filterValueDependencies,
|
||||||
|
);
|
||||||
|
|
||||||
const graphqlFilter = computeContextStoreFilters(
|
const deletedAtFieldMetadata = objectMetadataItem.fields.find(
|
||||||
contextStoreTargetedRecordsRule,
|
(field) => field.name === 'deletedAt',
|
||||||
contextStoreFilters,
|
);
|
||||||
objectMetadataItem,
|
|
||||||
filterValueDependencies,
|
|
||||||
);
|
|
||||||
|
|
||||||
const deletedAtFieldMetadata = objectMetadataItem.fields.find(
|
const isDeletedFilterActive = contextStoreFilters.some(
|
||||||
(field) => field.name === 'deletedAt',
|
(filter) =>
|
||||||
);
|
filter.fieldMetadataId === deletedAtFieldMetadata?.id &&
|
||||||
|
filter.operand === FilterOperand.IsNotEmpty,
|
||||||
|
);
|
||||||
|
|
||||||
const isDeletedFilterActive = contextStoreFilters.some(
|
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||||
(filter) =>
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
filter.fieldMetadataId === deletedAtFieldMetadata?.id &&
|
filter: graphqlFilter,
|
||||||
filter.operand === FilterOperand.IsNotEmpty,
|
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||||
);
|
recordGqlFields: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
const handleDeleteClick = useCallback(async () => {
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
const recordsToDelete = await fetchAllRecordIds();
|
||||||
filter: graphqlFilter,
|
const recordIdsToDelete = recordsToDelete.map((record) => record.id);
|
||||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
|
||||||
recordGqlFields: { id: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const { closeRightDrawer } = useRightDrawer();
|
resetTableRowSelection();
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(async () => {
|
await deleteManyRecords(recordIdsToDelete);
|
||||||
const recordsToDelete = await fetchAllRecordIds();
|
}, [deleteManyRecords, fetchAllRecordIds, resetTableRowSelection]);
|
||||||
const recordIdsToDelete = recordsToDelete.map((record) => record.id);
|
|
||||||
|
|
||||||
resetTableRowSelection();
|
const isRemoteObject = objectMetadataItem.isRemote;
|
||||||
|
|
||||||
await deleteManyRecords(recordIdsToDelete);
|
const shouldBeRegistered =
|
||||||
}, [deleteManyRecords, fetchAllRecordIds, resetTableRowSelection]);
|
!isRemoteObject &&
|
||||||
|
!isDeletedFilterActive &&
|
||||||
|
isDefined(contextStoreNumberOfSelectedRecords) &&
|
||||||
|
contextStoreNumberOfSelectedRecords < DELETE_MAX_COUNT &&
|
||||||
|
contextStoreNumberOfSelectedRecords > 0;
|
||||||
|
|
||||||
const isRemoteObject = objectMetadataItem.isRemote;
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const canDelete =
|
setIsDeleteRecordsModalOpen(true);
|
||||||
!isRemoteObject &&
|
};
|
||||||
!isDeletedFilterActive &&
|
|
||||||
isDefined(contextStoreNumberOfSelectedRecords) &&
|
|
||||||
contextStoreNumberOfSelectedRecords < DELETE_MAX_COUNT &&
|
|
||||||
contextStoreNumberOfSelectedRecords > 0;
|
|
||||||
|
|
||||||
const { isInRightDrawer, onActionStartedCallback, onActionExecutedCallback } =
|
const confirmationModal = (
|
||||||
useContext(ActionMenuContext);
|
<ConfirmationModal
|
||||||
|
isOpen={isDeleteRecordsModalOpen}
|
||||||
|
setIsOpen={setIsDeleteRecordsModalOpen}
|
||||||
|
title={'Delete Records'}
|
||||||
|
subtitle={`Are you sure you want to delete these records? They can be recovered from the Options menu.`}
|
||||||
|
onConfirmClick={handleDeleteClick}
|
||||||
|
deleteButtonText={'Delete Records'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
const registerDeleteMultipleRecordsAction = ({
|
return {
|
||||||
position,
|
shouldBeRegistered,
|
||||||
}: {
|
onClick,
|
||||||
position: number;
|
ConfirmationModal: confirmationModal,
|
||||||
}) => {
|
};
|
||||||
if (canDelete) {
|
|
||||||
addActionMenuEntry({
|
|
||||||
type: ActionMenuEntryType.Standard,
|
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
|
||||||
key: MultipleRecordsActionKeys.DELETE,
|
|
||||||
label: 'Delete records',
|
|
||||||
shortLabel: 'Delete',
|
|
||||||
position,
|
|
||||||
Icon: IconTrash,
|
|
||||||
accent: 'danger',
|
|
||||||
isPinned: true,
|
|
||||||
onClick: () => {
|
|
||||||
setIsDeleteRecordsModalOpen(true);
|
|
||||||
},
|
|
||||||
ConfirmationModal: (
|
|
||||||
<ConfirmationModal
|
|
||||||
isOpen={isDeleteRecordsModalOpen}
|
|
||||||
setIsOpen={setIsDeleteRecordsModalOpen}
|
|
||||||
title={'Delete Records'}
|
|
||||||
subtitle={`Are you sure you want to delete these records? They can be recovered from the Options menu.`}
|
|
||||||
onConfirmClick={async () => {
|
|
||||||
onActionStartedCallback?.({
|
|
||||||
key: 'delete-multiple-records',
|
|
||||||
});
|
|
||||||
await handleDeleteClick();
|
|
||||||
onActionExecutedCallback?.({
|
|
||||||
key: 'delete-multiple-records',
|
|
||||||
});
|
|
||||||
if (isInRightDrawer) {
|
|
||||||
closeRightDrawer();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
deleteButtonText={'Delete Records'}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterDeleteMultipleRecordsAction = () => {
|
|
||||||
removeActionMenuEntry('delete-multiple-records');
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerDeleteMultipleRecordsAction,
|
|
||||||
unregisterDeleteMultipleRecordsAction,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,68 +1,25 @@
|
|||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { IconDatabaseExport } from 'twenty-ui';
|
|
||||||
|
|
||||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
import { useExportRecords } from '@/object-record/record-index/export/hooks/useExportRecords';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
import {
|
|
||||||
displayedExportProgress,
|
|
||||||
useExportRecords,
|
|
||||||
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
|
||||||
import { useContext } from 'react';
|
|
||||||
|
|
||||||
export const useExportMultipleRecordsAction = ({
|
export const useExportMultipleRecordsAction = ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
const { download } = useExportRecords({
|
||||||
|
|
||||||
const { progress, download } = useExportRecords({
|
|
||||||
delayMs: 100,
|
delayMs: 100,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordIndexId: objectMetadataItem.namePlural,
|
recordIndexId: objectMetadataItem.namePlural,
|
||||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { onActionStartedCallback, onActionExecutedCallback } =
|
const onClick = async () => {
|
||||||
useContext(ActionMenuContext);
|
await download();
|
||||||
|
|
||||||
const registerExportMultipleRecordsAction = ({
|
|
||||||
position,
|
|
||||||
}: {
|
|
||||||
position: number;
|
|
||||||
}) => {
|
|
||||||
addActionMenuEntry({
|
|
||||||
type: ActionMenuEntryType.Standard,
|
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
|
||||||
key: MultipleRecordsActionKeys.EXPORT,
|
|
||||||
position,
|
|
||||||
label: displayedExportProgress(progress),
|
|
||||||
shortLabel: 'Export',
|
|
||||||
Icon: IconDatabaseExport,
|
|
||||||
accent: 'default',
|
|
||||||
onClick: async () => {
|
|
||||||
await onActionStartedCallback?.({
|
|
||||||
key: MultipleRecordsActionKeys.EXPORT,
|
|
||||||
});
|
|
||||||
await download();
|
|
||||||
await onActionExecutedCallback?.({
|
|
||||||
key: MultipleRecordsActionKeys.EXPORT,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterExportMultipleRecordsAction = () => {
|
|
||||||
removeActionMenuEntry('export-multiple-records');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
registerExportMultipleRecordsAction,
|
shouldBeRegistered: true,
|
||||||
unregisterExportMultipleRecordsAction,
|
onClick,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
|
||||||
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
|
|
||||||
export const useMultipleRecordsActions = ({
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
registerDeleteMultipleRecordsAction,
|
|
||||||
unregisterDeleteMultipleRecordsAction,
|
|
||||||
} = useDeleteMultipleRecordsAction({
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
registerExportMultipleRecordsAction,
|
|
||||||
unregisterExportMultipleRecordsAction,
|
|
||||||
} = useExportMultipleRecordsAction({
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
const registerMultipleRecordsActions = () => {
|
|
||||||
registerDeleteMultipleRecordsAction({ position: 1 });
|
|
||||||
registerExportMultipleRecordsAction({ position: 2 });
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterMultipleRecordsActions = () => {
|
|
||||||
unregisterDeleteMultipleRecordsAction();
|
|
||||||
unregisterExportMultipleRecordsAction();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerMultipleRecordsActions,
|
|
||||||
unregisterMultipleRecordsActions,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { useNoSelectionRecordActions } from '@/action-menu/actions/record-actions/no-selection/hooks/useNoSelectionRecordActions';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export const NoSelectionActionMenuEntrySetterEffect = ({
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
registerNoSelectionRecordActions,
|
|
||||||
unregisterNoSelectionRecordActions,
|
|
||||||
} = useNoSelectionRecordActions({
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
registerNoSelectionRecordActions();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unregisterNoSelectionRecordActions();
|
|
||||||
};
|
|
||||||
}, [registerNoSelectionRecordActions, unregisterNoSelectionRecordActions]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { expect } from '@storybook/test';
|
|
||||||
import { renderHook } from '@testing-library/react';
|
|
||||||
import { act } from 'react';
|
|
||||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
|
||||||
|
|
||||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
|
|
||||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
|
||||||
(item) => item.nameSingular === 'company',
|
|
||||||
)!;
|
|
||||||
|
|
||||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
|
||||||
apolloMocks: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('useExportViewNoSelectionRecordAction', () => {
|
|
||||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
||||||
<JestMetadataAndApolloMocksWrapper>
|
|
||||||
<JestObjectMetadataItemSetter>
|
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
|
||||||
value={{
|
|
||||||
instanceId: '1',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
|
||||||
value={{
|
|
||||||
instanceId: '1',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
|
||||||
</JestObjectMetadataItemSetter>
|
|
||||||
</JestMetadataAndApolloMocksWrapper>
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should register export view action', () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => {
|
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
|
||||||
actionMenuEntriesComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
actionMenuEntries,
|
|
||||||
useExportViewNoSelectionRecordAction:
|
|
||||||
useExportViewNoSelectionRecordAction({
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{ wrapper },
|
|
||||||
);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
|
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
|
||||||
expect(
|
|
||||||
result.current.actionMenuEntries.get('export-view-no-selection'),
|
|
||||||
).toBeDefined();
|
|
||||||
expect(
|
|
||||||
result.current.actionMenuEntries.get('export-view-no-selection')
|
|
||||||
?.position,
|
|
||||||
).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unregister export view action', () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => {
|
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
|
||||||
actionMenuEntriesComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
actionMenuEntries,
|
|
||||||
useExportViewNoSelectionRecordAction:
|
|
||||||
useExportViewNoSelectionRecordAction({
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{ wrapper },
|
|
||||||
);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
|
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.useExportViewNoSelectionRecordAction.unregisterExportViewNoSelectionRecordsAction();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { IconDatabaseExport } from 'twenty-ui';
|
|
||||||
|
|
||||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
|
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
import {
|
|
||||||
displayedExportProgress,
|
|
||||||
useExportRecords,
|
|
||||||
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
|
||||||
|
|
||||||
export const useExportViewNoSelectionRecordAction = ({
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const { progress, download } = useExportRecords({
|
|
||||||
delayMs: 100,
|
|
||||||
objectMetadataItem,
|
|
||||||
recordIndexId: objectMetadataItem.namePlural,
|
|
||||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const registerExportViewNoSelectionRecordsAction = ({
|
|
||||||
position,
|
|
||||||
}: {
|
|
||||||
position: number;
|
|
||||||
}) => {
|
|
||||||
addActionMenuEntry({
|
|
||||||
type: ActionMenuEntryType.Standard,
|
|
||||||
scope: ActionMenuEntryScope.Global,
|
|
||||||
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
|
||||||
position,
|
|
||||||
label: displayedExportProgress(progress),
|
|
||||||
Icon: IconDatabaseExport,
|
|
||||||
accent: 'default',
|
|
||||||
onClick: () => download(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterExportViewNoSelectionRecordsAction = () => {
|
|
||||||
removeActionMenuEntry('export-view-no-selection');
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerExportViewNoSelectionRecordsAction,
|
|
||||||
unregisterExportViewNoSelectionRecordsAction,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
|
|
||||||
export const useNoSelectionRecordActions = ({
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
registerExportViewNoSelectionRecordsAction,
|
|
||||||
unregisterExportViewNoSelectionRecordsAction,
|
|
||||||
} = useExportViewNoSelectionRecordAction({
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
const registerNoSelectionRecordActions = () => {
|
|
||||||
registerExportViewNoSelectionRecordsAction({ position: 1 });
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterNoSelectionRecordActions = () => {
|
|
||||||
unregisterExportViewNoSelectionRecordsAction();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerNoSelectionRecordActions,
|
|
||||||
unregisterNoSelectionRecordActions,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
|
||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
|
||||||
import { useActionMenuEntriesWithCallbacks } from '@/action-menu/hooks/useActionMenuEntriesWithCallbacks';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export const SingleRecordActionMenuEntrySetterEffect = ({
|
|
||||||
objectMetadataItem,
|
|
||||||
viewType,
|
|
||||||
}: {
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
viewType: ActionViewType;
|
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const { actionMenuEntries } = useActionMenuEntriesWithCallbacks(
|
|
||||||
objectMetadataItem,
|
|
||||||
viewType,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
for (const action of actionMenuEntries) {
|
|
||||||
addActionMenuEntry(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
for (const action of actionMenuEntries) {
|
|
||||||
removeActionMenuEntry(action.key);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [actionMenuEntries, addActionMenuEntry, removeActionMenuEntry]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
@ -85,7 +85,6 @@ describe('useAddToFavoritesSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useAddToFavoritesSingleRecordAction({
|
useAddToFavoritesSingleRecordAction({
|
||||||
recordId: peopleMock[1].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -100,7 +99,6 @@ describe('useAddToFavoritesSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useAddToFavoritesSingleRecordAction({
|
useAddToFavoritesSingleRecordAction({
|
||||||
recordId: peopleMock[0].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -115,7 +113,6 @@ describe('useAddToFavoritesSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useAddToFavoritesSingleRecordAction({
|
useAddToFavoritesSingleRecordAction({
|
||||||
recordId: peopleMock[1].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -39,7 +39,6 @@ describe('useDeleteSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useDeleteSingleRecordAction({
|
useDeleteSingleRecordAction({
|
||||||
recordId: peopleMock[0].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -85,7 +85,6 @@ describe('useRemoveFromFavoritesSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useRemoveFromFavoritesSingleRecordAction({
|
useRemoveFromFavoritesSingleRecordAction({
|
||||||
recordId: peopleMock[0].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -100,7 +99,6 @@ describe('useRemoveFromFavoritesSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useRemoveFromFavoritesSingleRecordAction({
|
useRemoveFromFavoritesSingleRecordAction({
|
||||||
recordId: peopleMock[1].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -115,7 +113,6 @@ describe('useRemoveFromFavoritesSingleRecordAction', () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useRemoveFromFavoritesSingleRecordAction({
|
useRemoveFromFavoritesSingleRecordAction({
|
||||||
recordId: peopleMock[0].id,
|
|
||||||
objectMetadataItem: personMockObjectMetadataItem,
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
@ -6,8 +7,10 @@ import { isNull } from '@sniptt/guards';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useAddToFavoritesSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useAddToFavoritesSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||||
({ recordId, objectMetadataItem }) => {
|
({ objectMetadataItem }) => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
|
|
||||||
const { createFavorite } = useCreateFavorite();
|
const { createFavorite } = useCreateFavorite();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
@ -12,80 +13,83 @@ import { useCallback, useContext, useState } from 'react';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDeleteSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useDeleteSingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||||
({ recordId, objectMetadataItem }) => {
|
objectMetadataItem,
|
||||||
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
}) => {
|
||||||
useState(false);
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { resetTableRowSelection } = useRecordTable({
|
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
||||||
recordTableId: objectMetadataItem.namePlural,
|
useState(false);
|
||||||
});
|
|
||||||
|
|
||||||
const { deleteOneRecord } = useDeleteOneRecord({
|
const { resetTableRowSelection } = useRecordTable({
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
recordTableId: objectMetadataItem.namePlural,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
const { deleteOneRecord } = useDeleteOneRecord({
|
||||||
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
|
||||||
|
|
||||||
const { closeRightDrawer } = useRightDrawer();
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(async () => {
|
const { closeRightDrawer } = useRightDrawer();
|
||||||
resetTableRowSelection();
|
|
||||||
|
|
||||||
const foundFavorite = favorites?.find(
|
const handleDeleteClick = useCallback(async () => {
|
||||||
(favorite) => favorite.recordId === recordId,
|
resetTableRowSelection();
|
||||||
);
|
|
||||||
|
|
||||||
if (isDefined(foundFavorite)) {
|
const foundFavorite = favorites?.find(
|
||||||
deleteFavorite(foundFavorite.id);
|
(favorite) => favorite.recordId === recordId,
|
||||||
}
|
);
|
||||||
|
|
||||||
await deleteOneRecord(recordId);
|
if (isDefined(foundFavorite)) {
|
||||||
}, [
|
deleteFavorite(foundFavorite.id);
|
||||||
deleteFavorite,
|
}
|
||||||
deleteOneRecord,
|
|
||||||
favorites,
|
|
||||||
resetTableRowSelection,
|
|
||||||
recordId,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const isRemoteObject = objectMetadataItem.isRemote;
|
await deleteOneRecord(recordId);
|
||||||
|
}, [
|
||||||
|
deleteFavorite,
|
||||||
|
deleteOneRecord,
|
||||||
|
favorites,
|
||||||
|
resetTableRowSelection,
|
||||||
|
recordId,
|
||||||
|
]);
|
||||||
|
|
||||||
const { isInRightDrawer } = useContext(ActionMenuContext);
|
const isRemoteObject = objectMetadataItem.isRemote;
|
||||||
|
|
||||||
const shouldBeRegistered =
|
const { isInRightDrawer } = useContext(ActionMenuContext);
|
||||||
!isRemoteObject && isNull(selectedRecord?.deletedAt);
|
|
||||||
|
|
||||||
const onClick = () => {
|
const shouldBeRegistered =
|
||||||
if (!shouldBeRegistered) {
|
!isRemoteObject && isNull(selectedRecord?.deletedAt);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsDeleteRecordsModalOpen(true);
|
const onClick = () => {
|
||||||
};
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
setIsDeleteRecordsModalOpen(true);
|
||||||
shouldBeRegistered,
|
|
||||||
onClick,
|
|
||||||
ConfirmationModal: (
|
|
||||||
<ConfirmationModal
|
|
||||||
isOpen={isDeleteRecordsModalOpen}
|
|
||||||
setIsOpen={setIsDeleteRecordsModalOpen}
|
|
||||||
title={'Delete Record'}
|
|
||||||
subtitle={
|
|
||||||
'Are you sure you want to delete this record? It can be recovered from the Options menu.'
|
|
||||||
}
|
|
||||||
onConfirmClick={() => {
|
|
||||||
handleDeleteClick();
|
|
||||||
if (isInRightDrawer) {
|
|
||||||
closeRightDrawer();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
deleteButtonText={'Delete Record'}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
ConfirmationModal: (
|
||||||
|
<ConfirmationModal
|
||||||
|
isOpen={isDeleteRecordsModalOpen}
|
||||||
|
setIsOpen={setIsDeleteRecordsModalOpen}
|
||||||
|
title={'Delete Record'}
|
||||||
|
subtitle={
|
||||||
|
'Are you sure you want to delete this record? It can be recovered from the Options menu.'
|
||||||
|
}
|
||||||
|
onConfirmClick={() => {
|
||||||
|
handleDeleteClick();
|
||||||
|
if (isInRightDrawer) {
|
||||||
|
closeRightDrawer();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
deleteButtonText={'Delete Record'}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
|
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
@ -9,63 +10,66 @@ import { useCallback, useContext, useState } from 'react';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDestroySingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useDestroySingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||||
({ recordId, objectMetadataItem }) => {
|
objectMetadataItem,
|
||||||
const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
|
}) => {
|
||||||
useState(false);
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { resetTableRowSelection } = useRecordTable({
|
const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
|
||||||
recordTableId: objectMetadataItem.namePlural,
|
useState(false);
|
||||||
});
|
|
||||||
|
|
||||||
const { destroyOneRecord } = useDestroyOneRecord({
|
const { resetTableRowSelection } = useRecordTable({
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
recordTableId: objectMetadataItem.namePlural,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
const { destroyOneRecord } = useDestroyOneRecord({
|
||||||
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
const { closeRightDrawer } = useRightDrawer();
|
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(async () => {
|
const { closeRightDrawer } = useRightDrawer();
|
||||||
resetTableRowSelection();
|
|
||||||
|
|
||||||
await destroyOneRecord(recordId);
|
const handleDeleteClick = useCallback(async () => {
|
||||||
}, [resetTableRowSelection, destroyOneRecord, recordId]);
|
resetTableRowSelection();
|
||||||
|
|
||||||
const isRemoteObject = objectMetadataItem.isRemote;
|
await destroyOneRecord(recordId);
|
||||||
|
}, [resetTableRowSelection, destroyOneRecord, recordId]);
|
||||||
|
|
||||||
const { isInRightDrawer } = useContext(ActionMenuContext);
|
const isRemoteObject = objectMetadataItem.isRemote;
|
||||||
|
|
||||||
const shouldBeRegistered =
|
const { isInRightDrawer } = useContext(ActionMenuContext);
|
||||||
!isRemoteObject && isDefined(selectedRecord?.deletedAt);
|
|
||||||
|
|
||||||
const onClick = () => {
|
const shouldBeRegistered =
|
||||||
if (!shouldBeRegistered) {
|
!isRemoteObject && isDefined(selectedRecord?.deletedAt);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsDestroyRecordsModalOpen(true);
|
const onClick = () => {
|
||||||
};
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
setIsDestroyRecordsModalOpen(true);
|
||||||
shouldBeRegistered,
|
|
||||||
onClick,
|
|
||||||
ConfirmationModal: (
|
|
||||||
<ConfirmationModal
|
|
||||||
isOpen={isDestroyRecordsModalOpen}
|
|
||||||
setIsOpen={setIsDestroyRecordsModalOpen}
|
|
||||||
title={'Permanently Destroy Record'}
|
|
||||||
subtitle={
|
|
||||||
'Are you sure you want to destroy this record? It cannot be recovered anymore.'
|
|
||||||
}
|
|
||||||
onConfirmClick={async () => {
|
|
||||||
await handleDeleteClick();
|
|
||||||
if (isInRightDrawer) {
|
|
||||||
closeRightDrawer();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
deleteButtonText={'Permanently Destroy Record'}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
ConfirmationModal: (
|
||||||
|
<ConfirmationModal
|
||||||
|
isOpen={isDestroyRecordsModalOpen}
|
||||||
|
setIsOpen={setIsDestroyRecordsModalOpen}
|
||||||
|
title={'Permanently Destroy Record'}
|
||||||
|
subtitle={
|
||||||
|
'Are you sure you want to destroy this record? It cannot be recovered anymore.'
|
||||||
|
}
|
||||||
|
onConfirmClick={async () => {
|
||||||
|
await handleDeleteClick();
|
||||||
|
if (isInRightDrawer) {
|
||||||
|
closeRightDrawer();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
deleteButtonText={'Permanently Destroy Record'}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@ -1,49 +1,51 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { BlockNoteEditor } from '@blocknote/core';
|
import { BlockNoteEditor } from '@blocknote/core';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useExportNoteAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useExportNoteAction: ActionHookWithObjectMetadataItem = ({
|
||||||
({ recordId, objectMetadataItem }) => {
|
objectMetadataItem,
|
||||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
}) => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const filename = `${(selectedRecord?.title || 'Untitled Note').replace(/[<>:"/\\|?*]/g, '-')}`;
|
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
const isNoteOrTask =
|
const filename = `${(selectedRecord?.title || 'Untitled Note').replace(/[<>:"/\\|?*]/g, '-')}`;
|
||||||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note ||
|
|
||||||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task;
|
|
||||||
|
|
||||||
const shouldBeRegistered =
|
const isNoteOrTask =
|
||||||
isDefined(objectMetadataItem) &&
|
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note ||
|
||||||
isDefined(selectedRecord) &&
|
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task;
|
||||||
isNoteOrTask;
|
|
||||||
|
|
||||||
const onClick = async () => {
|
const shouldBeRegistered =
|
||||||
if (!shouldBeRegistered || !selectedRecord?.body) {
|
isDefined(objectMetadataItem) && isDefined(selectedRecord) && isNoteOrTask;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = await BlockNoteEditor.create({
|
const onClick = async () => {
|
||||||
initialContent: JSON.parse(selectedRecord.body),
|
if (!shouldBeRegistered || !selectedRecord?.body) {
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { exportBlockNoteEditorToPdf } = await import(
|
const editor = await BlockNoteEditor.create({
|
||||||
'@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToPdf'
|
initialContent: JSON.parse(selectedRecord.body),
|
||||||
);
|
});
|
||||||
|
|
||||||
await exportBlockNoteEditorToPdf(editor, filename);
|
const { exportBlockNoteEditorToPdf } = await import(
|
||||||
|
'@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToPdf'
|
||||||
|
);
|
||||||
|
|
||||||
// TODO later: implement DOCX export
|
await exportBlockNoteEditorToPdf(editor, filename);
|
||||||
// const { exportBlockNoteEditorToDocx } = await import(
|
|
||||||
// '@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToDocx'
|
|
||||||
// );
|
|
||||||
// await exportBlockNoteEditorToDocx(editor, filename);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
// TODO later: implement DOCX export
|
||||||
shouldBeRegistered,
|
// const { exportBlockNoteEditorToDocx } = await import(
|
||||||
onClick,
|
// '@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToDocx'
|
||||||
};
|
// );
|
||||||
|
// await exportBlockNoteEditorToDocx(editor, filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
export const useNavigateToNextRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useNavigateToNextRecordSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||||
({ recordId, objectMetadataItem }) => {
|
({ objectMetadataItem }) => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { isInRightDrawer } = useContext(ActionMenuContext);
|
const { isInRightDrawer } = useContext(ActionMenuContext);
|
||||||
|
|
||||||
const { navigateToNextRecord } = useRecordShowPagePagination(
|
const { navigateToNextRecord } = useRecordShowPagePagination(
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
export const useNavigateToPreviousRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useNavigateToPreviousRecordSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||||
({ recordId, objectMetadataItem }) => {
|
({ objectMetadataItem }) => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { isInRightDrawer } = useContext(ActionMenuContext);
|
const { isInRightDrawer } = useContext(ActionMenuContext);
|
||||||
|
|
||||||
const { navigateToPreviousRecord } = useRecordShowPagePagination(
|
const { navigateToPreviousRecord } = useRecordShowPagePagination(
|
||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
recordId,
|
recordId,
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useRemoveFromFavoritesSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
export const useRemoveFromFavoritesSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||||
({ recordId, objectMetadataItem }) => {
|
({ objectMetadataItem }) => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
|
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
|
||||||
|
export const useSelectedRecordIdOrThrow = () => {
|
||||||
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
contextStoreTargetedRecordsRule.mode === 'exclusion' ||
|
||||||
|
(contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||||
|
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0)
|
||||||
|
) {
|
||||||
|
throw new Error('Selected record ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextStoreTargetedRecordsRule.selectedRecordIds[0];
|
||||||
|
};
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1 } from '@/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV1';
|
|
||||||
import { DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2 } from '@/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV2';
|
|
||||||
import { WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/single-record/workflow-actions/constants/WorkflowSingleRecordActionsConfig';
|
|
||||||
import { WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/constants/WorkflowRunsSingleRecordActionsConfig';
|
|
||||||
import { WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/constants/WorkflowVersionsSingleRecordActionsConfig';
|
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
|
|
||||||
export const getActionConfig = (
|
|
||||||
objectMetadataItem: ObjectMetadataItem,
|
|
||||||
isPageHeaderV2Enabled: boolean,
|
|
||||||
) => {
|
|
||||||
if (objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow) {
|
|
||||||
return WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkflowVersion
|
|
||||||
) {
|
|
||||||
return WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG;
|
|
||||||
}
|
|
||||||
if (objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkflowRun) {
|
|
||||||
return WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG;
|
|
||||||
}
|
|
||||||
return isPageHeaderV2Enabled
|
|
||||||
? DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2
|
|
||||||
: DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1;
|
|
||||||
};
|
|
||||||
@ -55,10 +55,7 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
|||||||
describe('useActivateDraftWorkflowSingleRecordAction', () => {
|
describe('useActivateDraftWorkflowSingleRecordAction', () => {
|
||||||
it('should be registered', () => {
|
it('should be registered', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useActivateDraftWorkflowSingleRecordAction(),
|
||||||
useActivateDraftWorkflowSingleRecordAction({
|
|
||||||
recordId: workflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper,
|
wrapper,
|
||||||
},
|
},
|
||||||
@ -69,10 +66,7 @@ describe('useActivateDraftWorkflowSingleRecordAction', () => {
|
|||||||
|
|
||||||
it('should call activateWorkflowVersion on click', () => {
|
it('should call activateWorkflowVersion on click', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useActivateDraftWorkflowSingleRecordAction(),
|
||||||
useActivateDraftWorkflowSingleRecordAction({
|
|
||||||
recordId: workflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper,
|
wrapper,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -56,10 +56,7 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
|||||||
describe('useActivateLastPublishedVersionWorkflowSingleRecordAction', () => {
|
describe('useActivateLastPublishedVersionWorkflowSingleRecordAction', () => {
|
||||||
it('should be registered', () => {
|
it('should be registered', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useActivateLastPublishedVersionWorkflowSingleRecordAction(),
|
||||||
useActivateLastPublishedVersionWorkflowSingleRecordAction({
|
|
||||||
recordId: workflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper,
|
wrapper,
|
||||||
},
|
},
|
||||||
@ -70,10 +67,7 @@ describe('useActivateLastPublishedVersionWorkflowSingleRecordAction', () => {
|
|||||||
|
|
||||||
it('should call activateWorkflowVersion on click', () => {
|
it('should call activateWorkflowVersion on click', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useActivateLastPublishedVersionWorkflowSingleRecordAction(),
|
||||||
useActivateLastPublishedVersionWorkflowSingleRecordAction({
|
|
||||||
recordId: workflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper,
|
wrapper,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -104,10 +104,7 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
|
|||||||
() => deactivatedWorkflowMock,
|
() => deactivatedWorkflowMock,
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDeactivateWorkflowSingleRecordAction(),
|
||||||
useDeactivateWorkflowSingleRecordAction({
|
|
||||||
recordId: deactivatedWorkflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: deactivatedWorkflowWrapper,
|
wrapper: deactivatedWorkflowWrapper,
|
||||||
},
|
},
|
||||||
@ -121,10 +118,7 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
|
|||||||
() => activeWorkflowMock,
|
() => activeWorkflowMock,
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDeactivateWorkflowSingleRecordAction(),
|
||||||
useDeactivateWorkflowSingleRecordAction({
|
|
||||||
recordId: activeWorkflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: activeWorkflowWrapper,
|
wrapper: activeWorkflowWrapper,
|
||||||
},
|
},
|
||||||
@ -138,10 +132,7 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
|
|||||||
() => activeWorkflowMock,
|
() => activeWorkflowMock,
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDeactivateWorkflowSingleRecordAction(),
|
||||||
useDeactivateWorkflowSingleRecordAction({
|
|
||||||
recordId: activeWorkflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: activeWorkflowWrapper,
|
wrapper: activeWorkflowWrapper,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -178,10 +178,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
|||||||
() => noDraftWorkflowMock,
|
() => noDraftWorkflowMock,
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDiscardDraftWorkflowSingleRecordAction(),
|
||||||
useDiscardDraftWorkflowSingleRecordAction({
|
|
||||||
recordId: noDraftWorkflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: noDraftWorkflowWrapper,
|
wrapper: noDraftWorkflowWrapper,
|
||||||
},
|
},
|
||||||
@ -196,10 +193,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDiscardDraftWorkflowSingleRecordAction(),
|
||||||
useDiscardDraftWorkflowSingleRecordAction({
|
|
||||||
recordId: draftWorkflowMockWithOneVersion.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: draftWorkflowWithOneVersionWrapper,
|
wrapper: draftWorkflowWithOneVersionWrapper,
|
||||||
},
|
},
|
||||||
@ -213,10 +207,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
|||||||
() => draftWorkflowMock,
|
() => draftWorkflowMock,
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDiscardDraftWorkflowSingleRecordAction(),
|
||||||
useDiscardDraftWorkflowSingleRecordAction({
|
|
||||||
recordId: draftWorkflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: draftWorkflowWrapper,
|
wrapper: draftWorkflowWrapper,
|
||||||
},
|
},
|
||||||
@ -230,10 +221,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
|||||||
() => draftWorkflowMock,
|
() => draftWorkflowMock,
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() => useDiscardDraftWorkflowSingleRecordAction(),
|
||||||
useDiscardDraftWorkflowSingleRecordAction({
|
|
||||||
recordId: draftWorkflowMock.id,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
wrapper: draftWorkflowWrapper,
|
wrapper: draftWorkflowWrapper,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useActivateDraftWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useActivateDraftWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useActivateLastPublishedVersionWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useActivateLastPublishedVersionWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDeactivateWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useDeactivateWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDiscardDraftWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useDiscardDraftWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useSeeActiveVersionWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useSeeActiveVersionWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflow = useWorkflowWithCurrentVersion(recordId);
|
const workflow = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
const isDraft = workflow?.statuses?.includes('DRAFT') || false;
|
const isDraft = workflow?.statuses?.includes('DRAFT') || false;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
@ -6,8 +7,10 @@ import qs from 'qs';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useSeeRunsWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useSeeRunsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
@ -6,8 +7,10 @@ import qs from 'qs';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useSeeVersionsWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useSeeVersionsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useTestWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useTestWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
@ -8,8 +9,10 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useSeeRunsWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useSeeRunsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||||
|
|||||||
@ -1,21 +1,23 @@
|
|||||||
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useSeeVersionsWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useSeeVersionsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
if (!isDefined(workflowVersion)) {
|
if (!isDefined(workflowVersion)) {
|
||||||
throw new Error('Workflow version not found');
|
throw new Error('Workflow version not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add recordIds to the hook
|
||||||
const { shouldBeRegistered, onClick } =
|
const { shouldBeRegistered, onClick } =
|
||||||
useSeeVersionsWorkflowSingleRecordAction({
|
useSeeVersionsWorkflowSingleRecordAction();
|
||||||
recordId: workflowVersion.workflow.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldBeRegistered,
|
shouldBeRegistered,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL';
|
import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL';
|
||||||
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
||||||
@ -10,8 +11,10 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useUseAsDraftWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
export const useUseAsDraftWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||||
({ recordId }) => {
|
() => {
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
const workflowVersion = useWorkflowVersion(recordId);
|
const workflowVersion = useWorkflowVersion(recordId);
|
||||||
|
|
||||||
const workflow = useWorkflowWithCurrentVersion(
|
const workflow = useWorkflowWithCurrentVersion(
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { ActionHookResult } from '@/action-menu/actions/types/ActionHookResult';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
|
export type ActionHook =
|
||||||
|
| ActionHookWithoutObjectMetadataItem
|
||||||
|
| ActionHookWithObjectMetadataItem;
|
||||||
|
|
||||||
|
export type ActionHookWithoutObjectMetadataItem = () => ActionHookResult;
|
||||||
|
|
||||||
|
export type ActionHookWithObjectMetadataItem = ({
|
||||||
|
objectMetadataItem,
|
||||||
|
}: {
|
||||||
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
}) => ActionHookResult;
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import { ActionHookResult } from '@/action-menu/actions/types/ActionHookResult';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
|
|
||||||
export type SingleRecordActionHook =
|
|
||||||
| SingleRecordActionHookWithoutObjectMetadataItem
|
|
||||||
| SingleRecordActionHookWithObjectMetadataItem;
|
|
||||||
|
|
||||||
export type SingleRecordActionHookWithoutObjectMetadataItem = ({
|
|
||||||
recordId,
|
|
||||||
}: {
|
|
||||||
recordId: string;
|
|
||||||
}) => ActionHookResult;
|
|
||||||
|
|
||||||
export type SingleRecordActionHookWithObjectMetadataItem = ({
|
|
||||||
recordId,
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
recordId: string;
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => ActionHookResult;
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { DEFAULT_ACTIONS_CONFIG_V1 } from '@/action-menu/actions/record-actions/constants/DefaultActionsConfigV1';
|
||||||
|
import { DEFAULT_ACTIONS_CONFIG_V2 } from '@/action-menu/actions/record-actions/constants/DefaultActionsConfigV2';
|
||||||
|
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 { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
|
export const getActionConfig = (
|
||||||
|
objectMetadataItem: ObjectMetadataItem,
|
||||||
|
isPageHeaderV2Enabled: boolean,
|
||||||
|
) => {
|
||||||
|
switch (objectMetadataItem.nameSingular) {
|
||||||
|
case CoreObjectNameSingular.Workflow:
|
||||||
|
return WORKFLOW_ACTIONS_CONFIG;
|
||||||
|
case CoreObjectNameSingular.WorkflowVersion:
|
||||||
|
return WORKFLOW_VERSIONS_ACTIONS_CONFIG;
|
||||||
|
case CoreObjectNameSingular.WorkflowRun:
|
||||||
|
return WORKFLOW_RUNS_ACTIONS_CONFIG;
|
||||||
|
default:
|
||||||
|
return isPageHeaderV2Enabled
|
||||||
|
? DEFAULT_ACTIONS_CONFIG_V2
|
||||||
|
: DEFAULT_ACTIONS_CONFIG_V1;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||||
|
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
|
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
||||||
|
|
||||||
|
export const getActionViewType = (
|
||||||
|
contextStoreCurrentViewType: ContextStoreViewType | null,
|
||||||
|
contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule,
|
||||||
|
) => {
|
||||||
|
if (contextStoreCurrentViewType === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contextStoreCurrentViewType === ContextStoreViewType.ShowPage) {
|
||||||
|
return ActionViewType.SHOW_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||||
|
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0
|
||||||
|
) {
|
||||||
|
return ActionViewType.INDEX_PAGE_NO_SELECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||||
|
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1
|
||||||
|
) {
|
||||||
|
return ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionViewType.INDEX_PAGE_BULK_SELECTION;
|
||||||
|
};
|
||||||
@ -1,70 +0,0 @@
|
|||||||
import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
|
|
||||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
|
||||||
import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
|
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { useContext } from 'react';
|
|
||||||
import { isDefined } from 'twenty-ui';
|
|
||||||
import { FeatureFlagKey } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export const useActionMenuEntriesWithCallbacks = (
|
|
||||||
objectMetadataItem: ObjectMetadataItem,
|
|
||||||
viewType: ActionViewType,
|
|
||||||
) => {
|
|
||||||
const isPageHeaderV2Enabled = useIsFeatureEnabled(
|
|
||||||
FeatureFlagKey.IsPageHeaderV2Enabled,
|
|
||||||
);
|
|
||||||
|
|
||||||
const actionConfig = getActionConfig(
|
|
||||||
objectMetadataItem,
|
|
||||||
isPageHeaderV2Enabled,
|
|
||||||
);
|
|
||||||
|
|
||||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
|
||||||
contextStoreTargetedRecordsRuleComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedRecordId =
|
|
||||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
|
||||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (!isDefined(selectedRecordId)) {
|
|
||||||
throw new Error('Selected record ID is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { onActionStartedCallback, onActionExecutedCallback } =
|
|
||||||
useContext(ActionMenuContext);
|
|
||||||
|
|
||||||
const actionMenuEntries = Object.values(actionConfig ?? {})
|
|
||||||
.filter((action) => action.availableOn?.includes(viewType))
|
|
||||||
.map((action) => {
|
|
||||||
const { shouldBeRegistered, onClick, ConfirmationModal } =
|
|
||||||
action.actionHook({
|
|
||||||
recordId: selectedRecordId,
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!shouldBeRegistered) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappedAction = wrapActionInCallbacks({
|
|
||||||
action: {
|
|
||||||
...action,
|
|
||||||
onClick,
|
|
||||||
ConfirmationModal,
|
|
||||||
},
|
|
||||||
onActionStartedCallback,
|
|
||||||
onActionExecutedCallback,
|
|
||||||
});
|
|
||||||
|
|
||||||
return wrappedAction;
|
|
||||||
})
|
|
||||||
.filter(isDefined);
|
|
||||||
|
|
||||||
return { actionMenuEntries };
|
|
||||||
};
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { ReactNode, useEffect, useState } from 'react';
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||||
|
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||||
import {
|
import {
|
||||||
ContextStoreTargetedRecordsRule,
|
ContextStoreTargetedRecordsRule,
|
||||||
contextStoreTargetedRecordsRuleComponentState,
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
@ -13,10 +14,12 @@ export const JestContextStoreSetter = ({
|
|||||||
mode: 'selection',
|
mode: 'selection',
|
||||||
selectedRecordIds: [],
|
selectedRecordIds: [],
|
||||||
},
|
},
|
||||||
|
contextStoreNumberOfSelectedRecords = 0,
|
||||||
contextStoreCurrentObjectMetadataNameSingular = '',
|
contextStoreCurrentObjectMetadataNameSingular = '',
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
|
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
|
||||||
|
contextStoreNumberOfSelectedRecords?: number;
|
||||||
contextStoreCurrentObjectMetadataNameSingular?: string;
|
contextStoreCurrentObjectMetadataNameSingular?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
@ -28,6 +31,10 @@ export const JestContextStoreSetter = ({
|
|||||||
contextStoreCurrentObjectMetadataIdComponentState,
|
contextStoreCurrentObjectMetadataIdComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2(
|
||||||
|
contextStoreNumberOfSelectedRecordsComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular: contextStoreCurrentObjectMetadataNameSingular,
|
objectNameSingular: contextStoreCurrentObjectMetadataNameSingular,
|
||||||
});
|
});
|
||||||
@ -38,12 +45,15 @@ export const JestContextStoreSetter = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setContextStoreTargetedRecordsRule(contextStoreTargetedRecordsRule);
|
setContextStoreTargetedRecordsRule(contextStoreTargetedRecordsRule);
|
||||||
setContextStoreCurrentObjectMetadataId(contextStoreCurrentObjectMetadataId);
|
setContextStoreCurrentObjectMetadataId(contextStoreCurrentObjectMetadataId);
|
||||||
|
setContextStoreNumberOfSelectedRecords(contextStoreNumberOfSelectedRecords);
|
||||||
setIsLoaded(true);
|
setIsLoaded(true);
|
||||||
}, [
|
}, [
|
||||||
setContextStoreTargetedRecordsRule,
|
setContextStoreTargetedRecordsRule,
|
||||||
setContextStoreCurrentObjectMetadataId,
|
setContextStoreCurrentObjectMetadataId,
|
||||||
contextStoreTargetedRecordsRule,
|
contextStoreTargetedRecordsRule,
|
||||||
contextStoreCurrentObjectMetadataId,
|
contextStoreCurrentObjectMetadataId,
|
||||||
|
setContextStoreNumberOfSelectedRecords,
|
||||||
|
contextStoreNumberOfSelectedRecords,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return isLoaded ? <>{children}</> : null;
|
return isLoaded ? <>{children}</> : null;
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export type GetJestMetadataAndApolloMocksAndActionMenuWrapperProps = {
|
|||||||
| undefined;
|
| undefined;
|
||||||
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
|
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
|
||||||
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
|
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
|
||||||
|
contextStoreNumberOfSelectedRecords?: number;
|
||||||
contextStoreCurrentObjectMetadataNameSingular?: string;
|
contextStoreCurrentObjectMetadataNameSingular?: string;
|
||||||
componentInstanceId: string;
|
componentInstanceId: string;
|
||||||
};
|
};
|
||||||
@ -21,6 +22,7 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
|
|||||||
apolloMocks,
|
apolloMocks,
|
||||||
onInitializeRecoilSnapshot,
|
onInitializeRecoilSnapshot,
|
||||||
contextStoreTargetedRecordsRule,
|
contextStoreTargetedRecordsRule,
|
||||||
|
contextStoreNumberOfSelectedRecords,
|
||||||
contextStoreCurrentObjectMetadataNameSingular,
|
contextStoreCurrentObjectMetadataNameSingular,
|
||||||
componentInstanceId,
|
componentInstanceId,
|
||||||
}: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps) => {
|
}: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps) => {
|
||||||
@ -43,6 +45,9 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
|
|||||||
>
|
>
|
||||||
<JestContextStoreSetter
|
<JestContextStoreSetter
|
||||||
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
||||||
|
contextStoreNumberOfSelectedRecords={
|
||||||
|
contextStoreNumberOfSelectedRecords
|
||||||
|
}
|
||||||
contextStoreCurrentObjectMetadataNameSingular={
|
contextStoreCurrentObjectMetadataNameSingular={
|
||||||
contextStoreCurrentObjectMetadataNameSingular
|
contextStoreCurrentObjectMetadataNameSingular
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user