Files
twenty/packages/twenty-front/src/modules/command-menu-item/engine-command/components/HeadlessConfirmationModalEngineCommandEffect.tsx
T
Raphaël Bosi 49afe5dbc4 Refactor: Unify command menu item execution (#18908)
- Makes engineComponentKey non-nullable on CommandMenuItem. Adds two new
engine component keys: TRIGGER_WORKFLOW_VERSION and
FRONT_COMPONENT_RENDERER to cover the former standalone cases.
- Unifies the previously separated engine, front component and workflow
runners into a single `CommandRunner` component with a unified
`useMountCommand` hook that handles all command types.
2026-03-24 15:59:52 +00:00

97 lines
3.2 KiB
TypeScript

import { useIsHeadlessEngineCommandEffectInitialized } from '@/command-menu-item/engine-command/hooks/useIsHeadlessEngineCommandEffectInitialized';
import { type ReactNode, useEffect } from 'react';
import { COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalResultBrowserEventName';
import { useCommandMenuConfirmationModal } from '@/command-menu-item/confirmation-modal/hooks/useCommandMenuConfirmationModal';
import { type CommandMenuConfirmationModalResultBrowserEventDetail } from '@/command-menu-item/confirmation-modal/types/CommandMenuConfirmationModalResultBrowserEventDetail';
import { useUnmountCommand } from '@/command-menu-item/engine-command/hooks/useUnmountEngineCommand';
import { CommandComponentInstanceContext } from '@/command-menu-item/engine-command/states/contexts/CommandComponentInstanceContext';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { type ButtonAccent } from 'twenty-ui/input';
export type HeadlessConfirmationModalEngineCommandEffectProps = {
title: string;
subtitle: ReactNode;
confirmButtonText: string;
confirmButtonAccent?: ButtonAccent;
execute: () => void | Promise<unknown>;
};
export const HeadlessConfirmationModalEngineCommandEffect = ({
title,
subtitle,
confirmButtonText,
confirmButtonAccent = 'danger',
execute,
}: HeadlessConfirmationModalEngineCommandEffectProps) => {
const { isInitializedRef, setIsInitialized } =
useIsHeadlessEngineCommandEffectInitialized();
const commandMenuItemId = useAvailableComponentInstanceIdOrThrow(
CommandComponentInstanceContext,
);
const unmountCommand = useUnmountCommand();
const { openConfirmationModal } = useCommandMenuConfirmationModal();
useEffect(() => {
if (isInitializedRef.current) {
return;
}
setIsInitialized(true);
openConfirmationModal({
caller: { type: 'commandMenuItem', commandMenuItemId },
title,
subtitle,
confirmButtonText,
confirmButtonAccent,
});
}, [
isInitializedRef,
setIsInitialized,
commandMenuItemId,
openConfirmationModal,
title,
subtitle,
confirmButtonText,
confirmButtonAccent,
]);
useEffect(() => {
const handleConfirmationResult = async (event: Event) => {
const customEvent =
event as CustomEvent<CommandMenuConfirmationModalResultBrowserEventDetail>;
const caller = customEvent.detail.caller;
if (
caller.type !== 'commandMenuItem' ||
caller.commandMenuItemId !== commandMenuItemId
) {
return;
}
if (customEvent.detail.confirmationResult === 'confirm') {
await execute();
}
unmountCommand(commandMenuItemId);
};
window.addEventListener(
COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME,
handleConfirmationResult,
);
return () => {
window.removeEventListener(
COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME,
handleConfirmationResult,
);
};
}, [execute, commandMenuItemId, unmountCommand]);
return null;
};