Activate/Deactivate workflow and Discard Draft (#7022)
## 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>
This commit is contained in:
committed by
GitHub
parent
75b493ba6c
commit
729c990546
@ -7,11 +7,10 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const cache = new InMemoryCache();
|
||||
|
||||
@ -141,7 +140,7 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||
result.current.setObjectMetadataItems(mockObjectMetadataItems);
|
||||
result.current.setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
});
|
||||
|
||||
const activityTargetObjectRecords =
|
||||
|
||||
@ -26,13 +26,13 @@ const mocks: MockedResponse[] = [
|
||||
mutation CreateOneTask($input: TaskCreateInput!) {
|
||||
createTask(data: $input) {
|
||||
__typename
|
||||
status
|
||||
assigneeId
|
||||
updatedAt
|
||||
body
|
||||
createdAt
|
||||
dueAt
|
||||
id
|
||||
status
|
||||
body
|
||||
assigneeId
|
||||
title
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,10 @@ import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import gql from 'graphql-tag';
|
||||
import pick from 'lodash.pick';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { mockedTasks } from '~/testing/mock-data/tasks';
|
||||
|
||||
const mockedDate = '2024-03-15T12:00:00.000Z';
|
||||
@ -69,7 +69,7 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
const mockObjectMetadataItems = generatedMockObjectMetadataItems;
|
||||
|
||||
describe('useOpenCreateActivityDrawer', () => {
|
||||
it('works as expected', async () => {
|
||||
|
||||
@ -28,20 +28,21 @@ const mocks: MockedResponse[] = [
|
||||
mutation UpdateOneTask($idToUpdate: ID!, $input: TaskUpdateInput!) {
|
||||
updateTask(id: $idToUpdate, data: $input) {
|
||||
__typename
|
||||
status
|
||||
assigneeId
|
||||
updatedAt
|
||||
body
|
||||
createdAt
|
||||
deletedAt
|
||||
dueAt
|
||||
position
|
||||
id
|
||||
title
|
||||
status
|
||||
body
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
assigneeId
|
||||
position
|
||||
title
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -53,7 +54,7 @@ const mocks: MockedResponse[] = [
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
updateTask: {
|
||||
__typename: 'Activity',
|
||||
__typename: 'Task',
|
||||
createdAt: '2024-03-15T07:33:14.212Z',
|
||||
reminderAt: null,
|
||||
authorId: '123',
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { gql } from '@apollo/client';
|
||||
import { AvatarType } from 'twenty-ui';
|
||||
|
||||
import { PERSON_FRAGMENT } from '@/object-record/hooks/__mocks__/personFragment';
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
export const mockId = '8f3b2121-f194-4ba4-9fbf-2d5a37126806';
|
||||
@ -48,36 +47,38 @@ export const initialFavorites = [
|
||||
},
|
||||
];
|
||||
|
||||
export const sortedFavorites = [
|
||||
{
|
||||
id: '1',
|
||||
recordId: '2',
|
||||
position: 0,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: undefined,
|
||||
labelIdentifier: 'ABC Corp',
|
||||
link: '/object/company/2',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
recordId: '4',
|
||||
position: 1,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: undefined,
|
||||
labelIdentifier: 'Company Test',
|
||||
link: '/object/company/4',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
position: 2,
|
||||
key: '8f3b2121-f194-4ba4-9fbf-2d5a37126806',
|
||||
labelIdentifier: 'favoriteLabel',
|
||||
avatarUrl: 'example.com',
|
||||
avatarType: 'squared',
|
||||
link: 'example.com',
|
||||
recordId: '1',
|
||||
},
|
||||
];
|
||||
export const sortedFavorites = [
|
||||
{
|
||||
"avatarType": "rounded",
|
||||
"avatarUrl": "",
|
||||
"id": "1",
|
||||
"labelIdentifier": " ",
|
||||
"link": "/object/person/1",
|
||||
"position": 0,
|
||||
"recordId": "1",
|
||||
"workspaceMemberId": undefined,
|
||||
},
|
||||
{
|
||||
"avatarType": "rounded",
|
||||
"avatarUrl": "",
|
||||
"id": "2",
|
||||
"labelIdentifier": " ",
|
||||
"link": "/object/person/3",
|
||||
"position": 1,
|
||||
"recordId": "3",
|
||||
"workspaceMemberId": undefined,
|
||||
},
|
||||
{
|
||||
"avatarType": "squared",
|
||||
"avatarUrl": "example.com",
|
||||
"id": "3",
|
||||
"key": "8f3b2121-f194-4ba4-9fbf-2d5a37126806",
|
||||
"labelIdentifier": "favoriteLabel",
|
||||
"link": "example.com",
|
||||
"position": 2,
|
||||
"recordId": "1",
|
||||
},
|
||||
]
|
||||
|
||||
export const mocks = [
|
||||
{
|
||||
@ -86,132 +87,155 @@ export const mocks = [
|
||||
mutation CreateOneFavorite($input: FavoriteCreateInput!) {
|
||||
createFavorite(data: $input) {
|
||||
__typename
|
||||
noteId
|
||||
taskId
|
||||
myCustomObjectId
|
||||
workspaceMemberId
|
||||
workspaceMember {
|
||||
person {
|
||||
__typename
|
||||
userId
|
||||
updatedAt
|
||||
dateFormat
|
||||
id
|
||||
locale
|
||||
avatarUrl
|
||||
timeZone
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
userEmail
|
||||
createdAt
|
||||
timeFormat
|
||||
colorScheme
|
||||
}
|
||||
companyId
|
||||
myCustomObject {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
position
|
||||
updatedAt
|
||||
name
|
||||
myCustomField
|
||||
id
|
||||
createdAt
|
||||
}
|
||||
updatedAt
|
||||
id
|
||||
opportunity {
|
||||
__typename
|
||||
companyId
|
||||
closeDate
|
||||
stage
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
id
|
||||
updatedAt
|
||||
name
|
||||
createdAt
|
||||
pointOfContactId
|
||||
amount {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
position
|
||||
}
|
||||
noteId
|
||||
note {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
position
|
||||
body
|
||||
updatedAt
|
||||
createdAt
|
||||
title
|
||||
id
|
||||
}
|
||||
personId
|
||||
task {
|
||||
__typename
|
||||
status
|
||||
assigneeId
|
||||
updatedAt
|
||||
body
|
||||
createdAt
|
||||
dueAt
|
||||
position
|
||||
id
|
||||
title
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
}
|
||||
opportunityId
|
||||
position
|
||||
createdAt
|
||||
company {
|
||||
__typename
|
||||
id
|
||||
visaSponsorship
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
position
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
employees
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
workPolicy
|
||||
deletedAt
|
||||
createdAt
|
||||
updatedAt
|
||||
jobTitle
|
||||
intro
|
||||
workPrefereance
|
||||
performanceRating
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
city
|
||||
companyId
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
id
|
||||
position
|
||||
emails {
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
avatarUrl
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
}
|
||||
task {
|
||||
__typename
|
||||
updatedAt
|
||||
createdAt
|
||||
deletedAt
|
||||
dueAt
|
||||
id
|
||||
status
|
||||
body
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
assigneeId
|
||||
position
|
||||
title
|
||||
}
|
||||
rocketId
|
||||
viewId
|
||||
updatedAt
|
||||
workflowId
|
||||
personId
|
||||
workspaceMemberId
|
||||
note {
|
||||
__typename
|
||||
deletedAt
|
||||
id
|
||||
position
|
||||
updatedAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
body
|
||||
title
|
||||
createdAt
|
||||
}
|
||||
createdAt
|
||||
view {
|
||||
__typename
|
||||
id
|
||||
type
|
||||
icon
|
||||
key
|
||||
isCompact
|
||||
kanbanFieldMetadataId
|
||||
objectMetadataId
|
||||
position
|
||||
createdAt
|
||||
deletedAt
|
||||
updatedAt
|
||||
name
|
||||
}
|
||||
opportunityId
|
||||
position
|
||||
deletedAt
|
||||
id
|
||||
companyId
|
||||
workflow {
|
||||
__typename
|
||||
deletedAt
|
||||
lastPublishedVersionId
|
||||
createdAt
|
||||
id
|
||||
statuses
|
||||
name
|
||||
position
|
||||
updatedAt
|
||||
}
|
||||
workspaceMember {
|
||||
__typename
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
avatarUrl
|
||||
userId
|
||||
createdAt
|
||||
timeZone
|
||||
id
|
||||
timeFormat
|
||||
updatedAt
|
||||
locale
|
||||
userEmail
|
||||
deletedAt
|
||||
colorScheme
|
||||
dateFormat
|
||||
}
|
||||
company {
|
||||
__typename
|
||||
updatedAt
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
visaSponsorship
|
||||
address {
|
||||
addressStreet1
|
||||
addressStreet2
|
||||
@ -222,21 +246,76 @@ export const mocks = [
|
||||
addressLat
|
||||
addressLng
|
||||
}
|
||||
position
|
||||
employees
|
||||
deletedAt
|
||||
accountOwnerId
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
myCustomField
|
||||
createdAt
|
||||
accountOwnerId
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
workPolicy
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
tagline
|
||||
idealCustomerProfile
|
||||
}
|
||||
person {
|
||||
${PERSON_FRAGMENT}
|
||||
rocket {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
updatedAt
|
||||
name
|
||||
position
|
||||
createdAt
|
||||
id
|
||||
deletedAt
|
||||
}
|
||||
opportunity {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
amount {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
stage
|
||||
position
|
||||
closeDate
|
||||
id
|
||||
name
|
||||
pointOfContactId
|
||||
companyId
|
||||
updatedAt
|
||||
deletedAt
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -286,132 +365,155 @@ export const mocks = [
|
||||
) {
|
||||
updateFavorite(id: $idToUpdate, data: $input) {
|
||||
__typename
|
||||
noteId
|
||||
taskId
|
||||
myCustomObjectId
|
||||
workspaceMemberId
|
||||
workspaceMember {
|
||||
person {
|
||||
__typename
|
||||
userId
|
||||
updatedAt
|
||||
dateFormat
|
||||
id
|
||||
locale
|
||||
avatarUrl
|
||||
timeZone
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
userEmail
|
||||
createdAt
|
||||
timeFormat
|
||||
colorScheme
|
||||
}
|
||||
companyId
|
||||
myCustomObject {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
position
|
||||
updatedAt
|
||||
name
|
||||
myCustomField
|
||||
id
|
||||
createdAt
|
||||
}
|
||||
updatedAt
|
||||
id
|
||||
opportunity {
|
||||
__typename
|
||||
companyId
|
||||
closeDate
|
||||
stage
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
id
|
||||
updatedAt
|
||||
name
|
||||
createdAt
|
||||
pointOfContactId
|
||||
amount {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
position
|
||||
}
|
||||
noteId
|
||||
note {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
position
|
||||
body
|
||||
updatedAt
|
||||
createdAt
|
||||
title
|
||||
id
|
||||
}
|
||||
personId
|
||||
task {
|
||||
__typename
|
||||
status
|
||||
assigneeId
|
||||
updatedAt
|
||||
body
|
||||
createdAt
|
||||
dueAt
|
||||
position
|
||||
id
|
||||
title
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
}
|
||||
opportunityId
|
||||
position
|
||||
createdAt
|
||||
company {
|
||||
__typename
|
||||
id
|
||||
visaSponsorship
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
position
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
employees
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
workPolicy
|
||||
deletedAt
|
||||
createdAt
|
||||
updatedAt
|
||||
jobTitle
|
||||
intro
|
||||
workPrefereance
|
||||
performanceRating
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
city
|
||||
companyId
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
id
|
||||
position
|
||||
emails {
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
avatarUrl
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
}
|
||||
task {
|
||||
__typename
|
||||
updatedAt
|
||||
createdAt
|
||||
deletedAt
|
||||
dueAt
|
||||
id
|
||||
status
|
||||
body
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
assigneeId
|
||||
position
|
||||
title
|
||||
}
|
||||
rocketId
|
||||
viewId
|
||||
updatedAt
|
||||
workflowId
|
||||
personId
|
||||
workspaceMemberId
|
||||
note {
|
||||
__typename
|
||||
deletedAt
|
||||
id
|
||||
position
|
||||
updatedAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
body
|
||||
title
|
||||
createdAt
|
||||
}
|
||||
createdAt
|
||||
view {
|
||||
__typename
|
||||
id
|
||||
type
|
||||
icon
|
||||
key
|
||||
isCompact
|
||||
kanbanFieldMetadataId
|
||||
objectMetadataId
|
||||
position
|
||||
createdAt
|
||||
deletedAt
|
||||
updatedAt
|
||||
name
|
||||
}
|
||||
opportunityId
|
||||
position
|
||||
deletedAt
|
||||
id
|
||||
companyId
|
||||
workflow {
|
||||
__typename
|
||||
deletedAt
|
||||
lastPublishedVersionId
|
||||
createdAt
|
||||
id
|
||||
statuses
|
||||
name
|
||||
position
|
||||
updatedAt
|
||||
}
|
||||
workspaceMember {
|
||||
__typename
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
avatarUrl
|
||||
userId
|
||||
createdAt
|
||||
timeZone
|
||||
id
|
||||
timeFormat
|
||||
updatedAt
|
||||
locale
|
||||
userEmail
|
||||
deletedAt
|
||||
colorScheme
|
||||
dateFormat
|
||||
}
|
||||
company {
|
||||
__typename
|
||||
updatedAt
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
visaSponsorship
|
||||
address {
|
||||
addressStreet1
|
||||
addressStreet2
|
||||
@ -422,21 +524,76 @@ export const mocks = [
|
||||
addressLat
|
||||
addressLng
|
||||
}
|
||||
position
|
||||
employees
|
||||
deletedAt
|
||||
accountOwnerId
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
myCustomField
|
||||
createdAt
|
||||
accountOwnerId
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
workPolicy
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
tagline
|
||||
idealCustomerProfile
|
||||
}
|
||||
person {
|
||||
${PERSON_FRAGMENT}
|
||||
rocket {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
updatedAt
|
||||
name
|
||||
position
|
||||
createdAt
|
||||
id
|
||||
deletedAt
|
||||
}
|
||||
opportunity {
|
||||
__typename
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
amount {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
stage
|
||||
position
|
||||
closeDate
|
||||
id
|
||||
name
|
||||
pointOfContactId
|
||||
companyId
|
||||
updatedAt
|
||||
deletedAt
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,9 +8,9 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import {
|
||||
favoriteId,
|
||||
favoriteTargetObjectRecord,
|
||||
@ -29,8 +29,6 @@ jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
|
||||
useFindManyRecords: () => ({ records: initialFavorites }),
|
||||
}));
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||
@ -51,7 +49,7 @@ describe('useFavorites', () => {
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
@ -72,7 +70,7 @@ describe('useFavorites', () => {
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
@ -100,7 +98,7 @@ describe('useFavorites', () => {
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
@ -125,7 +123,7 @@ describe('useFavorites', () => {
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
|
||||
@ -4,16 +4,17 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import {
|
||||
COMPANY_OBJECT_METADATA_ID,
|
||||
getObjectMetadataItemsMock,
|
||||
} from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { mockedUserData } from '~/testing/mock-data/users';
|
||||
|
||||
jest.mock('@/prefetch/hooks/usePrefetchedData');
|
||||
const setupMockPrefetchedData = (viewId?: string) => {
|
||||
const companyObjectMetadata = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
);
|
||||
|
||||
jest.mocked(usePrefetchedData).mockReturnValue({
|
||||
isDataPrefetched: true,
|
||||
records: viewId
|
||||
@ -21,7 +22,7 @@ const setupMockPrefetchedData = (viewId?: string) => {
|
||||
{
|
||||
id: viewId,
|
||||
__typename: 'object',
|
||||
objectMetadataId: COMPANY_OBJECT_METADATA_ID,
|
||||
objectMetadataId: companyObjectMetadata?.id,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
@ -36,7 +37,7 @@ const renderHooks = (withCurrentUser: boolean) => {
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
|
||||
setObjectMetadataItems(getObjectMetadataItemsMock());
|
||||
setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
if (withCurrentUser) {
|
||||
setCurrentUser(mockedUserData);
|
||||
|
||||
@ -6,8 +6,8 @@ import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { WorkspaceActivationStatus } from '~/generated/graphql';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
@ -29,7 +29,7 @@ export const ObjectMetadataItemsLoadEffect = () => {
|
||||
const toSetObjectMetadataItems =
|
||||
isUndefinedOrNull(currentUser) ||
|
||||
currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active
|
||||
? getObjectMetadataItemsMock()
|
||||
? generatedMockObjectMetadataItems
|
||||
: newObjectMetadataItems;
|
||||
if (
|
||||
!loading &&
|
||||
|
||||
@ -3,7 +3,7 @@ import { Nullable } from 'twenty-ui';
|
||||
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
describe('useColumnDefinitionsFromFieldMetadata', () => {
|
||||
it('should return empty definitions if no object is passed', () => {
|
||||
@ -22,22 +22,24 @@ describe('useColumnDefinitionsFromFieldMetadata', () => {
|
||||
});
|
||||
|
||||
it('should return expected definitions', () => {
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
const companyObjectMetadata = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
(objectMetadataItem?: Nullable<ObjectMetadataItem>) => {
|
||||
return useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
},
|
||||
{
|
||||
initialProps: mockObjectMetadataItems[1],
|
||||
initialProps: companyObjectMetadata,
|
||||
},
|
||||
);
|
||||
|
||||
const { columnDefinitions, filterDefinitions, sortDefinitions } =
|
||||
result.current;
|
||||
|
||||
expect(columnDefinitions.length).toBe(5);
|
||||
expect(filterDefinitions.length).toBe(4);
|
||||
expect(sortDefinitions.length).toBe(4);
|
||||
expect(columnDefinitions.length).toBe(21);
|
||||
expect(filterDefinitions.length).toBe(14);
|
||||
expect(sortDefinitions.length).toBe(14);
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
} from '@/object-metadata/hooks/__mocks__/useFilteredObjectMetadataItems';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
@ -34,14 +34,12 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
describe('useFilteredObjectMetadataItems', () => {
|
||||
it('should findActiveObjectMetadataItemBySlug', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFilteredObjectMetadataItems();
|
||||
},
|
||||
@ -61,7 +59,7 @@ describe('useFilteredObjectMetadataItems', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFilteredObjectMetadataItems();
|
||||
},
|
||||
@ -78,10 +76,14 @@ describe('useFilteredObjectMetadataItems', () => {
|
||||
});
|
||||
|
||||
it('should findObjectMetadataItemById', async () => {
|
||||
const peopleObjectMetadata = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.namePlural === 'people',
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFilteredObjectMetadataItems();
|
||||
},
|
||||
@ -92,7 +94,7 @@ describe('useFilteredObjectMetadataItems', () => {
|
||||
|
||||
act(() => {
|
||||
const res = result.current.findObjectMetadataItemById(
|
||||
'ff2881da-89f6-4f15-8f0a-e3f355ea3b94',
|
||||
peopleObjectMetadata?.id,
|
||||
);
|
||||
expect(res).toBeDefined();
|
||||
expect(res?.namePlural).toBe('people');
|
||||
@ -103,7 +105,7 @@ describe('useFilteredObjectMetadataItems', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFilteredObjectMetadataItems();
|
||||
},
|
||||
|
||||
@ -3,9 +3,7 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
describe('useGetObjectRecordIdentifierByNameSingular', () => {
|
||||
it('should work as expected', async () => {
|
||||
@ -19,7 +17,7 @@ describe('useGetObjectRecordIdentifierByNameSingular', () => {
|
||||
}) => {
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useGetObjectRecordIdentifierByNameSingular()(
|
||||
record,
|
||||
|
||||
@ -5,7 +5,7 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
@ -15,8 +15,7 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
|
||||
describe('useGetRelationMetadata', () => {
|
||||
it('should return correct properties', async () => {
|
||||
const objectMetadataItems = getObjectMetadataItemsMock();
|
||||
const objectMetadata = objectMetadataItems.find(
|
||||
const objectMetadata = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
const fieldMetadataItem = objectMetadata.fields.find(
|
||||
@ -28,7 +27,7 @@ describe('useGetRelationMetadata', () => {
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
|
||||
useEffect(() => {
|
||||
setMetadataItems(objectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
}, [setMetadataItems]);
|
||||
|
||||
return useGetRelationMetadata();
|
||||
@ -45,9 +44,10 @@ describe('useGetRelationMetadata', () => {
|
||||
relationType,
|
||||
} = result.current({ fieldMetadataItem }) ?? {};
|
||||
|
||||
const expectedRelationObjectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'opportunity',
|
||||
);
|
||||
const expectedRelationObjectMetadataItem =
|
||||
generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'opportunity',
|
||||
);
|
||||
const expectedRelationFieldMetadataItem =
|
||||
expectedRelationObjectMetadataItem?.fields.find(
|
||||
(field) => field.name === 'pointOfContact',
|
||||
|
||||
@ -4,6 +4,7 @@ import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
@ -13,6 +14,9 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
|
||||
// Split into tests for each new hook
|
||||
describe('useObjectMetadataItem', () => {
|
||||
const opportunityObjectMetadata = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'opportunity',
|
||||
);
|
||||
it('should return correct properties', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useObjectMetadataItem({ objectNameSingular: 'opportunity' }),
|
||||
@ -23,6 +27,6 @@ describe('useObjectMetadataItem', () => {
|
||||
|
||||
const { objectMetadataItem } = result.current;
|
||||
|
||||
expect(objectMetadataItem.id).toBe('b95b3f38-9fc2-4d7e-a823-7791cf13d089');
|
||||
expect(objectMetadataItem.id).toBe(opportunityObjectMetadata?.id);
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,10 +4,10 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { WorkspaceActivationStatus } from '~/generated/graphql';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
|
||||
|
||||
export const useObjectMetadataItem = ({
|
||||
@ -15,9 +15,6 @@ export const useObjectMetadataItem = ({
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
// Todo: deprecate this logic as mocked objectMetadataItems are load in ObjectMetadataItemsLoadEffect anyway
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
objectMetadataItemFamilySelector({
|
||||
objectName: objectNameSingular,
|
||||
@ -29,11 +26,11 @@ export const useObjectMetadataItem = ({
|
||||
|
||||
if (currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
generatedMockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.nameSingular === objectNameSingular,
|
||||
) ?? null;
|
||||
objectMetadataItems = mockObjectMetadataItems;
|
||||
objectMetadataItems = generatedMockObjectMetadataItems;
|
||||
}
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
|
||||
@ -2,8 +2,8 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { WorkspaceActivationStatus } from '~/generated/graphql';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useObjectNamePluralFromSingular = ({
|
||||
@ -12,7 +12,6 @@ export const useObjectNamePluralFromSingular = ({
|
||||
objectNameSingular: string;
|
||||
}) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
objectMetadataItemFamilySelector({
|
||||
@ -23,7 +22,7 @@ export const useObjectNamePluralFromSingular = ({
|
||||
|
||||
if (currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
generatedMockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.nameSingular === objectNameSingular,
|
||||
) ?? null;
|
||||
|
||||
@ -2,8 +2,8 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { WorkspaceActivationStatus } from '~/generated/graphql';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useObjectNameSingularFromPlural = ({
|
||||
@ -13,8 +13,6 @@ export const useObjectNameSingularFromPlural = ({
|
||||
}) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
objectMetadataItemFamilySelector({
|
||||
objectName: objectNamePlural,
|
||||
@ -24,7 +22,7 @@ export const useObjectNameSingularFromPlural = ({
|
||||
|
||||
if (currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
generatedMockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.namePlural === objectNamePlural,
|
||||
) ?? null;
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { getObjectMetadataItemByNameSingular } from '@/object-metadata/utils/getObjectMetadataItemBySingularName';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
describe('getObjectMetadataItemBySingularName', () => {
|
||||
it('should work as expected', () => {
|
||||
const firstObjectMetadataItem = mockObjectMetadataItems[0];
|
||||
const firstObjectMetadataItem = generatedMockObjectMetadataItems[0];
|
||||
|
||||
const foundObjectMetadataItem = getObjectMetadataItemByNameSingular({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
objectNameSingular: firstObjectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { getOrderByFieldForObjectMetadataItem } from '@/object-metadata/utils/getObjectOrderByField';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
describe('getObjectOrderByField', () => {
|
||||
it('should work as expected', () => {
|
||||
const objectMetadataItem = mockObjectMetadataItems.find(
|
||||
const objectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
const res = getOrderByFieldForObjectMetadataItem(objectMetadataItem);
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
describe('getObjectSlug', () => {
|
||||
it('should work as expected', () => {
|
||||
const objectMetadataItem = mockObjectMetadataItems.find(
|
||||
const objectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
describe('isObjectMetadataAvailableForRelation', () => {
|
||||
it('should work as expected', () => {
|
||||
const objectMetadataItem = mockObjectMetadataItems.find(
|
||||
const objectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { normalizeGQLField } from '~/utils/normalizeGQLField';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const personObjectMetadataItem = mockObjectMetadataItems.find(
|
||||
const personObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
);
|
||||
|
||||
@ -15,7 +13,7 @@ if (!personObjectMetadataItem) {
|
||||
describe('mapFieldMetadataToGraphQLQuery', () => {
|
||||
it('should return fieldName if simpleValue', async () => {
|
||||
const res = mapFieldMetadataToGraphQLQuery({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
field: personObjectMetadataItem.fields.find(
|
||||
(field) => field.name === 'id',
|
||||
)!,
|
||||
@ -24,7 +22,7 @@ describe('mapFieldMetadataToGraphQLQuery', () => {
|
||||
});
|
||||
it('should return fieldName if composite', async () => {
|
||||
const res = mapFieldMetadataToGraphQLQuery({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
field: personObjectMetadataItem.fields.find(
|
||||
(field) => field.name === 'name',
|
||||
)!,
|
||||
@ -40,7 +38,7 @@ describe('mapFieldMetadataToGraphQLQuery', () => {
|
||||
|
||||
it('should return non relation subFields if relation', async () => {
|
||||
const res = mapFieldMetadataToGraphQLQuery({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
field: personObjectMetadataItem.fields.find(
|
||||
(field) => field.name === 'company',
|
||||
)!,
|
||||
@ -96,7 +94,7 @@ idealCustomerProfile
|
||||
|
||||
it('should return only return relation subFields that are in recordGqlFields', async () => {
|
||||
const res = mapFieldMetadataToGraphQLQuery({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
relationrecordFields: {
|
||||
accountOwner: { id: true, name: true },
|
||||
people: true,
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { normalizeGQLQuery } from '~/utils/normalizeGQLQuery';
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const personObjectMetadataItem = mockObjectMetadataItems.find(
|
||||
const personObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
);
|
||||
|
||||
@ -15,7 +13,7 @@ if (!personObjectMetadataItem) {
|
||||
describe('mapObjectMetadataToGraphQLQuery', () => {
|
||||
it('should query only specified recordGqlFields', async () => {
|
||||
const res = mapObjectMetadataToGraphQLQuery({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
objectMetadataItem: personObjectMetadataItem,
|
||||
recordGqlFields: {
|
||||
company: true,
|
||||
@ -122,7 +120,7 @@ describe('mapObjectMetadataToGraphQLQuery', () => {
|
||||
|
||||
it('should load only specified operation fields nested', async () => {
|
||||
const res = mapObjectMetadataToGraphQLQuery({
|
||||
objectMetadataItems: mockObjectMetadataItems,
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
objectMetadataItem: personObjectMetadataItem,
|
||||
recordGqlFields: { company: { id: true }, id: true, name: true },
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,45 +1,48 @@
|
||||
export const PERSON_FRAGMENT = `
|
||||
__typename
|
||||
updatedAt
|
||||
myCustomObjectId
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
email
|
||||
position
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
avatarUrl
|
||||
deletedAt
|
||||
createdAt
|
||||
updatedAt
|
||||
jobTitle
|
||||
intro
|
||||
workPrefereance
|
||||
performanceRating
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
performanceRating
|
||||
createdAt
|
||||
phone {
|
||||
city
|
||||
companyId
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
id
|
||||
city
|
||||
companyId
|
||||
intro
|
||||
workPrefereance
|
||||
position
|
||||
emails {
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
avatarUrl
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
`
|
||||
|
||||
@ -18,7 +18,6 @@ const basePerson = {
|
||||
},
|
||||
createdAt: '',
|
||||
city: '',
|
||||
email: '',
|
||||
jobTitle: '',
|
||||
name: {
|
||||
firstName: '',
|
||||
|
||||
@ -4,7 +4,6 @@ import { ReactNode, useEffect } from 'react';
|
||||
import { RecoilRoot, useRecoilState } from 'recoil';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import {
|
||||
mockPageSize,
|
||||
peopleMockWithIdsOnly,
|
||||
@ -18,6 +17,7 @@ import {
|
||||
} from '@/object-record/hooks/__mocks__/useFetchAllRecordIds';
|
||||
import { useFetchAllRecordIds } from '@/object-record/hooks/useFetchAllRecordIds';
|
||||
import { SnackBarManagerScopeInternalContext } from '@/ui/feedback/snack-bar-manager/scopes/scope-internal-context/SnackBarManagerScopeInternalContext';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
@ -75,7 +75,7 @@ describe('useFetchAllRecordIds', () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setObjectMetadataItems(getObjectMetadataItemsMock());
|
||||
setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
}, [setObjectMetadataItems]);
|
||||
|
||||
return useFetchAllRecordIds({
|
||||
|
||||
@ -5,7 +5,6 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import {
|
||||
query,
|
||||
responseData,
|
||||
@ -13,6 +12,7 @@ import {
|
||||
} from '@/object-record/hooks/__mocks__/useFindManyRecords';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
@ -65,11 +65,9 @@ describe('useFindManyRecords', () => {
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFindManyRecords({
|
||||
objectNameSingular: 'person',
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
@ -18,7 +18,7 @@ describe('useGenerateFindManyRecordsForMultipleMetadataItemsQuery', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
return useGenerateCombinedFindManyRecordsQuery({
|
||||
operationSignatures: getObjectMetadataItemsMock()
|
||||
operationSignatures: generatedMockObjectMetadataItems
|
||||
.slice(0, 2)
|
||||
.map((item) => ({
|
||||
objectNameSingular: item.nameSingular,
|
||||
|
||||
@ -72,11 +72,11 @@ export const linkFieldDefinition: FieldDefinition<FieldLinkMetadata> = {
|
||||
},
|
||||
};
|
||||
|
||||
const phoneFieldMetadataItem = mockedPersonObjectMetadataItem.fields?.find(
|
||||
({ name }) => name === 'phone',
|
||||
const phonesFieldMetadataItem = mockedPersonObjectMetadataItem.fields?.find(
|
||||
({ name }) => name === 'phones',
|
||||
);
|
||||
export const phoneFieldDefinition = formatFieldMetadataItemAsFieldDefinition({
|
||||
field: phoneFieldMetadataItem!,
|
||||
export const phonesFieldDefinition = formatFieldMetadataItemAsFieldDefinition({
|
||||
field: phonesFieldMetadataItem!,
|
||||
objectMetadataItem: mockedPersonObjectMetadataItem,
|
||||
});
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { IconPencil } from 'twenty-ui';
|
||||
|
||||
import {
|
||||
phoneFieldDefinition,
|
||||
phonesFieldDefinition,
|
||||
relationFieldDefinition,
|
||||
} from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
@ -29,7 +29,7 @@ const getWrapper =
|
||||
</FieldContext.Provider>
|
||||
);
|
||||
|
||||
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||
const PhoneWrapper = getWrapper(phonesFieldDefinition);
|
||||
const RelationWrapper = getWrapper(relationFieldDefinition);
|
||||
|
||||
describe('useGetButtonIcon', () => {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { phoneFieldDefinition } from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import { phonesFieldDefinition } from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEmpty';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
@ -12,7 +12,7 @@ const recordId = 'recordId';
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
fieldDefinition: phoneFieldDefinition,
|
||||
fieldDefinition: phonesFieldDefinition,
|
||||
recordId,
|
||||
hotkeyScope: 'hotkeyScope',
|
||||
isLabelIdentifier: false,
|
||||
|
||||
@ -3,7 +3,7 @@ import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import {
|
||||
phoneFieldDefinition,
|
||||
phonesFieldDefinition,
|
||||
ratingFieldDefinition,
|
||||
} from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
@ -29,7 +29,7 @@ const getWrapper =
|
||||
);
|
||||
|
||||
const RatingWrapper = getWrapper(ratingFieldDefinition);
|
||||
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||
const PhoneWrapper = getWrapper(phonesFieldDefinition);
|
||||
|
||||
describe('useIsFieldInputOnly', () => {
|
||||
it('should return true', () => {
|
||||
|
||||
@ -4,7 +4,7 @@ import { RecoilRoot } from 'recoil';
|
||||
|
||||
import {
|
||||
actorFieldDefinition,
|
||||
phoneFieldDefinition,
|
||||
phonesFieldDefinition,
|
||||
} from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFieldReadOnly';
|
||||
@ -29,7 +29,7 @@ const getWrapper =
|
||||
);
|
||||
|
||||
const ActorWrapper = getWrapper(actorFieldDefinition);
|
||||
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||
const PhoneWrapper = getWrapper(phonesFieldDefinition);
|
||||
|
||||
describe('useIsFieldReadOnly', () => {
|
||||
it('should return true', () => {
|
||||
|
||||
@ -8,7 +8,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { PERSON_FRAGMENT } from '@/object-record/hooks/__mocks__/personFragment';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import {
|
||||
phoneFieldDefinition,
|
||||
phonesFieldDefinition,
|
||||
relationFieldDefinition,
|
||||
} from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import {
|
||||
@ -33,7 +33,16 @@ const mocks: MockedResponse[] = [
|
||||
{
|
||||
request: {
|
||||
query,
|
||||
variables: { idToUpdate: 'recordId', input: { phone: '+1 123 456' } },
|
||||
variables: {
|
||||
idToUpdate: 'recordId',
|
||||
input: {
|
||||
phones: {
|
||||
primaryPhoneNumber: '123 456',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
additionalPhones: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
@ -98,7 +107,7 @@ const getWrapper =
|
||||
);
|
||||
};
|
||||
|
||||
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||
const PhoneWrapper = getWrapper(phonesFieldDefinition);
|
||||
const RelationWrapper = getWrapper(relationFieldDefinition);
|
||||
|
||||
describe('usePersistField', () => {
|
||||
@ -118,7 +127,11 @@ describe('usePersistField', () => {
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.persistField('+1 123 456');
|
||||
result.current.persistField({
|
||||
primaryPhoneNumber: '123 456',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
additionalPhones: [],
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@ -26,35 +26,13 @@ const mocks: MockedResponse[] = [
|
||||
) {
|
||||
updateCompany(id: $idToUpdate, data: $input) {
|
||||
__typename
|
||||
id
|
||||
visaSponsorship
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
updatedAt
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
position
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
employees
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
workPolicy
|
||||
visaSponsorship
|
||||
address {
|
||||
addressStreet1
|
||||
addressStreet2
|
||||
@ -65,16 +43,38 @@ const mocks: MockedResponse[] = [
|
||||
addressLat
|
||||
addressLng
|
||||
}
|
||||
position
|
||||
employees
|
||||
deletedAt
|
||||
accountOwnerId
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
myCustomField
|
||||
createdAt
|
||||
accountOwnerId
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
workPolicy
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
tagline
|
||||
idealCustomerProfile
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||
import gql from 'graphql-tag';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const defaultResponseData = {
|
||||
pageInfo: {
|
||||
@ -65,7 +66,7 @@ const mockPerson = {
|
||||
city: 'city',
|
||||
companyId: '1',
|
||||
intro: 'intro',
|
||||
workPrefereance: 'workPrefereance',
|
||||
workPreference: 'workPrefereance',
|
||||
};
|
||||
const mocks: MockedResponse[] = [
|
||||
{
|
||||
@ -86,48 +87,51 @@ const mocks: MockedResponse[] = [
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
updatedAt
|
||||
myCustomObjectId
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
email
|
||||
position
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
avatarUrl
|
||||
deletedAt
|
||||
createdAt
|
||||
updatedAt
|
||||
jobTitle
|
||||
intro
|
||||
workPrefereance
|
||||
performanceRating
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
performanceRating
|
||||
createdAt
|
||||
phone {
|
||||
city
|
||||
companyId
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
id
|
||||
city
|
||||
companyId
|
||||
intro
|
||||
workPrefereance
|
||||
position
|
||||
emails {
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
avatarUrl
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
}
|
||||
cursor
|
||||
}
|
||||
@ -292,9 +296,17 @@ describe('useTableData', () => {
|
||||
},
|
||||
);
|
||||
|
||||
const personObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
);
|
||||
|
||||
const updatedAtFieldMetadataItem = personObjectMetadataItem?.fields.find(
|
||||
(field) => field.name === 'updatedAt',
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
result.current.setKanbanFieldName.setKanbanFieldMetadataName(
|
||||
result.current.kanbanData.hiddenBoardFields[0].metadata.fieldName,
|
||||
updatedAtFieldMetadataItem?.name,
|
||||
);
|
||||
});
|
||||
|
||||
@ -309,7 +321,7 @@ describe('useTableData', () => {
|
||||
{
|
||||
defaultValue: 'now',
|
||||
editButtonIcon: undefined,
|
||||
fieldMetadataId: '102963b7-3e77-4293-a1e6-1ab59a02b663',
|
||||
fieldMetadataId: updatedAtFieldMetadataItem?.id,
|
||||
iconName: 'IconCalendarClock',
|
||||
isFilterable: true,
|
||||
isLabelIdentifier: false,
|
||||
@ -329,7 +341,7 @@ describe('useTableData', () => {
|
||||
relationType: undefined,
|
||||
targetFieldMetadataName: '',
|
||||
},
|
||||
position: 0,
|
||||
position: 7,
|
||||
showLabel: undefined,
|
||||
size: 100,
|
||||
type: 'DATE_TIME',
|
||||
|
||||
@ -5,7 +5,7 @@ import { ComponentDecorator } from 'twenty-ui';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import {
|
||||
RecordFieldValueSelectorContextProvider,
|
||||
@ -21,10 +21,9 @@ import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorato
|
||||
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
|
||||
|
||||
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import { mockPerformance } from './mock';
|
||||
|
||||
const objectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const RelationFieldValueSetterEffect = () => {
|
||||
const setEntity = useSetRecoilState(
|
||||
recordStoreFamilyState(mockPerformance.recordId),
|
||||
@ -48,7 +47,7 @@ const RelationFieldValueSetterEffect = () => {
|
||||
mockPerformance.relationFieldValue,
|
||||
);
|
||||
|
||||
setObjectMetadataItems(objectMetadataItems);
|
||||
setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
}, [setEntity, setRelationEntity, setRecordValue, setObjectMetadataItems]);
|
||||
|
||||
return null;
|
||||
|
||||
@ -5,12 +5,12 @@ import { createState } from 'twenty-ui';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { textfieldDefinition } from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useUpsertRecord } from '@/object-record/record-table/record-table-cell/hooks/useUpsertRecord';
|
||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const draftValue = 'updated Name';
|
||||
|
||||
@ -55,8 +55,6 @@ const updateOneRecordMock = jest.fn();
|
||||
createOneRecord: createOneRecordMock,
|
||||
});
|
||||
|
||||
const objectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const Wrapper = ({
|
||||
children,
|
||||
pendingRecordIdMockedValue,
|
||||
@ -68,7 +66,7 @@ const Wrapper = ({
|
||||
}) => (
|
||||
<RecoilRoot
|
||||
initializeState={(snapshot) => {
|
||||
snapshot.set(objectMetadataItemsState, objectMetadataItems);
|
||||
snapshot.set(objectMetadataItemsState, generatedMockObjectMetadataItems);
|
||||
snapshot.set(pendingRecordIdState, pendingRecordIdMockedValue);
|
||||
snapshot.set(draftValueState, draftValueMockedValue);
|
||||
}}
|
||||
|
||||
@ -2,9 +2,9 @@ import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
|
||||
import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
@ -13,8 +13,6 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
</RelationPickerScopeInternalContext.Provider>
|
||||
);
|
||||
|
||||
const objectMetadataItemsMock = getObjectMetadataItemsMock();
|
||||
|
||||
const opportunityId = 'cb702502-4b1d-488e-9461-df3fb096ebf6';
|
||||
const personId = 'ab091fd9-1b81-4dfd-bfdb-564ffee032a2';
|
||||
|
||||
@ -70,7 +68,7 @@ describe('useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'
|
||||
},
|
||||
);
|
||||
act(() => {
|
||||
result.current.setObjectMetadata(objectMetadataItemsMock);
|
||||
result.current.setObjectMetadata(generatedMockObjectMetadataItems);
|
||||
});
|
||||
|
||||
expect(
|
||||
|
||||
@ -26,35 +26,13 @@ const companyMocks = [
|
||||
) {
|
||||
createCompanies(data: $data, upsert: $upsert) {
|
||||
__typename
|
||||
id
|
||||
visaSponsorship
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
updatedAt
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
position
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
employees
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
workPolicy
|
||||
visaSponsorship
|
||||
address {
|
||||
addressStreet1
|
||||
addressStreet2
|
||||
@ -65,16 +43,38 @@ const companyMocks = [
|
||||
addressLat
|
||||
addressLng
|
||||
}
|
||||
position
|
||||
employees
|
||||
deletedAt
|
||||
accountOwnerId
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
myCustomField
|
||||
createdAt
|
||||
accountOwnerId
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
workPolicy
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
tagline
|
||||
idealCustomerProfile
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
|
||||
import {
|
||||
query,
|
||||
responseData,
|
||||
@ -75,11 +75,9 @@ describe('useFilteredSearchEntityQuery', () => {
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
|
||||
setMetadataItems(mockObjectMetadataItems);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFilteredSearchEntityQuery({
|
||||
orderByField: 'name',
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
@ -45,7 +44,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS = (
|
||||
},
|
||||
{
|
||||
position: 2,
|
||||
fieldMetadataId: COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID,
|
||||
fieldMetadataId: 'REPLACE_ME',
|
||||
label: 'Name',
|
||||
size: 100,
|
||||
type: FieldMetadataType.Text,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
|
||||
export const SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS = [
|
||||
@ -15,7 +14,7 @@ export const SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS = [
|
||||
type: 'NUMBER',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID,
|
||||
fieldMetadataId: 'REPLACE_ME',
|
||||
label: 'Name',
|
||||
iconName: 'IconBuildingSkyscraper',
|
||||
type: 'TEXT',
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
|
||||
|
||||
export const SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS = [
|
||||
@ -13,7 +12,7 @@ export const SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS = [
|
||||
iconName: 'IconUsers',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID,
|
||||
fieldMetadataId: 'REPLACE_ME',
|
||||
label: 'Name',
|
||||
iconName: 'IconBuildingSkyscraper',
|
||||
},
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
|
||||
export const SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS = [
|
||||
@ -60,7 +59,7 @@ export const SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS = [
|
||||
{
|
||||
__typename: 'ViewField',
|
||||
id: 'cafacdc8-cbfc-4545-8242-94787f144ace',
|
||||
fieldMetadataId: COMPANY_LABEL_IDENTIFIER_FIELD_METADATA_ID,
|
||||
fieldMetadataId: 'REPLACE_ME',
|
||||
size: 180,
|
||||
createdAt: '2023-11-23T15:38:03.706Z',
|
||||
viewId: '20202020-2441-4424-8163-4002c523d415',
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
// Generate test for getCombinedViewFilters
|
||||
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { getCombinedViewFilters } from '../getCombinedViewFilters';
|
||||
|
||||
describe('getCombinedViewFilters', () => {
|
||||
it('should return expected combined view filters when additional filters are present', () => {
|
||||
const viewFilters: ViewFilter[] = [
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
];
|
||||
const toUpsertViewFilters: ViewFilter[] = [
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
];
|
||||
const toDeleteViewFilterIds: string[] = [];
|
||||
|
||||
expect(
|
||||
getCombinedViewFilters(
|
||||
viewFilters,
|
||||
toUpsertViewFilters,
|
||||
toDeleteViewFilterIds,
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return expected combined view filters when additional filters are not present', () => {
|
||||
const viewFilters: ViewFilter[] = [
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
];
|
||||
const toUpsertViewFilters: ViewFilter[] = [];
|
||||
const toDeleteViewFilterIds: string[] = [];
|
||||
|
||||
expect(
|
||||
getCombinedViewFilters(
|
||||
viewFilters,
|
||||
toUpsertViewFilters,
|
||||
toDeleteViewFilterIds,
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return expected combined view filters when additional filters are present and some filters are to be deleted', () => {
|
||||
const viewFilters: ViewFilter[] = [
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
];
|
||||
const toUpsertViewFilters: ViewFilter[] = [
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
},
|
||||
];
|
||||
const toDeleteViewFilterIds: string[] = ['id'];
|
||||
|
||||
expect(
|
||||
getCombinedViewFilters(
|
||||
viewFilters,
|
||||
toUpsertViewFilters,
|
||||
toDeleteViewFilterIds,
|
||||
),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,96 @@
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import {
|
||||
IconPlayerPlay,
|
||||
IconPlayerStop,
|
||||
IconPower,
|
||||
IconTrash,
|
||||
isDefined,
|
||||
} from 'twenty-ui';
|
||||
import { assertWorkflowWithCurrentVersionIsDefined } from '../utils/assertWorkflowWithCurrentVersionIsDefined';
|
||||
|
||||
export const RecordShowPageWorkflowHeader = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string | undefined;
|
||||
}) => {
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const isWaitingForWorkflowWithCurrentVersion =
|
||||
!isDefined(workflowWithCurrentVersion) ||
|
||||
!isDefined(workflowWithCurrentVersion.currentVersion);
|
||||
|
||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
title="Test"
|
||||
variant="secondary"
|
||||
Icon={IconPlayerPlay}
|
||||
disabled={isWaitingForWorkflowWithCurrentVersion}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
|
||||
{workflowWithCurrentVersion?.currentVersion?.status === 'DRAFT' &&
|
||||
workflowWithCurrentVersion.versions?.length > 1 ? (
|
||||
<Button
|
||||
title="Discard Draft"
|
||||
variant="secondary"
|
||||
Icon={IconTrash}
|
||||
disabled={isWaitingForWorkflowWithCurrentVersion}
|
||||
onClick={() => {
|
||||
assertWorkflowWithCurrentVersionIsDefined(
|
||||
workflowWithCurrentVersion,
|
||||
);
|
||||
|
||||
return deleteOneWorkflowVersion({
|
||||
workflowId: workflowWithCurrentVersion.id,
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{workflowWithCurrentVersion?.currentVersion?.status === 'DRAFT' ||
|
||||
workflowWithCurrentVersion?.currentVersion?.status === 'DEACTIVATED' ? (
|
||||
<Button
|
||||
title="Activate"
|
||||
variant="secondary"
|
||||
Icon={IconPower}
|
||||
disabled={isWaitingForWorkflowWithCurrentVersion}
|
||||
onClick={() => {
|
||||
assertWorkflowWithCurrentVersionIsDefined(
|
||||
workflowWithCurrentVersion,
|
||||
);
|
||||
|
||||
return activateWorkflowVersion(
|
||||
workflowWithCurrentVersion.currentVersion.id,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : workflowWithCurrentVersion?.currentVersion?.status === 'ACTIVE' ? (
|
||||
<Button
|
||||
title="Deactivate"
|
||||
variant="secondary"
|
||||
Icon={IconPlayerStop}
|
||||
disabled={isWaitingForWorkflowWithCurrentVersion}
|
||||
onClick={() => {
|
||||
assertWorkflowWithCurrentVersionIsDefined(
|
||||
workflowWithCurrentVersion,
|
||||
);
|
||||
|
||||
return deactivateWorkflowVersion(
|
||||
workflowWithCurrentVersion.currentVersion.id,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,666 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
|
||||
import { RecordShowPageWorkflowHeader } from '@/workflow/components/RecordShowPageWorkflowHeader';
|
||||
import { expect, within } from '@storybook/test';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
|
||||
const meta: Meta<typeof RecordShowPageWorkflowHeader> = {
|
||||
title: 'Modules/Workflow/RecordShowPageWorkflowHeader',
|
||||
component: RecordShowPageWorkflowHeader,
|
||||
decorators: [
|
||||
ComponentDecorator,
|
||||
ObjectMetadataItemsDecorator,
|
||||
SnackBarDecorator,
|
||||
],
|
||||
parameters: {
|
||||
container: { width: 728 },
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RecordShowPageWorkflowHeader>;
|
||||
|
||||
const blankInitialVersionWorkflowId = '78fd5184-08f4-47b7-bb60-adb541608f65';
|
||||
|
||||
export const BlankInitialVersion: Story = {
|
||||
args: {
|
||||
workflowId: blankInitialVersionWorkflowId,
|
||||
},
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query('FindManyWorkflows', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflows: {
|
||||
__typename: 'WorkflowConnection',
|
||||
totalCount: 1,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor:
|
||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
||||
endCursor:
|
||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowEdge',
|
||||
cursor:
|
||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
||||
node: {
|
||||
__typename: 'Workflow',
|
||||
id: blankInitialVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query('FindOneWorkflow', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflow: {
|
||||
__typename: 'Workflow',
|
||||
id: blankInitialVersionWorkflowId,
|
||||
name: '1231 qqerrt',
|
||||
statuses: null,
|
||||
lastPublishedVersionId: '',
|
||||
deletedAt: null,
|
||||
updatedAt: '2024-09-19T10:10:04.505Z',
|
||||
position: 0,
|
||||
createdAt: '2024-09-19T10:10:04.505Z',
|
||||
favorites: {
|
||||
__typename: 'FavoriteConnection',
|
||||
edges: [],
|
||||
},
|
||||
eventListeners: {
|
||||
__typename: 'WorkflowEventListenerConnection',
|
||||
edges: [],
|
||||
},
|
||||
runs: {
|
||||
__typename: 'WorkflowRunConnection',
|
||||
edges: [],
|
||||
},
|
||||
versions: {
|
||||
__typename: 'WorkflowVersionConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
||||
steps: null,
|
||||
createdAt: '2024-09-19T10:10:04.725Z',
|
||||
status: 'DRAFT',
|
||||
name: 'v1',
|
||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
||||
trigger: null,
|
||||
deletedAt: null,
|
||||
workflowId: blankInitialVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query('FindManyWorkflowVersions', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflowVersions: {
|
||||
__typename: 'WorkflowVersionConnection',
|
||||
totalCount: 1,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
||||
endCursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
cursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
||||
steps: null,
|
||||
createdAt: '2024-09-19T10:10:04.725Z',
|
||||
status: 'DRAFT',
|
||||
name: 'v1',
|
||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
||||
trigger: null,
|
||||
deletedAt: null,
|
||||
workflowId: blankInitialVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
...graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
},
|
||||
play: async () => {
|
||||
const canvas = within(document.body);
|
||||
|
||||
expect(await canvas.findByText('Test')).toBeVisible();
|
||||
expect(await canvas.findByText('Activate')).toBeVisible();
|
||||
expect(canvas.queryByText('Discard Draft')).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
const activeVersionWorkflowId = 'ca177fb1-7780-4911-8b1f-ef0a245fbd61';
|
||||
|
||||
export const ActiveVersion: Story = {
|
||||
args: {
|
||||
workflowId: activeVersionWorkflowId,
|
||||
},
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query('FindManyWorkflows', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflows: {
|
||||
__typename: 'WorkflowConnection',
|
||||
totalCount: 1,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor:
|
||||
'eyJwb3NpdGlvbiI6LTEsImlkIjoiN2JlM2E4MmMtNDRiNy00MTUwLWEyZTgtNDA4ODcxNDZmNGQ0In0=',
|
||||
endCursor:
|
||||
'eyJwb3NpdGlvbiI6LTEsImlkIjoiN2JlM2E4MmMtNDRiNy00MTUwLWEyZTgtNDA4ODcxNDZmNGQ0In0=',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowEdge',
|
||||
cursor:
|
||||
'eyJwb3NpdGlvbiI6LTEsImlkIjoiN2JlM2E4MmMtNDRiNy00MTUwLWEyZTgtNDA4ODcxNDZmNGQ0In0=',
|
||||
node: {
|
||||
__typename: 'Workflow',
|
||||
id: activeVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query('FindOneWorkflow', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflow: {
|
||||
__typename: 'Workflow',
|
||||
name: 'test qqqq',
|
||||
lastPublishedVersionId: 'b57e577a-ae55-4de2-ba08-fe361dcc1a57',
|
||||
id: activeVersionWorkflowId,
|
||||
deletedAt: null,
|
||||
statuses: null,
|
||||
createdAt: '2024-09-20T10:18:59.977Z',
|
||||
updatedAt: '2024-09-20T16:59:37.212Z',
|
||||
position: -1,
|
||||
runs: {
|
||||
__typename: 'WorkflowRunConnection',
|
||||
edges: [],
|
||||
},
|
||||
favorites: {
|
||||
__typename: 'FavoriteConnection',
|
||||
edges: [],
|
||||
},
|
||||
eventListeners: {
|
||||
__typename: 'WorkflowEventListenerConnection',
|
||||
edges: [],
|
||||
},
|
||||
versions: {
|
||||
__typename: 'WorkflowVersionConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-20T16:59:37.212Z',
|
||||
status: 'ARCHIVED',
|
||||
deletedAt: null,
|
||||
steps: [
|
||||
{
|
||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
workflowId: activeVersionWorkflowId,
|
||||
trigger: {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: 'note.created',
|
||||
},
|
||||
},
|
||||
name: 'v1',
|
||||
id: '394cd0b5-bd48-41d7-a110-a92cafaf171d',
|
||||
createdAt: '2024-09-20T10:19:00.141Z',
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-20T17:01:15.637Z',
|
||||
status: 'DRAFT',
|
||||
deletedAt: null,
|
||||
steps: [
|
||||
{
|
||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4177d57d-35dc-4eb1-a467-07e25cb31da0',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '0cc392d9-5f28-4d92-90a0-08180f264e68',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
workflowId: activeVersionWorkflowId,
|
||||
trigger: {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: 'note.created',
|
||||
},
|
||||
},
|
||||
name: 'v3',
|
||||
id: '5eae34ef-9d62-4a9e-b827-3eb927481728',
|
||||
createdAt: '2024-09-20T17:01:15.637Z',
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-20T17:00:16.097Z',
|
||||
status: 'ACTIVE',
|
||||
deletedAt: null,
|
||||
steps: [
|
||||
{
|
||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4177d57d-35dc-4eb1-a467-07e25cb31da0',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
workflowId: activeVersionWorkflowId,
|
||||
trigger: {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: 'note.created',
|
||||
},
|
||||
},
|
||||
name: 'v2',
|
||||
id: 'b57e577a-ae55-4de2-ba08-fe361dcc1a57',
|
||||
createdAt: '2024-09-20T16:59:35.755Z',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query('FindManyWorkflowVersions', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflowVersions: {
|
||||
__typename: 'WorkflowVersionConnection',
|
||||
totalCount: 3,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: true,
|
||||
hasPreviousPage: false,
|
||||
startCursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTIwVDE3OjAxOjE1LjYzN1oiLCJpZCI6IjVlYWUzNGVmLTlkNjItNGE5ZS1iODI3LTNlYjkyNzQ4MTcyOCJ9',
|
||||
endCursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTIwVDE3OjAxOjE1LjYzN1oiLCJpZCI6IjVlYWUzNGVmLTlkNjItNGE5ZS1iODI3LTNlYjkyNzQ4MTcyOCJ9',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
cursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTIwVDE3OjAxOjE1LjYzN1oiLCJpZCI6IjVlYWUzNGVmLTlkNjItNGE5ZS1iODI3LTNlYjkyNzQ4MTcyOCJ9',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-20T17:01:15.637Z',
|
||||
status: 'ACTIVE',
|
||||
deletedAt: null,
|
||||
steps: [
|
||||
{
|
||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4177d57d-35dc-4eb1-a467-07e25cb31da0',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '0cc392d9-5f28-4d92-90a0-08180f264e68',
|
||||
name: 'Code',
|
||||
type: 'CODE',
|
||||
valid: false,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
serverlessFunctionId: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
workflowId: activeVersionWorkflowId,
|
||||
trigger: {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: 'note.created',
|
||||
},
|
||||
},
|
||||
name: 'v3',
|
||||
id: '5eae34ef-9d62-4a9e-b827-3eb927481728',
|
||||
createdAt: '2024-09-20T17:01:15.637Z',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
...graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
},
|
||||
play: async () => {
|
||||
const canvas = within(document.body);
|
||||
|
||||
expect(await canvas.findByText('Test')).toBeVisible();
|
||||
expect(await canvas.findByText('Deactivate')).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
const draftVersionWithPreviousActiveVersionWorkflowId =
|
||||
'89c00f14-4ebd-4675-a098-cdf59eee372b';
|
||||
|
||||
export const DraftVersionWithPreviousActiveVersion: Story = {
|
||||
args: {
|
||||
workflowId: draftVersionWithPreviousActiveVersionWorkflowId,
|
||||
},
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query('FindManyWorkflows', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflows: {
|
||||
__typename: 'WorkflowConnection',
|
||||
totalCount: 1,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor:
|
||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
||||
endCursor:
|
||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowEdge',
|
||||
cursor:
|
||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
||||
node: {
|
||||
__typename: 'Workflow',
|
||||
id: draftVersionWithPreviousActiveVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query('FindOneWorkflow', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflow: {
|
||||
__typename: 'Workflow',
|
||||
id: draftVersionWithPreviousActiveVersionWorkflowId,
|
||||
name: '1231 qqerrt',
|
||||
statuses: null,
|
||||
lastPublishedVersionId: '',
|
||||
deletedAt: null,
|
||||
updatedAt: '2024-09-19T10:10:04.505Z',
|
||||
position: 0,
|
||||
createdAt: '2024-09-19T10:10:04.505Z',
|
||||
favorites: {
|
||||
__typename: 'FavoriteConnection',
|
||||
edges: [],
|
||||
},
|
||||
eventListeners: {
|
||||
__typename: 'WorkflowEventListenerConnection',
|
||||
edges: [],
|
||||
},
|
||||
runs: {
|
||||
__typename: 'WorkflowRunConnection',
|
||||
edges: [],
|
||||
},
|
||||
versions: {
|
||||
__typename: 'WorkflowVersionConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
||||
steps: null,
|
||||
createdAt: '2024-09-19T10:10:04.725Z',
|
||||
status: 'ACTIVE',
|
||||
name: 'v1',
|
||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
||||
trigger: null,
|
||||
deletedAt: null,
|
||||
workflowId:
|
||||
draftVersionWithPreviousActiveVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
||||
steps: null,
|
||||
createdAt: '2024-09-19T10:10:05.725Z',
|
||||
status: 'DRAFT',
|
||||
name: 'v2',
|
||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f1',
|
||||
trigger: null,
|
||||
deletedAt: null,
|
||||
workflowId:
|
||||
draftVersionWithPreviousActiveVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query('FindManyWorkflowVersions', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
workflowVersions: {
|
||||
__typename: 'WorkflowVersionConnection',
|
||||
totalCount: 1,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
||||
endCursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
__typename: 'WorkflowVersionEdge',
|
||||
cursor:
|
||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
||||
node: {
|
||||
__typename: 'WorkflowVersion',
|
||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
||||
steps: null,
|
||||
createdAt: '2024-09-19T10:10:05.725Z',
|
||||
status: 'DRAFT',
|
||||
name: 'v2',
|
||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f1',
|
||||
trigger: null,
|
||||
deletedAt: null,
|
||||
workflowId:
|
||||
draftVersionWithPreviousActiveVersionWorkflowId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
...graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
},
|
||||
play: async () => {
|
||||
const canvas = within(document.body);
|
||||
|
||||
expect(await canvas.findByText('Test')).toBeVisible();
|
||||
expect(await canvas.findByText('Discard Draft')).toBeVisible();
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const ACTIVATE_WORKFLOW_VERSION = gql`
|
||||
mutation ActivateWorkflowVersion($workflowVersionId: String!) {
|
||||
activateWorkflowVersion(workflowVersionId: $workflowVersionId)
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DEACTIVATE_WORKFLOW_VERSION = gql`
|
||||
mutation DeactivateWorkflowVersion($workflowVersionId: String!) {
|
||||
deactivateWorkflowVersion(workflowVersionId: $workflowVersionId)
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,45 @@
|
||||
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
||||
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { ACTIVATE_WORKFLOW_VERSION } from '@/workflow/graphql/activateWorkflowVersion';
|
||||
import {
|
||||
ActivateWorkflowVersionMutation,
|
||||
ActivateWorkflowVersionMutationVariables,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const useActivateWorkflowVersion = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
const [mutate] = useMutation<
|
||||
ActivateWorkflowVersionMutation,
|
||||
ActivateWorkflowVersionMutationVariables
|
||||
>(ACTIVATE_WORKFLOW_VERSION, {
|
||||
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
|
||||
});
|
||||
|
||||
const { findOneRecordQuery: findOneWorkflowVersionQuery } =
|
||||
useFindOneRecordQuery({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const activateWorkflowVersion = async (workflowVersionId: string) => {
|
||||
await mutate({
|
||||
variables: {
|
||||
workflowVersionId,
|
||||
},
|
||||
});
|
||||
|
||||
await apolloClient.query({
|
||||
query: findOneWorkflowVersionQuery,
|
||||
variables: {
|
||||
objectRecordId: workflowVersionId,
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
};
|
||||
|
||||
return { activateWorkflowVersion };
|
||||
};
|
||||
@ -0,0 +1,30 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
|
||||
export const useCreateNewWorkflowVersion = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { createOneRecord: createOneWorkflowVersion } =
|
||||
useCreateOneRecord<WorkflowVersion>({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const createNewWorkflowVersion = (
|
||||
workflowVersionData: Pick<
|
||||
WorkflowVersion,
|
||||
'name' | 'status' | 'trigger' | 'steps'
|
||||
>,
|
||||
) => {
|
||||
return createOneWorkflowVersion({
|
||||
workflowId,
|
||||
...workflowVersionData,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
createNewWorkflowVersion,
|
||||
};
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||
import { workflowCreateStepFromParentStepIdState } from '@/workflow/states/workflowCreateStepFromParentStepIdState';
|
||||
import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/states/workflowDiagramTriggerNodeSelectionState';
|
||||
import {
|
||||
@ -31,7 +32,11 @@ export const useCreateStep = ({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const insertNodeAndSave = ({
|
||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion({
|
||||
workflowId: workflow.id,
|
||||
});
|
||||
|
||||
const insertNodeAndSave = async ({
|
||||
parentNodeId,
|
||||
nodeToAdd,
|
||||
}: {
|
||||
@ -43,15 +48,28 @@ export const useCreateStep = ({
|
||||
throw new Error("Can't add a node when there is no current version.");
|
||||
}
|
||||
|
||||
return updateOneWorkflowVersion({
|
||||
idToUpdate: currentVersion.id,
|
||||
updateOneRecordInput: {
|
||||
steps: insertStep({
|
||||
steps: currentVersion.steps ?? [],
|
||||
parentStepId: parentNodeId,
|
||||
stepToAdd: nodeToAdd,
|
||||
}),
|
||||
},
|
||||
const updatedSteps = insertStep({
|
||||
steps: currentVersion.steps ?? [],
|
||||
parentStepId: parentNodeId,
|
||||
stepToAdd: nodeToAdd,
|
||||
});
|
||||
|
||||
if (workflow.currentVersion.status === 'DRAFT') {
|
||||
await updateOneWorkflowVersion({
|
||||
idToUpdate: currentVersion.id,
|
||||
updateOneRecordInput: {
|
||||
steps: updatedSteps,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await createNewWorkflowVersion({
|
||||
name: `v${workflow.versions.length + 1}`,
|
||||
status: 'DRAFT',
|
||||
trigger: workflow.currentVersion.trigger,
|
||||
steps: updatedSteps,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
||||
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { DEACTIVATE_WORKFLOW_VERSION } from '@/workflow/graphql/deactivateWorkflowVersion';
|
||||
import {
|
||||
ActivateWorkflowVersionMutation,
|
||||
ActivateWorkflowVersionMutationVariables,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const useDeactivateWorkflowVersion = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
const [mutate] = useMutation<
|
||||
ActivateWorkflowVersionMutation,
|
||||
ActivateWorkflowVersionMutationVariables
|
||||
>(DEACTIVATE_WORKFLOW_VERSION, {
|
||||
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
|
||||
});
|
||||
|
||||
const { findOneRecordQuery: findOneWorkflowVersionQuery } =
|
||||
useFindOneRecordQuery({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const deactivateWorkflowVersion = async (workflowVersionId: string) => {
|
||||
await mutate({
|
||||
variables: {
|
||||
workflowVersionId,
|
||||
},
|
||||
});
|
||||
|
||||
await apolloClient.query({
|
||||
query: findOneWorkflowVersionQuery,
|
||||
variables: {
|
||||
objectRecordId: workflowVersionId,
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
};
|
||||
|
||||
return { deactivateWorkflowVersion };
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
export const useDeleteOneWorkflowVersion = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { findOneRecordQuery: findOneWorkflowRecordQuery } =
|
||||
useFindOneRecordQuery({
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
});
|
||||
|
||||
const { deleteOneRecord } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const deleteOneWorkflowVersion = async ({
|
||||
workflowId,
|
||||
workflowVersionId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
workflowVersionId: string;
|
||||
}) => {
|
||||
await deleteOneRecord(workflowVersionId);
|
||||
|
||||
await apolloClient.query({
|
||||
query: findOneWorkflowRecordQuery,
|
||||
variables: {
|
||||
objectRecordId: workflowId,
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
};
|
||||
|
||||
return { deleteOneWorkflowVersion };
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||
import {
|
||||
WorkflowStep,
|
||||
WorkflowVersion,
|
||||
@ -20,20 +21,37 @@ export const useUpdateWorkflowVersionStep = ({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion({
|
||||
workflowId: workflow.id,
|
||||
});
|
||||
|
||||
const updateStep = async (updatedStep: WorkflowStep) => {
|
||||
if (!isDefined(workflow.currentVersion)) {
|
||||
throw new Error('Can not update an undefined workflow version.');
|
||||
}
|
||||
|
||||
await updateOneWorkflowVersion({
|
||||
idToUpdate: workflow.currentVersion.id,
|
||||
updateOneRecordInput: {
|
||||
steps: replaceStep({
|
||||
steps: workflow.currentVersion.steps ?? [],
|
||||
stepId,
|
||||
stepToReplace: updatedStep,
|
||||
}),
|
||||
},
|
||||
const updatedSteps = replaceStep({
|
||||
steps: workflow.currentVersion.steps ?? [],
|
||||
stepId,
|
||||
stepToReplace: updatedStep,
|
||||
});
|
||||
|
||||
if (workflow.currentVersion.status === 'DRAFT') {
|
||||
await updateOneWorkflowVersion({
|
||||
idToUpdate: workflow.currentVersion.id,
|
||||
updateOneRecordInput: {
|
||||
steps: updatedSteps,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await createNewWorkflowVersion({
|
||||
name: `v${workflow.versions.length + 1}`,
|
||||
status: 'DRAFT',
|
||||
trigger: workflow.currentVersion.trigger,
|
||||
steps: updatedSteps,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||
import {
|
||||
WorkflowTrigger,
|
||||
WorkflowVersion,
|
||||
@ -17,16 +18,31 @@ export const useUpdateWorkflowVersionTrigger = ({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion({
|
||||
workflowId: workflow.id,
|
||||
});
|
||||
|
||||
const updateTrigger = async (updatedTrigger: WorkflowTrigger) => {
|
||||
if (!isDefined(workflow.currentVersion)) {
|
||||
throw new Error('Can not update an undefined workflow version.');
|
||||
}
|
||||
|
||||
await updateOneWorkflowVersion({
|
||||
idToUpdate: workflow.currentVersion.id,
|
||||
updateOneRecordInput: {
|
||||
trigger: updatedTrigger,
|
||||
},
|
||||
if (workflow.currentVersion.status === 'DRAFT') {
|
||||
await updateOneWorkflowVersion({
|
||||
idToUpdate: workflow.currentVersion.id,
|
||||
updateOneRecordInput: {
|
||||
trigger: updatedTrigger,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await createNewWorkflowVersion({
|
||||
name: `v${workflow.versions.length + 1}`,
|
||||
status: 'DRAFT',
|
||||
trigger: updatedTrigger,
|
||||
steps: workflow.currentVersion.steps,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -19,6 +19,9 @@ export const useWorkflowWithCurrentVersion = (
|
||||
id: true,
|
||||
name: true,
|
||||
statuses: true,
|
||||
versions: {
|
||||
totalCount: true,
|
||||
},
|
||||
},
|
||||
skip: !isDefined(workflowId),
|
||||
});
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
import {
|
||||
Workflow,
|
||||
WorkflowVersion,
|
||||
WorkflowWithCurrentVersion,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
export function assertWorkflowWithCurrentVersionIsDefined(
|
||||
workflow: WorkflowWithCurrentVersion | undefined,
|
||||
): asserts workflow is Workflow & { currentVersion: WorkflowVersion } {
|
||||
if (!isDefined(workflow) || !isDefined(workflow.currentVersion)) {
|
||||
throw new Error('Expected workflow and its current version to be defined');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user