Only display Flow for Workflow Runs and display Output tab for triggers (#11520)
> [!WARNING] > I refactored a bunch of components into utility functions to make it possible to display the `WorkflowStepHeader` component for **triggers** in the `CommandMenuWorkflowRunViewStep` component. Previously, we were asserting that we were displaying the header in `Output` and `Input` tabs only for **actions**. Handling triggers too required a bunch of changes. We can think of making a bigger refactor of this part. In this PR: - Only display the Flow for Workflow Runs; removed the Code Editor tab - Allows users to see the Output of trigger nodes - Prevent impossible states by manually setting the selected tab when selecting a node ## Demo ### Success, Running and Not Executed steps https://github.com/user-attachments/assets/c6bebd0f-5da2-4ccc-aef2-d9890eafa59a ### Failed step https://github.com/user-attachments/assets/e1f4e13a-2f5e-4792-a089-928e4d6b1ac0 Closes https://github.com/twentyhq/core-team-issues/issues/709
This commit is contained in:
committed by
GitHub
parent
c8011da4d7
commit
e8488e1da0
@ -6,6 +6,7 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { CRON_TRIGGER_INTERVAL_OPTIONS } from '@/workflow/workflow-trigger/constants/CronTriggerIntervalOptions';
|
||||
import { getCronTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getCronTriggerDefaultSettings';
|
||||
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
|
||||
import { useTheme } from '@emotion/react';
|
||||
@ -48,18 +49,11 @@ export const WorkflowEditTriggerCronForm = ({
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const headerIcon = getTriggerIcon({
|
||||
type: 'CRON',
|
||||
});
|
||||
const headerIcon = getTriggerIcon(trigger);
|
||||
|
||||
const defaultLabel =
|
||||
getTriggerDefaultLabel({
|
||||
type: 'CRON',
|
||||
}) ?? '';
|
||||
|
||||
const headerTitle = isDefined(trigger.name) ? trigger.name : defaultLabel;
|
||||
|
||||
const headerType = 'Trigger';
|
||||
const defaultLabel = getTriggerDefaultLabel(trigger);
|
||||
const headerTitle = trigger.name ?? defaultLabel;
|
||||
const headerType = getTriggerHeaderType(trigger);
|
||||
|
||||
const onBlur = () => {
|
||||
setErrorMessagesVisible(true);
|
||||
|
||||
@ -12,6 +12,7 @@ import { WorkflowDatabaseEventTrigger } from '@/workflow/types/Workflow';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
|
||||
import { useTheme } from '@emotion/react';
|
||||
@ -105,18 +106,9 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
|
||||
[systemObjects, searchInputValue],
|
||||
);
|
||||
|
||||
const defaultLabel =
|
||||
getTriggerDefaultLabel({
|
||||
type: 'DATABASE_EVENT',
|
||||
eventName: triggerEvent.event,
|
||||
}) ?? '-';
|
||||
|
||||
const headerIcon = getTriggerIcon({
|
||||
type: 'DATABASE_EVENT',
|
||||
eventName: triggerEvent.event,
|
||||
});
|
||||
|
||||
const headerType = `Trigger · ${defaultLabel}`;
|
||||
const defaultLabel = trigger.name ?? getTriggerDefaultLabel(trigger);
|
||||
const headerIcon = getTriggerIcon(trigger);
|
||||
const headerType = getTriggerHeaderType(trigger);
|
||||
|
||||
const handleOptionClick = (value: string) => {
|
||||
if (triggerOptions.readonly === true) {
|
||||
|
||||
@ -8,11 +8,13 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { MANUAL_TRIGGER_AVAILABILITY_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerAvailabilityOptions';
|
||||
import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings';
|
||||
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
|
||||
type WorkflowEditTriggerManualFormProps = {
|
||||
trigger: WorkflowManualTrigger;
|
||||
@ -48,11 +50,10 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
? 'WHEN_RECORD_SELECTED'
|
||||
: 'EVERYWHERE';
|
||||
|
||||
const headerTitle = isDefined(trigger.name) ? trigger.name : 'Manual Trigger';
|
||||
const headerTitle = trigger.name ?? getTriggerDefaultLabel(trigger);
|
||||
|
||||
const headerIcon = getTriggerIcon({
|
||||
type: 'MANUAL',
|
||||
});
|
||||
const headerIcon = getTriggerIcon(trigger);
|
||||
const headerType = getTriggerHeaderType(trigger);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -70,7 +71,7 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={theme.font.color.tertiary}
|
||||
initialTitle={headerTitle}
|
||||
headerType="Trigger · Manual"
|
||||
headerType={headerType}
|
||||
disabled={triggerOptions.readonly}
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
import { WorkflowWebhookTrigger } from '@/workflow/types/Workflow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons, IconCopy } from 'twenty-ui/display';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { WEBHOOK_TRIGGER_HTTP_METHOD_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerHttpMethodOptions';
|
||||
import { getWebhookTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getWebhookTriggerDefaultSettings';
|
||||
import { WEBHOOK_TRIGGER_AUTHENTICATION_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerAuthenticationOptions';
|
||||
import { FormRawJsonFieldInput } from '@/object-record/record-field/form-types/components/FormRawJsonFieldInput';
|
||||
import { useState } from 'react';
|
||||
import { getFunctionOutputSchema } from '@/serverless-functions/utils/getFunctionOutputSchema';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { WorkflowWebhookTrigger } from '@/workflow/types/Workflow';
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { WEBHOOK_TRIGGER_AUTHENTICATION_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerAuthenticationOptions';
|
||||
import { WEBHOOK_TRIGGER_HTTP_METHOD_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerHttpMethodOptions';
|
||||
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
|
||||
import { getWebhookTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getWebhookTriggerDefaultSettings';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconCopy, useIcons } from 'twenty-ui/display';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
||||
type WorkflowEditTriggerWebhookFormProps = {
|
||||
trigger: WorkflowWebhookTrigger;
|
||||
@ -59,11 +61,10 @@ export const WorkflowEditTriggerWebhookForm = ({
|
||||
setErrorMessagesVisible(true);
|
||||
};
|
||||
|
||||
const headerTitle = isDefined(trigger.name) ? trigger.name : 'Webhook';
|
||||
const headerTitle = trigger.name ?? getTriggerDefaultLabel(trigger);
|
||||
|
||||
const headerIcon = getTriggerIcon({
|
||||
type: 'WEBHOOK',
|
||||
});
|
||||
const headerIcon = getTriggerIcon(trigger);
|
||||
const headerType = getTriggerHeaderType(trigger);
|
||||
|
||||
const webhookUrl = `${REACT_APP_SERVER_BASE_URL}/webhooks/workflows/${currentWorkspace?.id}/${workflowId}`;
|
||||
const displayWebhookUrl = webhookUrl.replace(/^(https?:\/\/)?(www\.)?/, '');
|
||||
@ -98,7 +99,7 @@ export const WorkflowEditTriggerWebhookForm = ({
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={theme.font.color.tertiary}
|
||||
initialTitle={headerTitle}
|
||||
headerType="Trigger · Webhook"
|
||||
headerType={headerType}
|
||||
disabled={triggerOptions.readonly}
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
import { WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
export const getTriggerHeaderType = (trigger: WorkflowTrigger) => {
|
||||
switch (trigger.type) {
|
||||
case 'CRON': {
|
||||
return 'Trigger';
|
||||
}
|
||||
case 'WEBHOOK': {
|
||||
return 'Trigger · Webhook';
|
||||
}
|
||||
case 'MANUAL': {
|
||||
return 'Trigger · Manual';
|
||||
}
|
||||
case 'DATABASE_EVENT': {
|
||||
const defaultLabel = getTriggerDefaultLabel(trigger);
|
||||
|
||||
return `Trigger · ${defaultLabel}`;
|
||||
}
|
||||
default: {
|
||||
assertUnreachable(trigger, 'Unknown trigger type');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,26 +1,18 @@
|
||||
import { WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
|
||||
import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/OtherTriggerTypes';
|
||||
|
||||
export const getTriggerIcon = (
|
||||
trigger:
|
||||
| {
|
||||
type: 'MANUAL';
|
||||
}
|
||||
| {
|
||||
type: 'CRON';
|
||||
}
|
||||
| {
|
||||
type: 'WEBHOOK';
|
||||
}
|
||||
| {
|
||||
type: 'DATABASE_EVENT';
|
||||
eventName: string;
|
||||
},
|
||||
trigger: WorkflowTrigger,
|
||||
): string | undefined => {
|
||||
if (trigger.type === 'DATABASE_EVENT') {
|
||||
return DATABASE_TRIGGER_TYPES.find(
|
||||
(type) => type.event === trigger.eventName,
|
||||
)?.icon;
|
||||
const eventName = splitWorkflowTriggerEventName(
|
||||
trigger.settings.eventName,
|
||||
).event;
|
||||
|
||||
return DATABASE_TRIGGER_TYPES.find((type) => type.event === eventName)
|
||||
?.icon;
|
||||
}
|
||||
|
||||
return OTHER_TRIGGER_TYPES.find((item) => item.type === trigger.type)?.icon;
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
import { Theme } from '@emotion/react';
|
||||
|
||||
export const getTriggerIconColor = ({ theme }: { theme: Theme }) => {
|
||||
return theme.font.color.tertiary;
|
||||
};
|
||||
@ -1,28 +1,33 @@
|
||||
import { WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
|
||||
import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/OtherTriggerTypes';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const getTriggerDefaultLabel = (
|
||||
trigger:
|
||||
| {
|
||||
type: 'MANUAL';
|
||||
}
|
||||
| {
|
||||
type: 'CRON';
|
||||
}
|
||||
| {
|
||||
type: 'WEBHOOK';
|
||||
}
|
||||
| {
|
||||
type: 'DATABASE_EVENT';
|
||||
eventName: string;
|
||||
},
|
||||
): string | undefined => {
|
||||
export const getTriggerDefaultLabel = (trigger: WorkflowTrigger): string => {
|
||||
if (trigger.type === 'DATABASE_EVENT') {
|
||||
return DATABASE_TRIGGER_TYPES.find(
|
||||
(type) => type.event === trigger.eventName,
|
||||
const triggerEvent = splitWorkflowTriggerEventName(
|
||||
trigger.settings.eventName,
|
||||
);
|
||||
|
||||
const label = DATABASE_TRIGGER_TYPES.find(
|
||||
(type) => type.event === triggerEvent.event,
|
||||
)?.defaultLabel;
|
||||
|
||||
if (!isDefined(label)) {
|
||||
throw new Error('Unknown trigger event');
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
return OTHER_TRIGGER_TYPES.find((item) => item.type === trigger.type)
|
||||
?.defaultLabel;
|
||||
const label = OTHER_TRIGGER_TYPES.find(
|
||||
(item) => item.type === trigger.type,
|
||||
)?.defaultLabel;
|
||||
|
||||
if (!isDefined(label)) {
|
||||
throw new Error('Unknown trigger type');
|
||||
}
|
||||
|
||||
return label;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user