## Setup This PR can be tested only if some feature flags have specific values: - `IsWorkflowEnabled` equals `true` - `IsQueryRunnerTwentyORMEnabled` equals `false` These feature flags weren't committed to don't break other branches. ## What this PR brings - Display buttons to activate and deactivate a workflow version and a button to discard the current draft version. I also scaffolded a "Test" button, which doesn't do anything for now. - Wired the activate, deactivate and discard draft buttons to the backend. - Made it possible to "edit" active and deactivated versions by automatically creating a new draft version when the user tries to edit the version. - Hide the "Discard Draft", button if the current version is not a draft or is the first version ever created. - On the backend, don't consider discarded drafts when checking if a new draft version can be created. - On the backend, disallow deleting the first created workflow version. Otherwise, we will end up with a blank canvas in the front end, and it will be impossible to recover from it. - On the backend, disallow running deactivation steps if the workflow version is not currently active. Previously, we were throwing, which is unnecessary as it's a valid case. ## Spotted bugs that we must dive into ### Duplicate workflow versions in Apollo cache https://github.com/user-attachments/assets/7cfffd06-11e0-417a-8da0-f9a5f43b84e2 --------- Co-authored-by: Charles Bochet <charles@twenty.com>
291 lines
9.2 KiB
TypeScript
291 lines
9.2 KiB
TypeScript
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
|
import {
|
|
FieldMetadataType,
|
|
ObjectEdge,
|
|
ObjectMetadataItemsQuery,
|
|
} from '~/generated-metadata/graphql';
|
|
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/mock-metadata-query-result';
|
|
|
|
// TODO: replace with new mock
|
|
const customObjectMetadataItemEdge: ObjectEdge = {
|
|
__typename: 'objectEdge',
|
|
node: {
|
|
__typename: 'object',
|
|
id: 'efa1addc-a9cb-4789-b99e-a060fa84f982',
|
|
dataSourceId: 'd36e6a2d-28bc-459d-afd5-fe18e4405729',
|
|
nameSingular: 'myCustom',
|
|
namePlural: 'myCustoms',
|
|
labelSingular: 'My Custom',
|
|
labelPlural: 'My Customs',
|
|
description: 'A custom object example',
|
|
icon: 'IconLayoutCollage',
|
|
isCustom: true,
|
|
isRemote: false,
|
|
isActive: true,
|
|
isSystem: false,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
labelIdentifierFieldMetadataId: null,
|
|
imageIdentifierFieldMetadataId: null,
|
|
fields: {
|
|
__typename: 'ObjectFieldsConnection',
|
|
pageInfo: {
|
|
__typename: 'PageInfo',
|
|
hasNextPage: false,
|
|
hasPreviousPage: false,
|
|
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
|
|
endCursor: 'YXJyYXljb25uZWN0aW9uOjEz',
|
|
},
|
|
edges: [
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: 'ea83af89-be10-49af-a605-10c3392ae007',
|
|
type: 'RELATION',
|
|
name: 'companies',
|
|
label: 'Companies',
|
|
description: 'A custom Relation example',
|
|
icon: 'IconTag',
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: true,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: null,
|
|
relationDefinition: {
|
|
relationId: '1ec22b36-9e3c-4f24-8cf6-6c387ec3f243',
|
|
__typename: 'RelationDefinition',
|
|
direction: 'ONE_TO_MANY',
|
|
sourceObjectMetadata: {
|
|
__typename: 'object',
|
|
id: 'efa1addc-a9cb-4789-b99e-a060fa84f982',
|
|
nameSingular: 'myCustom',
|
|
namePlural: 'myCustoms',
|
|
},
|
|
sourceFieldMetadata: {
|
|
__typename: 'field',
|
|
id: 'ea83af89-be10-49af-a605-10c3392ae007',
|
|
name: 'companies',
|
|
},
|
|
targetObjectMetadata: {
|
|
__typename: 'object',
|
|
id: 'dba899da-7d88-41ac-b70e-5ea612ab4b2e',
|
|
nameSingular: 'company',
|
|
namePlural: 'companies',
|
|
},
|
|
targetFieldMetadata: {
|
|
__typename: 'field',
|
|
id: 'c9607ed7-168d-4743-a56a-689ffcfffe98',
|
|
name: 'myCustom',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: 'c5384d2a-9ec3-4e1b-b93f-86f53f122169',
|
|
type: 'UUID',
|
|
name: 'objectMetadataId',
|
|
label: 'Object Metadata Id',
|
|
description: 'View target object',
|
|
icon: null,
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: false,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: null,
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: 'bb4d96be-e4d9-47a9-812d-fcdfb063ebf3',
|
|
type: 'POSITION',
|
|
name: 'position',
|
|
label: 'Position',
|
|
description: 'View position',
|
|
icon: null,
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: true,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: null,
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: 'f20c68aa-3930-41c4-9f79-45dceda506df',
|
|
type: 'TEXT',
|
|
name: 'name',
|
|
label: 'Name',
|
|
description: 'Custom name',
|
|
icon: null,
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: false,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: "''",
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: 'a3ef848d-660a-4aef-9cd4-5baf25ce36ed',
|
|
type: 'DATE_TIME',
|
|
name: 'createdAt',
|
|
label: 'Creation date',
|
|
description: 'Creation date',
|
|
icon: 'IconCalendar',
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: false,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: 'now',
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: '92f3e27c-041d-45b2-b2bd-46db2b1aec3f',
|
|
type: 'DATE_TIME',
|
|
name: 'updatedAt',
|
|
label: 'Update date',
|
|
description: 'Update date',
|
|
icon: 'IconCalendar',
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: false,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: 'now',
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: '8d7987eb-99e8-4e54-a86c-86b3bd07d2be',
|
|
type: 'UUID',
|
|
name: 'id',
|
|
label: 'Id',
|
|
description: 'Id',
|
|
icon: 'Icon123',
|
|
isCustom: false,
|
|
isActive: true,
|
|
isSystem: true,
|
|
options: null,
|
|
isNullable: false,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: 'uuid',
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
{
|
|
__typename: 'fieldEdge',
|
|
node: {
|
|
__typename: 'field',
|
|
id: 'e07fcc3f-beec-4d91-8488-9d1d2cfa5f99',
|
|
type: FieldMetadataType.Select,
|
|
name: 'priority',
|
|
label: 'Priority',
|
|
description: 'A custom Select example',
|
|
icon: 'IconWarning',
|
|
isCustom: true,
|
|
isActive: true,
|
|
isSystem: false,
|
|
options: [
|
|
{
|
|
id: '2b98dc02-0d99-4f3e-890e-e2e6b8f3196c',
|
|
value: 'LOW',
|
|
label: 'Low',
|
|
color: 'turquoise',
|
|
},
|
|
{
|
|
id: 'd925a8de-d8ec-4b59-a079-64f4012e3311',
|
|
value: 'MEDIUM',
|
|
label: 'Medium',
|
|
color: 'yellow',
|
|
},
|
|
{
|
|
id: '6f6e1421-8a42-4d4a-bf76-465b5f84b6d2',
|
|
value: 'HIGH',
|
|
label: 'High',
|
|
color: 'red',
|
|
},
|
|
],
|
|
isNullable: true,
|
|
createdAt: '2024-04-08T12:48:49.538Z',
|
|
updatedAt: '2024-04-08T12:48:49.538Z',
|
|
defaultValue: null,
|
|
relationDefinition: null,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
} as ObjectEdge;
|
|
|
|
export const mockedObjectMetadataItemsQueryResult = {
|
|
...mockedStandardObjectMetadataQueryResult,
|
|
objects: {
|
|
...mockedStandardObjectMetadataQueryResult.objects,
|
|
edges: [
|
|
...mockedStandardObjectMetadataQueryResult.objects.edges,
|
|
customObjectMetadataItemEdge,
|
|
],
|
|
},
|
|
} as ObjectMetadataItemsQuery;
|
|
|
|
export const mockedObjectMetadataItems =
|
|
mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
|
pagedObjectMetadataItems: mockedObjectMetadataItemsQueryResult,
|
|
});
|
|
|
|
export const mockedCompanyObjectMetadataItem = mockedObjectMetadataItems?.find(
|
|
(object) => object.nameSingular === 'company',
|
|
) as ObjectMetadataItem;
|
|
|
|
export const mockedPersonObjectMetadataItem = mockedObjectMetadataItems?.find(
|
|
(object) => object.nameSingular === 'person',
|
|
) as ObjectMetadataItem;
|
|
|
|
export const mockedCustomObjectMetadataItem = mockedObjectMetadataItems?.find(
|
|
(object) => object.nameSingular === 'myCustom',
|
|
) as ObjectMetadataItem;
|
|
|
|
export const mockedOpportunityObjectMetadataItem =
|
|
mockedObjectMetadataItems?.find(
|
|
(object) => object.nameSingular === 'opportunity',
|
|
) as ObjectMetadataItem;
|