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:
@ -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: {
|
||||
|
||||
@ -114,6 +114,10 @@ export type WorkflowVersion = {
|
||||
__typename: 'WorkflowVersion';
|
||||
};
|
||||
|
||||
export type ManualTriggerWorkflowVersion = WorkflowVersion & {
|
||||
trigger: WorkflowManualTrigger | null;
|
||||
};
|
||||
|
||||
export type WorkflowRunOutput = z.infer<typeof workflowRunOutputSchema>;
|
||||
export type WorkflowExecutorOutput = z.infer<
|
||||
typeof workflowExecutorOutputSchema
|
||||
|
||||
@ -206,6 +206,7 @@ export const workflowManualTriggerSchema = baseTriggerSchema.extend({
|
||||
settings: z.object({
|
||||
objectType: z.string().optional(),
|
||||
outputSchema: z.object({}).passthrough(),
|
||||
icon: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
@ -78,8 +109,9 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
<Select
|
||||
dropdownId="workflow-edit-manual-trigger-availability"
|
||||
label="Available"
|
||||
dropdownId={'workflow-edit-manual-trigger-availability'}
|
||||
label={t`Available`}
|
||||
description={availabilityDescriptions[manualTriggerAvailability]}
|
||||
fullWidth
|
||||
disabled={triggerOptions.readonly}
|
||||
value={manualTriggerAvailability}
|
||||
@ -94,6 +126,7 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
settings: getManualTriggerDefaultSettings({
|
||||
availability: updatedTriggerType,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
icon: trigger.settings.icon,
|
||||
}),
|
||||
});
|
||||
}}
|
||||
@ -103,10 +136,11 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
|
||||
{manualTriggerAvailability === 'WHEN_RECORD_SELECTED' ? (
|
||||
<Select
|
||||
dropdownId="workflow-edit-manual-trigger-object"
|
||||
label="Object"
|
||||
dropdownId={'workflow-edit-manual-trigger-object'}
|
||||
label={t`Object`}
|
||||
description={t`Will return one ${objectType} to the next step of this workflow`}
|
||||
fullWidth
|
||||
value={trigger.settings.objectType}
|
||||
value={objectType}
|
||||
options={availableMetadata}
|
||||
disabled={triggerOptions.readonly}
|
||||
onChange={(updatedObject) => {
|
||||
@ -117,6 +151,7 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
triggerOptions.onTriggerUpdate({
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
objectType: updatedObject,
|
||||
outputSchema: {},
|
||||
},
|
||||
@ -126,6 +161,48 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
|
||||
/>
|
||||
) : 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>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const COMMAND_MENU_DEFAULT_ICON = 'IconHandMove';
|
||||
@ -7,12 +7,12 @@ export const MANUAL_TRIGGER_AVAILABILITY_OPTIONS: Array<{
|
||||
Icon: IconComponent;
|
||||
}> = [
|
||||
{
|
||||
label: 'When record(s) are selected',
|
||||
label: 'When record is selected',
|
||||
value: 'WHEN_RECORD_SELECTED',
|
||||
Icon: IconCheckbox,
|
||||
},
|
||||
{
|
||||
label: 'When no record(s) are selected',
|
||||
label: 'When no record is selected',
|
||||
value: 'EVERYWHERE',
|
||||
Icon: IconSquare,
|
||||
},
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
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', () => {
|
||||
expect(
|
||||
@ -10,6 +11,7 @@ it('returns settings for a manual trigger that can be activated from any where',
|
||||
).toStrictEqual({
|
||||
objectType: undefined,
|
||||
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({
|
||||
availability: 'WHEN_RECORD_SELECTED',
|
||||
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
|
||||
icon: 'IconTest',
|
||||
}),
|
||||
).toStrictEqual({
|
||||
objectType: generatedMockObjectMetadataItems[0].nameSingular,
|
||||
outputSchema: {},
|
||||
icon: 'IconTest',
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getTriggerDefaultDefinition } from '../getTriggerDefaultDefinition';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
|
||||
describe('getTriggerDefaultDefinition', () => {
|
||||
it('throws if the activeNonSystemObjectMetadataItems list is empty', () => {
|
||||
@ -94,6 +95,7 @@ describe('getTriggerDefaultDefinition', () => {
|
||||
settings: {
|
||||
objectType: generatedMockObjectMetadataItems[0].nameSingular,
|
||||
outputSchema: {},
|
||||
icon: COMMAND_MENU_DEFAULT_ICON,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,25 +4,30 @@ import {
|
||||
WorkflowManualTriggerSettings,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
|
||||
export const getManualTriggerDefaultSettings = ({
|
||||
availability,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
icon,
|
||||
}: {
|
||||
availability: WorkflowManualTriggerAvailability;
|
||||
activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
|
||||
icon?: string;
|
||||
}): WorkflowManualTriggerSettings => {
|
||||
switch (availability) {
|
||||
case 'EVERYWHERE': {
|
||||
return {
|
||||
objectType: undefined,
|
||||
outputSchema: {},
|
||||
icon: icon || COMMAND_MENU_DEFAULT_ICON,
|
||||
};
|
||||
}
|
||||
case 'WHEN_RECORD_SELECTED': {
|
||||
return {
|
||||
objectType: activeNonSystemObjectMetadataItems[0].nameSingular,
|
||||
outputSchema: {},
|
||||
icon: icon || COMMAND_MENU_DEFAULT_ICON,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user