Add icon select to manual trigger (#12724)

## After

<img width="1220" alt="image"
src="https://github.com/user-attachments/assets/98a73aae-80d7-4e92-93d3-be13210da88b"
/>

<img width="1131" alt="image"
src="https://github.com/user-attachments/assets/9919e415-4355-4995-8979-9055b821f1e9"
/>

<img width="1300" alt="image"
src="https://github.com/user-attachments/assets/27f11cb3-d72c-468a-a641-8414172b9b54"
/>

<img width="1353" alt="image"
src="https://github.com/user-attachments/assets/2f0037f2-fe17-48b6-b7e6-c7528687a5fd"
/>
This commit is contained in:
martmull
2025-06-19 18:32:42 +02:00
committed by GitHub
parent adcf6107e7
commit 28466ecbf3
17 changed files with 208 additions and 69 deletions

View File

@ -11,7 +11,8 @@ import { msg } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { capitalize, isDefined } from 'twenty-shared/utils'; 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 = ({ export const useRunWorkflowRecordActions = ({
objectMetadataItem, objectMetadataItem,
@ -20,6 +21,7 @@ export const useRunWorkflowRecordActions = ({
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
skip?: boolean; skip?: boolean;
}) => { }) => {
const { getIcon } = useIcons();
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2( const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState, contextStoreTargetedRecordsRuleComponentState,
); );
@ -48,13 +50,18 @@ export const useRunWorkflowRecordActions = ({
.map((activeWorkflowVersion, index) => { .map((activeWorkflowVersion, index) => {
const name = capitalize(activeWorkflowVersion.workflow.name); const name = capitalize(activeWorkflowVersion.workflow.name);
const Icon = getIcon(
activeWorkflowVersion.trigger?.settings.icon,
COMMAND_MENU_DEFAULT_ICON,
);
return { return {
type: ActionType.WorkflowRun, type: ActionType.WorkflowRun,
key: `workflow-run-${activeWorkflowVersion.id}`, key: `workflow-run-${activeWorkflowVersion.id}`,
scope: ActionScope.RecordSelection, scope: ActionScope.RecordSelection,
label: msg`${name}`, label: msg`${name}`,
position: index, position: index,
Icon: IconSettingsAutomation, Icon,
shouldBeRegistered: () => true, shouldBeRegistered: () => true,
component: ( component: (
<Action <Action

View File

@ -7,9 +7,12 @@ import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useContext } from 'react'; import { useContext } from 'react';
import { capitalize, isDefined } from 'twenty-shared/utils'; 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 useRunWorkflowRecordAgnosticActions = () => { export const useRunWorkflowRecordAgnosticActions = () => {
const { getIcon } = useIcons();
const { actionMenuType } = useContext(ActionMenuContext); const { actionMenuType } = useContext(ActionMenuContext);
const { records: activeWorkflowVersions } = const { records: activeWorkflowVersions } =
@ -29,13 +32,18 @@ export const useRunWorkflowRecordAgnosticActions = () => {
const name = capitalize(activeWorkflowVersion.workflow.name); const name = capitalize(activeWorkflowVersion.workflow.name);
const Icon = getIcon(
activeWorkflowVersion.trigger?.settings.icon,
COMMAND_MENU_DEFAULT_ICON,
);
return { return {
type: ActionType.WorkflowRun, type: ActionType.WorkflowRun,
key: `workflow-run-${activeWorkflowVersion.id}`, key: `workflow-run-${activeWorkflowVersion.id}`,
scope: ActionScope.Global, scope: ActionScope.Global,
label: msg`${name}`, label: msg`${name}`,
position: index, position: index,
Icon: IconSettingsAutomation, Icon,
shouldBeRegistered: () => true, shouldBeRegistered: () => true,
component: ( component: (
<Action <Action

View File

@ -1,5 +1,5 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useMemo, useState } from 'react'; import { ReactNode, useMemo, useState } from 'react';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -25,6 +25,8 @@ import {
LightIconButton, LightIconButton,
} from 'twenty-ui/input'; } from 'twenty-ui/input';
import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope'; import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
export type IconPickerProps = { export type IconPickerProps = {
disabled?: boolean; disabled?: boolean;
dropdownId?: string; dropdownId?: string;
@ -36,6 +38,10 @@ export type IconPickerProps = {
variant?: IconButtonVariant; variant?: IconButtonVariant;
className?: string; className?: string;
size?: IconButtonSize; size?: IconButtonSize;
clickableComponent?: ReactNode;
dropdownWidth?: number;
dropdownOffset?: DropdownOffset;
maxIconsVisible?: number;
}; };
const StyledMenuIconItemsContainer = styled.div` const StyledMenuIconItemsContainer = styled.div`
@ -102,6 +108,10 @@ export const IconPicker = ({
variant = 'secondary', variant = 'secondary',
className, className,
size = 'medium', size = 'medium',
clickableComponent,
dropdownWidth,
dropdownOffset,
maxIconsVisible = 25,
}: IconPickerProps) => { }: IconPickerProps) => {
const [searchString, setSearchString] = useState(''); const [searchString, setSearchString] = useState('');
const { const {
@ -171,9 +181,9 @@ export const IconPicker = ({
...filteredAndSortedIconKeys.filter( ...filteredAndSortedIconKeys.filter(
(iconKey) => iconKey !== selectedIconKey, (iconKey) => iconKey !== selectedIconKey,
), ),
].slice(0, 25) ].slice(0, maxIconsVisible)
: filteredAndSortedIconKeys.slice(0, 25); : filteredAndSortedIconKeys.slice(0, maxIconsVisible);
}, [icons, searchString, selectedIconKey]); }, [icons, searchString, selectedIconKey, maxIconsVisible]);
const iconKeys2d = useMemo( const iconKeys2d = useMemo(
() => arrayToChunks(matchingSearchIconKeys.slice(), 5), () => arrayToChunks(matchingSearchIconKeys.slice(), 5),
@ -186,21 +196,26 @@ export const IconPicker = ({
<div className={className}> <div className={className}>
<Dropdown <Dropdown
dropdownId={dropdownId} dropdownId={dropdownId}
dropdownOffset={dropdownOffset}
clickableComponent={ clickableComponent={
<IconButton clickableComponent || (
ariaLabel={`Click to select icon ${ <IconButton
selectedIconKey ariaLabel={`Click to select icon ${
? `(selected: ${selectedIconKey})` selectedIconKey
: `(no icon selected)` ? `(selected: ${selectedIconKey})`
}`} : `(no icon selected)`
disabled={disabled} }`}
Icon={icon} disabled={disabled}
variant={variant} Icon={icon}
size={size} variant={variant}
/> size={size}
/>
)
} }
dropdownComponents={ dropdownComponents={
<DropdownContent widthInPixels={ICON_PICKER_DROPDOWN_CONTENT_WIDTH}> <DropdownContent
widthInPixels={dropdownWidth || ICON_PICKER_DROPDOWN_CONTENT_WIDTH}
>
<SelectableList <SelectableList
selectableListInstanceId="icon-list" selectableListInstanceId="icon-list"
selectableItemIdMatrix={iconKeys2d} selectableItemIdMatrix={iconKeys2d}

View File

@ -41,6 +41,7 @@ export type SelectProps<Value extends SelectValue> = {
emptyOption?: SelectOption<Value>; emptyOption?: SelectOption<Value>;
fullWidth?: boolean; fullWidth?: boolean;
label?: string; label?: string;
description?: string;
onChange?: (value: Value) => void; onChange?: (value: Value) => void;
onBlur?: () => void; onBlur?: () => void;
options: SelectOption<Value>[]; options: SelectOption<Value>[];
@ -64,6 +65,11 @@ const StyledLabel = styled.span`
margin-bottom: ${({ theme }) => theme.spacing(1)}; 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 = <Value extends SelectValue>({ export const Select = <Value extends SelectValue>({
className, className,
disabled: disabledFromProps, disabled: disabledFromProps,
@ -74,6 +80,7 @@ export const Select = <Value extends SelectValue>({
emptyOption, emptyOption,
fullWidth, fullWidth,
label, label,
description,
onChange, onChange,
onBlur, onBlur,
options, options,
@ -214,6 +221,7 @@ export const Select = <Value extends SelectValue>({
} }
/> />
)} )}
{!!description && <StyledDescription>{description}</StyledDescription>}
</StyledContainer> </StyledContainer>
); );
}; };

View File

@ -3,7 +3,10 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; 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'; import { isDefined } from 'twenty-shared/utils';
export const useActiveWorkflowVersionsWithManualTrigger = ({ export const useActiveWorkflowVersionsWithManualTrigger = ({
@ -40,7 +43,7 @@ export const useActiveWorkflowVersionsWithManualTrigger = ({
}); });
const { records } = useFindManyRecords< const { records } = useFindManyRecords<
WorkflowVersion & { workflow: Workflow } ManualTriggerWorkflowVersion & { workflow: Workflow }
>({ >({
objectNameSingular: CoreObjectNameSingular.WorkflowVersion, objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
filter: { filter: {

View File

@ -114,6 +114,10 @@ export type WorkflowVersion = {
__typename: 'WorkflowVersion'; __typename: 'WorkflowVersion';
}; };
export type ManualTriggerWorkflowVersion = WorkflowVersion & {
trigger: WorkflowManualTrigger | null;
};
export type WorkflowRunOutput = z.infer<typeof workflowRunOutputSchema>; export type WorkflowRunOutput = z.infer<typeof workflowRunOutputSchema>;
export type WorkflowExecutorOutput = z.infer< export type WorkflowExecutorOutput = z.infer<
typeof workflowExecutorOutputSchema typeof workflowExecutorOutputSchema

View File

@ -206,6 +206,7 @@ export const workflowManualTriggerSchema = baseTriggerSchema.extend({
settings: z.object({ settings: z.object({
objectType: z.string().optional(), objectType: z.string().optional(),
outputSchema: z.object({}).passthrough(), outputSchema: z.object({}).passthrough(),
icon: z.string().optional(),
}), }),
}); });

View File

@ -16,6 +16,10 @@ import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display'; import { useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input'; import { SelectOption } from 'twenty-ui/input';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; 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 = { type WorkflowEditTriggerManualFormProps = {
trigger: WorkflowManualTrigger; 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 = ({ export const WorkflowEditTriggerManualForm = ({
trigger, trigger,
triggerOptions, triggerOptions,
}: WorkflowEditTriggerManualFormProps) => { }: WorkflowEditTriggerManualFormProps) => {
const theme = useTheme(); const theme = useTheme();
const { t } = useLingui();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } = const { activeNonSystemObjectMetadataItems } =
@ -47,16 +72,22 @@ export const WorkflowEditTriggerManualForm = ({
Icon: getIcon(item.icon), Icon: getIcon(item.icon),
})); }));
const objectType = trigger.settings.objectType;
const manualTriggerAvailability: WorkflowManualTriggerAvailability = const manualTriggerAvailability: WorkflowManualTriggerAvailability =
isDefined(trigger.settings.objectType) isDefined(objectType) ? 'WHEN_RECORD_SELECTED' : 'EVERYWHERE';
? 'WHEN_RECORD_SELECTED'
: 'EVERYWHERE';
const headerTitle = trigger.name ?? getTriggerDefaultLabel(trigger); const headerTitle = trigger.name ?? getTriggerDefaultLabel(trigger);
const headerIcon = getTriggerIcon(trigger); const headerIcon = getTriggerIcon(trigger);
const headerType = getTriggerHeaderType(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 ( return (
<> <>
<WorkflowStepHeader <WorkflowStepHeader
@ -78,8 +109,9 @@ export const WorkflowEditTriggerManualForm = ({
/> />
<WorkflowStepBody> <WorkflowStepBody>
<Select <Select
dropdownId="workflow-edit-manual-trigger-availability" dropdownId={'workflow-edit-manual-trigger-availability'}
label="Available" label={t`Available`}
description={availabilityDescriptions[manualTriggerAvailability]}
fullWidth fullWidth
disabled={triggerOptions.readonly} disabled={triggerOptions.readonly}
value={manualTriggerAvailability} value={manualTriggerAvailability}
@ -94,6 +126,7 @@ export const WorkflowEditTriggerManualForm = ({
settings: getManualTriggerDefaultSettings({ settings: getManualTriggerDefaultSettings({
availability: updatedTriggerType, availability: updatedTriggerType,
activeNonSystemObjectMetadataItems, activeNonSystemObjectMetadataItems,
icon: trigger.settings.icon,
}), }),
}); });
}} }}
@ -103,10 +136,11 @@ export const WorkflowEditTriggerManualForm = ({
{manualTriggerAvailability === 'WHEN_RECORD_SELECTED' ? ( {manualTriggerAvailability === 'WHEN_RECORD_SELECTED' ? (
<Select <Select
dropdownId="workflow-edit-manual-trigger-object" dropdownId={'workflow-edit-manual-trigger-object'}
label="Object" label={t`Object`}
description={t`Will return one ${objectType} to the next step of this workflow`}
fullWidth fullWidth
value={trigger.settings.objectType} value={objectType}
options={availableMetadata} options={availableMetadata}
disabled={triggerOptions.readonly} disabled={triggerOptions.readonly}
onChange={(updatedObject) => { onChange={(updatedObject) => {
@ -117,6 +151,7 @@ export const WorkflowEditTriggerManualForm = ({
triggerOptions.onTriggerUpdate({ triggerOptions.onTriggerUpdate({
...trigger, ...trigger,
settings: { settings: {
...trigger.settings,
objectType: updatedObject, objectType: updatedObject,
outputSchema: {}, outputSchema: {},
}, },
@ -126,6 +161,48 @@ export const WorkflowEditTriggerManualForm = ({
dropdownWidth={GenericDropdownContentWidth.ExtraLarge} dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/> />
) : null} ) : null}
<IconPicker
dropdownId={'workflow-edit-manual-trigger-icon'}
selectedIconKey={trigger.settings.icon}
dropdownOffset={{ y: -parseInt(theme.spacing(3), 10) }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
maxIconsVisible={9 * 8} // 9 columns * 8 lines
disabled={triggerOptions.readonly}
clickableComponent={
<StyledIconPickerContainer
onClick={(e) => {
if (triggerOptions.readonly === true) {
e.stopPropagation();
e.preventDefault();
}
}}
>
<StyledLabel>{t`Command menu icon`}</StyledLabel>
<SelectControl
isDisabled={triggerOptions.readonly}
selectedOption={{
Icon: getIcon(trigger.settings.icon),
value: trigger.settings.icon || null,
label: '',
}}
/>
<StyledDescription>{t`The icon your workflow trigger will display in the command menu`}</StyledDescription>
</StyledIconPickerContainer>
}
onChange={({ iconKey }) => {
if (triggerOptions.readonly === true) {
return;
}
triggerOptions.onTriggerUpdate({
...trigger,
settings: {
...trigger.settings,
icon: iconKey,
},
});
}}
/>
</WorkflowStepBody> </WorkflowStepBody>
</> </>
); );

View File

@ -0,0 +1 @@
export const COMMAND_MENU_DEFAULT_ICON = 'IconHandMove';

View File

@ -7,12 +7,12 @@ export const MANUAL_TRIGGER_AVAILABILITY_OPTIONS: Array<{
Icon: IconComponent; Icon: IconComponent;
}> = [ }> = [
{ {
label: 'When record(s) are selected', label: 'When record is selected',
value: 'WHEN_RECORD_SELECTED', value: 'WHEN_RECORD_SELECTED',
Icon: IconCheckbox, Icon: IconCheckbox,
}, },
{ {
label: 'When no record(s) are selected', label: 'When no record is selected',
value: 'EVERYWHERE', value: 'EVERYWHERE',
Icon: IconSquare, Icon: IconSquare,
}, },

View File

@ -1,5 +1,6 @@
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { getManualTriggerDefaultSettings } from '../getManualTriggerDefaultSettings'; import { getManualTriggerDefaultSettings } from '../getManualTriggerDefaultSettings';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
it('returns settings for a manual trigger that can be activated from any where', () => { it('returns settings for a manual trigger that can be activated from any where', () => {
expect( expect(
@ -10,6 +11,7 @@ it('returns settings for a manual trigger that can be activated from any where',
).toStrictEqual({ ).toStrictEqual({
objectType: undefined, objectType: undefined,
outputSchema: {}, outputSchema: {},
icon: COMMAND_MENU_DEFAULT_ICON,
}); });
}); });
@ -18,9 +20,11 @@ it('returns settings for a manual trigger that can be activated from any where',
getManualTriggerDefaultSettings({ getManualTriggerDefaultSettings({
availability: 'WHEN_RECORD_SELECTED', availability: 'WHEN_RECORD_SELECTED',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems, activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
icon: 'IconTest',
}), }),
).toStrictEqual({ ).toStrictEqual({
objectType: generatedMockObjectMetadataItems[0].nameSingular, objectType: generatedMockObjectMetadataItems[0].nameSingular,
outputSchema: {}, outputSchema: {},
icon: 'IconTest',
}); });
}); });

View File

@ -1,6 +1,7 @@
import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel'; import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { getTriggerDefaultDefinition } from '../getTriggerDefaultDefinition'; import { getTriggerDefaultDefinition } from '../getTriggerDefaultDefinition';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
describe('getTriggerDefaultDefinition', () => { describe('getTriggerDefaultDefinition', () => {
it('throws if the activeNonSystemObjectMetadataItems list is empty', () => { it('throws if the activeNonSystemObjectMetadataItems list is empty', () => {
@ -94,6 +95,7 @@ describe('getTriggerDefaultDefinition', () => {
settings: { settings: {
objectType: generatedMockObjectMetadataItems[0].nameSingular, objectType: generatedMockObjectMetadataItems[0].nameSingular,
outputSchema: {}, outputSchema: {},
icon: COMMAND_MENU_DEFAULT_ICON,
}, },
}); });
}); });

View File

@ -4,25 +4,30 @@ import {
WorkflowManualTriggerSettings, WorkflowManualTriggerSettings,
} from '@/workflow/types/Workflow'; } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
export const getManualTriggerDefaultSettings = ({ export const getManualTriggerDefaultSettings = ({
availability, availability,
activeNonSystemObjectMetadataItems, activeNonSystemObjectMetadataItems,
icon,
}: { }: {
availability: WorkflowManualTriggerAvailability; availability: WorkflowManualTriggerAvailability;
activeNonSystemObjectMetadataItems: ObjectMetadataItem[]; activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
icon?: string;
}): WorkflowManualTriggerSettings => { }): WorkflowManualTriggerSettings => {
switch (availability) { switch (availability) {
case 'EVERYWHERE': { case 'EVERYWHERE': {
return { return {
objectType: undefined, objectType: undefined,
outputSchema: {}, outputSchema: {},
icon: icon || COMMAND_MENU_DEFAULT_ICON,
}; };
} }
case 'WHEN_RECORD_SELECTED': { case 'WHEN_RECORD_SELECTED': {
return { return {
objectType: activeNonSystemObjectMetadataItems[0].nameSingular, objectType: activeNonSystemObjectMetadataItems[0].nameSingular,
outputSchema: {}, outputSchema: {},
icon: icon || COMMAND_MENU_DEFAULT_ICON,
}; };
} }
} }

View File

@ -63,7 +63,7 @@ export const prefillWorkflows = async (
trigger: JSON.stringify({ trigger: JSON.stringify({
name: 'Launch manually', name: 'Launch manually',
type: 'MANUAL', type: 'MANUAL',
settings: { outputSchema: {} }, settings: { outputSchema: {}, icon: 'IconUserPlus' },
}), }),
steps: JSON.stringify([ steps: JSON.stringify([
{ {

View File

@ -20,7 +20,7 @@ type BaseTrigger = {
export type WorkflowDatabaseEventTrigger = BaseTrigger & { export type WorkflowDatabaseEventTrigger = BaseTrigger & {
type: WorkflowTriggerType.DATABASE_EVENT; type: WorkflowTriggerType.DATABASE_EVENT;
settings: { settings: BaseWorkflowTriggerSettings & {
eventName: string; eventName: string;
}; };
}; };
@ -32,45 +32,49 @@ export enum WorkflowManualTriggerAvailability {
export type WorkflowManualTrigger = BaseTrigger & { export type WorkflowManualTrigger = BaseTrigger & {
type: WorkflowTriggerType.MANUAL; type: WorkflowTriggerType.MANUAL;
settings: { settings: BaseWorkflowTriggerSettings & {
objectType?: string; objectType?: string;
icon?: string;
}; };
}; };
export type WorkflowCronTrigger = BaseTrigger & { export type WorkflowCronTrigger = BaseTrigger & {
type: WorkflowTriggerType.CRON; type: WorkflowTriggerType.CRON;
settings: ( settings: BaseWorkflowTriggerSettings &
| { (
type: 'DAYS'; | {
schedule: { day: number; hour: number; minute: number }; type: 'DAYS';
} schedule: { day: number; hour: number; minute: number };
| { }
type: 'HOURS'; | {
schedule: { hour: number; minute: number }; type: 'HOURS';
} schedule: { hour: number; minute: number };
| { }
type: 'MINUTES'; | {
schedule: { minute: number }; type: 'MINUTES';
} schedule: { minute: number };
| { }
type: 'CUSTOM'; | {
pattern: string; type: 'CUSTOM';
} pattern: string;
) & { outputSchema: object }; }
);
}; };
export type WorkflowWebhookTrigger = BaseTrigger & { export type WorkflowWebhookTrigger = BaseTrigger & {
type: WorkflowTriggerType.WEBHOOK; type: WorkflowTriggerType.WEBHOOK;
settings: settings: BaseWorkflowTriggerSettings &
| { (
httpMethod: 'GET'; | {
authentication: 'API_KEY' | null; httpMethod: 'GET';
} authentication: 'API_KEY' | null;
| ({ }
httpMethod: 'POST'; | {
authentication: 'API_KEY' | null; httpMethod: 'POST';
expectedBody: object; authentication: 'API_KEY' | null;
} & { outputSchema: object }); expectedBody: object;
}
);
}; };
export type WorkflowManualTriggerSettings = WorkflowManualTrigger['settings']; export type WorkflowManualTriggerSettings = WorkflowManualTrigger['settings'];

View File

@ -11,12 +11,10 @@ export const useIcons = () => {
return icons; return icons;
}; };
const getIcon = (iconKey?: string | null) => { const getIcon = (iconKey?: string | null, customDefaultIcon?: string) => {
if (!iconKey) { return (
return defaultIcon; icons[iconKey ?? ''] || icons[customDefaultIcon ?? ''] || defaultIcon
} );
return icons[iconKey] ?? defaultIcon;
}; };
return { getIcons, getIcon }; return { getIcons, getIcon };

View File

@ -1,7 +1,9 @@
import { FunctionComponent } from 'react'; import { FunctionComponent } from 'react';
import * as React from 'react';
export type IconComponentProps = { export type IconComponentProps = {
className?: string; className?: string;
style?: React.CSSProperties;
size?: number | string; size?: number | string;
stroke?: number | string; stroke?: number | string;
color?: string; color?: string;