Remove iterator feature flag (#15165)

As title
This commit is contained in:
Thomas Trompette
2025-10-17 11:19:39 +02:00
committed by GitHub
parent d2e7f2a910
commit 91b54e7e68
21 changed files with 30 additions and 572 deletions
@@ -1,244 +0,0 @@
import { SidePanelHeader } from '@/command-menu/components/SidePanelHeader';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { IconPicker } from '@/ui/input/components/IconPicker';
import { Select } from '@/ui/input/components/Select';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import {
type WorkflowManualTrigger,
type WorkflowManualTriggerAvailability,
} from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { MANUAL_TRIGGER_AVAILABILITY_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerAvailabilityOptions';
import { MANUAL_TRIGGER_IS_PINNED_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerIsPinnedOptions';
import { getManualTriggerDefaultSettingsDeprecated } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettingsDeprecated';
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerDefaultLabel';
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import { getTriggerIconColor } from '@/workflow/workflow-trigger/utils/getTriggerIconColor';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
import { type SelectOption } from 'twenty-ui/input';
type WorkflowEditTriggerManualDeprecatedProps = {
trigger: WorkflowManualTrigger;
triggerOptions:
| {
readonly: true;
onTriggerUpdate?: undefined;
}
| {
readonly?: false;
onTriggerUpdate: (trigger: WorkflowManualTrigger) => void;
};
};
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 WorkflowEditTriggerManualDeprecated = ({
trigger,
triggerOptions,
}: WorkflowEditTriggerManualDeprecatedProps) => {
const theme = useTheme();
const { t } = useLingui();
const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const availableMetadata: Array<SelectOption<string>> =
activeNonSystemObjectMetadataItems.map((item) => ({
label: item.labelPlural,
value: item.nameSingular,
Icon: getIcon(item.icon),
}));
const objectType = trigger.settings.objectType;
const manualTriggerAvailability: WorkflowManualTriggerAvailability =
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`The selected record(s) will be passed to your workflow`,
EVERYWHERE: t`Open the ⌘K to trigger this workflow`,
};
return (
<>
<SidePanelHeader
onTitleChange={(newName: string) => {
if (triggerOptions.readonly === true) {
return;
}
triggerOptions.onTriggerUpdate({
...trigger,
name: newName,
});
}}
Icon={getIcon(headerIcon)}
iconColor={getTriggerIconColor({ theme, triggerType: trigger.type })}
initialTitle={headerTitle}
headerType={headerType}
disabled={triggerOptions.readonly}
/>
<WorkflowStepBody>
<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 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,
},
});
}}
/>
<Select
dropdownId="workflow-edit-manual-trigger-availability"
label={t`Availability`}
description={availabilityDescriptions[manualTriggerAvailability]}
fullWidth
disabled={triggerOptions.readonly}
value={manualTriggerAvailability}
options={MANUAL_TRIGGER_AVAILABILITY_OPTIONS}
onChange={(updatedTriggerType) => {
if (triggerOptions.readonly === true) {
return;
}
triggerOptions.onTriggerUpdate({
...trigger,
settings: getManualTriggerDefaultSettingsDeprecated({
availability: updatedTriggerType,
activeNonSystemObjectMetadataItems,
icon: trigger.settings.icon,
}),
});
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
{manualTriggerAvailability === 'WHEN_RECORD_SELECTED' ? (
<Select
dropdownId="workflow-edit-manual-trigger-object"
label={t`Object`}
description={t`On which object(s) should this trigger be available`}
fullWidth
value={objectType}
options={availableMetadata}
disabled={triggerOptions.readonly}
onChange={(updatedObject) => {
if (triggerOptions.readonly === true) {
return;
}
triggerOptions.onTriggerUpdate({
...trigger,
settings: {
...trigger.settings,
availability: {
objectNameSingular: updatedObject,
type: 'SINGLE_RECORD',
},
objectType: updatedObject,
outputSchema: {},
},
});
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
) : null}
{manualTriggerAvailability === 'WHEN_RECORD_SELECTED' ? (
<Select
dropdownId="workflow-edit-manual-trigger-navbar"
label={t`Navbar`}
description={t`Display a button in the top navbar to trigger this workflow`}
fullWidth
value={trigger.settings.isPinned}
options={MANUAL_TRIGGER_IS_PINNED_OPTIONS}
disabled={triggerOptions.readonly}
onChange={(updatedValue) => {
if (triggerOptions.readonly === true) {
return;
}
triggerOptions.onTriggerUpdate({
...trigger,
settings: {
...trigger.settings,
isPinned: updatedValue,
outputSchema: {},
},
});
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
) : null}
</WorkflowStepBody>
</>
);
};
@@ -1,73 +0,0 @@
import { type WorkflowManualTriggerAvailability } from '@/workflow/types/Workflow';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
import { getManualTriggerDefaultSettingsDeprecated } from '../getManualTriggerDefaultSettingsDeprecated';
describe('getManualTriggerDefaultSettingsDeprecated', () => {
it('returns settings for a manual trigger that can be activated from any where', () => {
expect(
getManualTriggerDefaultSettingsDeprecated({
availability: 'EVERYWHERE',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
}),
).toStrictEqual({
objectType: undefined,
outputSchema: {},
icon: COMMAND_MENU_DEFAULT_ICON,
isPinned: false,
availability: {
type: 'GLOBAL',
locations: [],
},
});
});
it('returns settings for a manual trigger that can be activated from any where', () => {
expect(
getManualTriggerDefaultSettingsDeprecated({
availability: 'WHEN_RECORD_SELECTED',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
icon: 'IconTest',
}),
).toStrictEqual({
objectType: generatedMockObjectMetadataItems[0].nameSingular,
outputSchema: {},
icon: 'IconTest',
isPinned: false,
availability: {
type: 'SINGLE_RECORD',
objectNameSingular: generatedMockObjectMetadataItems[0].nameSingular,
},
});
});
it('returns settings for WHEN_RECORD_SELECTED with default icon when no custom icon provided', () => {
expect(
getManualTriggerDefaultSettingsDeprecated({
availability: 'WHEN_RECORD_SELECTED',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
}),
).toStrictEqual({
objectType: generatedMockObjectMetadataItems[0].nameSingular,
outputSchema: {},
icon: COMMAND_MENU_DEFAULT_ICON,
isPinned: false,
availability: {
type: 'SINGLE_RECORD',
objectNameSingular: generatedMockObjectMetadataItems[0].nameSingular,
},
});
});
it('throws error for unsupported availability type', () => {
const invalidAvailability =
'INVALID_AVAILABILITY' as WorkflowManualTriggerAvailability;
expect(() =>
getManualTriggerDefaultSettingsDeprecated({
availability: invalidAvailability,
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
}),
).toThrow("Didn't expect to get here.");
});
});
@@ -10,7 +10,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: [],
isIteratorEnabled: false,
});
}).toThrow();
});
@@ -21,7 +20,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
@@ -43,7 +41,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_UPDATED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
@@ -65,7 +62,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_DELETED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
@@ -81,48 +77,25 @@ describe('getTriggerDefaultDefinition', () => {
});
});
it('returns a valid configuration for DATABASE_EVENT trigger type creation', () => {
expect(
getTriggerDefaultDefinition({
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
name: 'Record is created',
settings: {
eventName: `${generatedMockObjectMetadataItems[0].nameSingular}.created`,
outputSchema: {},
},
position: {
x: 0,
y: 0,
},
});
});
it('returns a valid configuration for MANUAL trigger type', () => {
expect(
getTriggerDefaultDefinition({
defaultLabel: 'Launch manually',
type: 'MANUAL',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'MANUAL',
name: 'Launch manually',
settings: {
objectType: generatedMockObjectMetadataItems[0].nameSingular,
availability: {
objectNameSingular: generatedMockObjectMetadataItems[0].nameSingular,
type: 'SINGLE_RECORD',
type: 'GLOBAL',
locations: undefined,
},
outputSchema: {},
icon: COMMAND_MENU_DEFAULT_ICON,
isPinned: false,
objectType: undefined,
},
position: {
x: 0,
@@ -137,7 +110,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: 'On a schedule',
type: 'CRON',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'CRON',
@@ -160,7 +132,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: 'Webhook',
type: 'WEBHOOK',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
}),
).toStrictEqual({
type: 'WEBHOOK',
@@ -183,7 +154,6 @@ describe('getTriggerDefaultDefinition', () => {
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'unknown' as any,
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
isIteratorEnabled: false,
});
}).toThrow('Unknown type: unknown');
});
@@ -1,49 +0,0 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import {
type WorkflowManualTriggerAvailability,
type WorkflowManualTriggerSettings,
} from '@/workflow/types/Workflow';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
import { assertUnreachable } from 'twenty-shared/utils';
export const getManualTriggerDefaultSettingsDeprecated = ({
availability,
activeNonSystemObjectMetadataItems,
icon,
isPinned,
}: {
availability: WorkflowManualTriggerAvailability;
activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
icon?: string;
isPinned?: boolean;
}): WorkflowManualTriggerSettings => {
switch (availability) {
case 'EVERYWHERE': {
return {
objectType: undefined,
outputSchema: {},
icon: icon || COMMAND_MENU_DEFAULT_ICON,
isPinned: isPinned || false,
availability: {
type: 'GLOBAL',
locations: [],
},
};
}
case 'WHEN_RECORD_SELECTED': {
return {
objectType: activeNonSystemObjectMetadataItems[0].nameSingular,
outputSchema: {},
icon: icon || COMMAND_MENU_DEFAULT_ICON,
isPinned: isPinned || false,
availability: {
type: 'SINGLE_RECORD',
objectNameSingular:
activeNonSystemObjectMetadataItems[0].nameSingular,
},
};
}
}
return assertUnreachable(availability);
};
@@ -5,7 +5,6 @@ import {
} from '@/workflow/types/Workflow';
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings';
import { getManualTriggerDefaultSettingsDeprecated } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettingsDeprecated';
import { assertUnreachable } from 'twenty-shared/utils';
// TODO: This needs to be migrated to the server
@@ -13,12 +12,10 @@ export const getTriggerDefaultDefinition = ({
defaultLabel,
type,
activeNonSystemObjectMetadataItems,
isIteratorEnabled,
}: {
defaultLabel: string;
type: WorkflowTriggerType;
activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
isIteratorEnabled: boolean;
}): WorkflowTrigger => {
if (activeNonSystemObjectMetadataItems.length === 0) {
throw new Error(
@@ -47,21 +44,11 @@ export const getTriggerDefaultDefinition = ({
};
}
case 'MANUAL': {
if (isIteratorEnabled) {
return {
...baseTriggerDefinition,
type,
settings: getManualTriggerDefaultSettings({
availabilityType: 'GLOBAL',
activeNonSystemObjectMetadataItems,
}),
};
}
return {
...baseTriggerDefinition,
type,
settings: getManualTriggerDefaultSettingsDeprecated({
availability: 'WHEN_RECORD_SELECTED',
settings: getManualTriggerDefaultSettings({
availabilityType: 'GLOBAL',
activeNonSystemObjectMetadataItems,
}),
};