Files
twenty/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts
T
Raphaël Bosi b11f77df2a [FRONT COMPONENTS] Introduce conditionalAvailabilityExpression to command menu items (#18319)
## PR Description

- Uses `expr-eval` to enable front components (SDK plugins) to define
conditional availability as declarative expressions.
- Moves shared types and constants to `twenty-shared`
- Introduces a `conditionalAvailabilityExpression` field on
`CommandMenuItemEntity`, allowing command menu items to store an
`expr-eval` compatible expression string that is evaluated against a
CommandMenuContext to determine if the item should be shown.
- Creates an esbuild transform plugin
`conditional-availability-transform-plugin` in `twenty-sdk` that
converts TypeScript conditional availability expressions into
`expr-eval` compatible syntax at build time, so SDK developers can write
natural TS expressions that get transformed to evaluable strings.
- Removes deprecated `forceRegisteredActionsByKey` state and its usage.
- Creates `useCommandMenuContext` hook that builds the full
`CommandMenuContext` object from React state, which is then passed to
`useCommandMenuItemFrontComponentActions` for evaluating conditional
availability expressions.
2026-03-04 16:33:58 +01:00

92 lines
2.7 KiB
TypeScript

import { useEffect } from 'react';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { shouldWorkflowRefetchRequestFamilyState } from '@/workflow/states/shouldWorkflowRefetchRequestFamilyState';
import {
type Workflow,
type WorkflowVersion,
type WorkflowWithCurrentVersion,
} from '@/workflow/types/Workflow';
import { isDefined } from 'twenty-shared/utils';
type WorkflowWithAllVersions = Omit<Workflow, 'versions'> & {
versions: Array<
Pick<WorkflowVersion, 'id' | 'status' | 'name' | 'createdAt'>
>;
};
export const useWorkflowWithCurrentVersion = (
workflowId: string | undefined,
): WorkflowWithCurrentVersion | undefined => {
const shouldWorkflowRefetchRequest = useAtomFamilyStateValue(
shouldWorkflowRefetchRequestFamilyState,
workflowId ?? '',
);
const setShouldWorkflowRefetchRequest = useSetAtomFamilyState(
shouldWorkflowRefetchRequestFamilyState,
workflowId ?? '',
);
const { record: workflow, refetch: refetchWorkflow } =
useFindOneRecord<WorkflowWithAllVersions>({
objectNameSingular: CoreObjectNameSingular.Workflow,
objectRecordId: workflowId,
recordGqlFields: {
id: true,
name: true,
statuses: true,
lastPublishedVersionId: true,
versions: {
id: true,
status: true,
name: true,
createdAt: true,
},
},
skip: !isDefined(workflowId),
});
useEffect(() => {
if (shouldWorkflowRefetchRequest) {
setShouldWorkflowRefetchRequest(false);
refetchWorkflow();
}
}, [
shouldWorkflowRefetchRequest,
setShouldWorkflowRefetchRequest,
refetchWorkflow,
]);
const draftVersion = workflow?.versions.find(
(workflowVersion) => workflowVersion.status === 'DRAFT',
);
const workflowVersions = [...(workflow?.versions ?? [])];
workflowVersions.sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1));
const latestVersion = workflowVersions[0];
const currentVersionWithoutSteps = draftVersion ?? latestVersion;
const { record: currentVersionWithSteps } = useFindOneRecord<WorkflowVersion>(
{
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
objectRecordId: currentVersionWithoutSteps?.id,
skip: !isDefined(currentVersionWithoutSteps?.id),
},
);
if (!isDefined(workflow) || !isDefined(currentVersionWithSteps)) {
return undefined;
}
return {
...workflow,
currentVersion: currentVersionWithSteps,
};
};