From 28466ecbf34cda7d239508cdccee209968149219 Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 19 Jun 2025 18:32:42 +0200 Subject: [PATCH] Add icon select to manual trigger (#12724) ## After image image image image --- .../hooks/useRunWorkflowRecordActions.tsx | 11 ++- .../useRunWorkflowRecordAgnosticActions.tsx | 12 ++- .../ui/input/components/IconPicker.tsx | 47 ++++++---- .../modules/ui/input/components/Select.tsx | 8 ++ ...ActiveWorkflowVersionsWithManualTrigger.ts | 7 +- .../src/modules/workflow/types/Workflow.ts | 4 + .../validation-schemas/workflowSchema.ts | 1 + .../WorkflowEditTriggerManualForm.tsx | 93 +++++++++++++++++-- .../constants/CommandMenuDefaultIcon.ts | 1 + .../ManualTriggerAvailabilityOptions.ts | 4 +- .../getManualTriggerDefaultSettings.test.ts | 4 + .../getTriggerDefaultDefinition.test.ts | 2 + .../utils/getManualTriggerDefaultSettings.ts | 5 + .../prefill-workflows.ts | 2 +- .../types/workflow-trigger.type.ts | 64 +++++++------ .../src/display/icon/hooks/useIcons.ts | 10 +- .../src/display/icon/types/IconComponent.ts | 2 + 17 files changed, 208 insertions(+), 69 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-trigger/constants/CommandMenuDefaultIcon.ts diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/run-workflow-actions/hooks/useRunWorkflowRecordActions.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/run-workflow-actions/hooks/useRunWorkflowRecordActions.tsx index c3f06e294..2123aef4b 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/run-workflow-actions/hooks/useRunWorkflowRecordActions.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/run-workflow-actions/hooks/useRunWorkflowRecordActions.tsx @@ -11,7 +11,8 @@ import { msg } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; import { capitalize, isDefined } from 'twenty-shared/utils'; -import { IconSettingsAutomation } from 'twenty-ui/display'; +import { useIcons } from 'twenty-ui/display'; +import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon'; export const useRunWorkflowRecordActions = ({ objectMetadataItem, @@ -20,6 +21,7 @@ export const useRunWorkflowRecordActions = ({ objectMetadataItem: ObjectMetadataItem; skip?: boolean; }) => { + const { getIcon } = useIcons(); const contextStoreTargetedRecordsRule = useRecoilComponentValueV2( contextStoreTargetedRecordsRuleComponentState, ); @@ -48,13 +50,18 @@ export const useRunWorkflowRecordActions = ({ .map((activeWorkflowVersion, index) => { const name = capitalize(activeWorkflowVersion.workflow.name); + const Icon = getIcon( + activeWorkflowVersion.trigger?.settings.icon, + COMMAND_MENU_DEFAULT_ICON, + ); + return { type: ActionType.WorkflowRun, key: `workflow-run-${activeWorkflowVersion.id}`, scope: ActionScope.RecordSelection, label: msg`${name}`, position: index, - Icon: IconSettingsAutomation, + Icon, shouldBeRegistered: () => true, component: ( { + const { getIcon } = useIcons(); + const { actionMenuType } = useContext(ActionMenuContext); const { records: activeWorkflowVersions } = @@ -29,13 +32,18 @@ export const useRunWorkflowRecordAgnosticActions = () => { const name = capitalize(activeWorkflowVersion.workflow.name); + const Icon = getIcon( + activeWorkflowVersion.trigger?.settings.icon, + COMMAND_MENU_DEFAULT_ICON, + ); + return { type: ActionType.WorkflowRun, key: `workflow-run-${activeWorkflowVersion.id}`, scope: ActionScope.Global, label: msg`${name}`, position: index, - Icon: IconSettingsAutomation, + Icon, shouldBeRegistered: () => true, component: ( { const [searchString, setSearchString] = useState(''); const { @@ -171,9 +181,9 @@ export const IconPicker = ({ ...filteredAndSortedIconKeys.filter( (iconKey) => iconKey !== selectedIconKey, ), - ].slice(0, 25) - : filteredAndSortedIconKeys.slice(0, 25); - }, [icons, searchString, selectedIconKey]); + ].slice(0, maxIconsVisible) + : filteredAndSortedIconKeys.slice(0, maxIconsVisible); + }, [icons, searchString, selectedIconKey, maxIconsVisible]); const iconKeys2d = useMemo( () => arrayToChunks(matchingSearchIconKeys.slice(), 5), @@ -186,21 +196,26 @@ export const IconPicker = ({
+ clickableComponent || ( + + ) } dropdownComponents={ - + = { emptyOption?: SelectOption; fullWidth?: boolean; label?: string; + description?: string; onChange?: (value: Value) => void; onBlur?: () => void; options: SelectOption[]; @@ -64,6 +65,11 @@ const StyledLabel = styled.span` margin-bottom: ${({ theme }) => theme.spacing(1)}; `; +const StyledDescription = styled.span` + color: ${({ theme }) => theme.font.color.light}; + font-size: ${({ theme }) => theme.font.size.sm}; +`; + export const Select = ({ className, disabled: disabledFromProps, @@ -74,6 +80,7 @@ export const Select = ({ emptyOption, fullWidth, label, + description, onChange, onBlur, options, @@ -214,6 +221,7 @@ export const Select = ({ } /> )} + {!!description && {description}} ); }; diff --git a/packages/twenty-front/src/modules/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger.ts b/packages/twenty-front/src/modules/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger.ts index d1ac17946..5b9822db7 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger.ts @@ -3,7 +3,10 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; -import { Workflow, WorkflowVersion } from '@/workflow/types/Workflow'; +import { + ManualTriggerWorkflowVersion, + Workflow, +} from '@/workflow/types/Workflow'; import { isDefined } from 'twenty-shared/utils'; export const useActiveWorkflowVersionsWithManualTrigger = ({ @@ -40,7 +43,7 @@ export const useActiveWorkflowVersionsWithManualTrigger = ({ }); const { records } = useFindManyRecords< - WorkflowVersion & { workflow: Workflow } + ManualTriggerWorkflowVersion & { workflow: Workflow } >({ objectNameSingular: CoreObjectNameSingular.WorkflowVersion, filter: { diff --git a/packages/twenty-front/src/modules/workflow/types/Workflow.ts b/packages/twenty-front/src/modules/workflow/types/Workflow.ts index 1afe345a6..3d6575b11 100644 --- a/packages/twenty-front/src/modules/workflow/types/Workflow.ts +++ b/packages/twenty-front/src/modules/workflow/types/Workflow.ts @@ -114,6 +114,10 @@ export type WorkflowVersion = { __typename: 'WorkflowVersion'; }; +export type ManualTriggerWorkflowVersion = WorkflowVersion & { + trigger: WorkflowManualTrigger | null; +}; + export type WorkflowRunOutput = z.infer; export type WorkflowExecutorOutput = z.infer< typeof workflowExecutorOutputSchema diff --git a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts index 0d403e511..b740a993d 100644 --- a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts +++ b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts @@ -206,6 +206,7 @@ export const workflowManualTriggerSchema = baseTriggerSchema.extend({ settings: z.object({ objectType: z.string().optional(), outputSchema: z.object({}).passthrough(), + icon: z.string().optional(), }), }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm.tsx index 7d3cf5e5a..a78cc1006 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm.tsx @@ -16,6 +16,10 @@ import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; import { SelectOption } from 'twenty-ui/input'; import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useLingui } from '@lingui/react/macro'; +import { IconPicker } from '@/ui/input/components/IconPicker'; +import { SelectControl } from '@/ui/input/components/SelectControl'; +import styled from '@emotion/styled'; type WorkflowEditTriggerManualFormProps = { trigger: WorkflowManualTrigger; @@ -30,11 +34,32 @@ type WorkflowEditTriggerManualFormProps = { }; }; +const StyledLabel = styled.span` + color: ${({ theme }) => theme.font.color.light}; + font-size: ${({ theme }) => theme.font.size.xs}; + font-weight: ${({ theme }) => theme.font.weight.semiBold}; + margin-bottom: ${({ theme }) => theme.spacing(1)}; +`; + +const StyledDescription = styled.span` + color: ${({ theme }) => theme.font.color.light}; + font-size: ${({ theme }) => theme.font.size.sm}; + margin-top: ${({ theme }) => theme.spacing(0.25)}; +`; + +const StyledIconPickerContainer = styled.div` + display: flex; + flex-direction: column; +`; + export const WorkflowEditTriggerManualForm = ({ trigger, triggerOptions, }: WorkflowEditTriggerManualFormProps) => { const theme = useTheme(); + + const { t } = useLingui(); + const { getIcon } = useIcons(); const { activeNonSystemObjectMetadataItems } = @@ -47,16 +72,22 @@ export const WorkflowEditTriggerManualForm = ({ Icon: getIcon(item.icon), })); + const objectType = trigger.settings.objectType; + const manualTriggerAvailability: WorkflowManualTriggerAvailability = - isDefined(trigger.settings.objectType) - ? 'WHEN_RECORD_SELECTED' - : 'EVERYWHERE'; + isDefined(objectType) ? 'WHEN_RECORD_SELECTED' : 'EVERYWHERE'; const headerTitle = trigger.name ?? getTriggerDefaultLabel(trigger); const headerIcon = getTriggerIcon(trigger); + const headerType = getTriggerHeaderType(trigger); + const availabilityDescriptions = { + WHEN_RECORD_SELECTED: t`Select a record then open the ⌘K to trigger this workflow`, + EVERYWHERE: t`Open the ⌘K to trigger this workflow`, + }; + return ( <>