From 800e50840191d26597ecbfebdb13005717ee62e9 Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Tue, 8 Oct 2024 18:16:36 +0200 Subject: [PATCH] Create workflow version show page (#7466) In this PR: - Refactored components to clarify their behavior. For example, I renamed the `Workflow` component to `WorkflowVisualizer`. This moved forward the issue #7010. - Create two variants of several workflow-related components: one version for editing and another for viewing. For instance, there is `WorkflowDiagramCanvasEditable.tsx` and `WorkflowDiagramCanvasReadonly.tsx` - Implement the show page for workflow versions. On this page, we display a readonly workflow visualizer. Users can click on nodes and it will expand the right drawer. - I added buttons in the header of the RecordShowPage for workflow versions: users can activate, deactivate or use the currently viewed version as the next draft. **There are many cache desynchronisation and I'll fix them really soon.** ## Demo (Turn sound on) https://github.com/user-attachments/assets/97fafa48-8902-4dab-8b39-f40848bf041e --- .../components/RightDrawerRouter.tsx | 2 + .../constants/RightDrawerPageIcons.ts | 1 + .../constants/RightDrawerPageTitles.ts | 1 + .../right-drawer/types/RightDrawerPages.ts | 1 + .../components/ShowPageRightContainer.tsx | 45 ++++++- .../RecordShowPageWorkflowHeader.tsx | 2 +- .../RecordShowPageWorkflowVersionHeader.tsx | 127 ++++++++++++++++++ .../RightDrawerWorkflowEditStepContent.tsx | 102 ++------------ .../RightDrawerWorkflowViewStep.tsx | 22 +++ .../RightDrawerWorkflowViewStepContent.tsx | 26 ++++ .../modules/workflow/components/Workflow.tsx | 58 -------- ...nvas.tsx => WorkflowDiagramCanvasBase.tsx} | 75 +++++++---- .../WorkflowDiagramCanvasEditable.tsx | 30 +++++ ...> WorkflowDiagramCanvasEditableEffect.tsx} | 35 +---- .../WorkflowDiagramCanvasReadonly.tsx | 28 ++++ .../WorkflowDiagramCanvasReadonlyEffect.tsx | 39 ++++++ ...owEffect.tsx => WorkflowDiagramEffect.tsx} | 18 +-- ...de.tsx => WorkflowDiagramStepNodeBase.tsx} | 37 +---- .../WorkflowDiagramStepNodeEditable.tsx | 45 +++++++ .../WorkflowDiagramStepNodeReadonly.tsx | 10 ++ .../WorkflowEditActionFormSendEmail.tsx | 40 ++++-- ...rkflowEditActionFormServerlessFunction.tsx | 34 +++-- .../components/WorkflowEditTriggerForm.tsx | 28 +++- .../components/WorkflowStepDetail.tsx | 80 +++++++++++ .../components/WorkflowVersionVisualizer.tsx | 23 ++++ .../WorkflowVersionVisualizerEffect.tsx | 36 +++++ .../components/WorkflowVisualizer.tsx | 34 +++++ .../components/WorkflowVisualizerEffect.tsx | 17 +++ .../hooks/useCreateNewWorkflowVersion.tsx | 13 +- .../modules/workflow/hooks/useCreateStep.tsx | 5 +- .../workflow/hooks/useDeleteOneStep.tsx | 6 +- .../hooks/useTriggerNodeSelection.tsx | 36 +++++ .../hooks/useUpdateWorkflowVersionStep.tsx | 5 +- .../hooks/useUpdateWorkflowVersionTrigger.tsx | 5 +- .../workflow/hooks/useWorkflowVersion.tsx | 36 +++++ .../workflow/states/workflowVersionIdState.ts | 6 + .../modules/workflow/types/WorkflowDiagram.ts | 5 + .../utils/getStepDefinitionOrThrow.ts | 45 +++++++ .../pages/object-record/RecordShowPage.tsx | 8 +- 39 files changed, 856 insertions(+), 310 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowVersionHeader.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowViewStep.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowViewStepContent.tsx delete mode 100644 packages/twenty-front/src/modules/workflow/components/Workflow.tsx rename packages/twenty-front/src/modules/workflow/components/{WorkflowDiagramCanvas.tsx => WorkflowDiagramCanvasBase.tsx} (67%) create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvasEditable.tsx rename packages/twenty-front/src/modules/workflow/components/{WorkflowDiagramCanvasEffect.tsx => WorkflowDiagramCanvasEditableEffect.tsx} (62%) create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvasReadonly.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvasReadonlyEffect.tsx rename packages/twenty-front/src/modules/workflow/components/{WorkflowEffect.tsx => WorkflowDiagramEffect.tsx} (85%) rename packages/twenty-front/src/modules/workflow/components/{WorkflowDiagramStepNode.tsx => WorkflowDiagramStepNodeBase.tsx} (61%) create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNodeEditable.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNodeReadonly.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowStepDetail.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowVersionVisualizer.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowVersionVisualizerEffect.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowVisualizer.tsx create mode 100644 packages/twenty-front/src/modules/workflow/components/WorkflowVisualizerEffect.tsx create mode 100644 packages/twenty-front/src/modules/workflow/hooks/useTriggerNodeSelection.tsx create mode 100644 packages/twenty-front/src/modules/workflow/hooks/useWorkflowVersion.tsx create mode 100644 packages/twenty-front/src/modules/workflow/states/workflowVersionIdState.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/getStepDefinitionOrThrow.ts diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerRouter.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerRouter.tsx index 972b2a75e3a2..a5640cfc045e 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerRouter.tsx +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerRouter.tsx @@ -11,6 +11,7 @@ import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDraw import { ComponentByRightDrawerPage } from '@/ui/layout/right-drawer/types/ComponentByRightDrawerPage'; import { RightDrawerWorkflowEditStep } from '@/workflow/components/RightDrawerWorkflowEditStep'; import { RightDrawerWorkflowSelectAction } from '@/workflow/components/RightDrawerWorkflowSelectAction'; +import { RightDrawerWorkflowViewStep } from '@/workflow/components/RightDrawerWorkflowViewStep'; import { isDefined } from 'twenty-ui'; import { rightDrawerPageState } from '../states/rightDrawerPageState'; import { RightDrawerPages } from '../types/RightDrawerPages'; @@ -41,6 +42,7 @@ const RIGHT_DRAWER_PAGES_CONFIG: ComponentByRightDrawerPage = { ), [RightDrawerPages.WorkflowStepEdit]: , + [RightDrawerPages.WorkflowStepView]: , }; export const RightDrawerRouter = () => { diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts index 7fc5d9849d89..85dc75ee18b6 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts @@ -7,4 +7,5 @@ export const RIGHT_DRAWER_PAGE_ICONS = { [RightDrawerPages.Copilot]: 'IconSparkles', [RightDrawerPages.WorkflowStepEdit]: 'IconSparkles', [RightDrawerPages.WorkflowStepSelectAction]: 'IconSparkles', + [RightDrawerPages.WorkflowStepView]: 'IconSparkles', }; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts index 749fb10384fc..9cba79382a0a 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts @@ -7,4 +7,5 @@ export const RIGHT_DRAWER_PAGE_TITLES = { [RightDrawerPages.Copilot]: 'Copilot', [RightDrawerPages.WorkflowStepEdit]: 'Workflow', [RightDrawerPages.WorkflowStepSelectAction]: 'Workflow', + [RightDrawerPages.WorkflowStepView]: 'Workflow', }; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts index f016669b48a2..68e20913a4f6 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts @@ -4,5 +4,6 @@ export enum RightDrawerPages { ViewRecord = 'view-record', Copilot = 'copilot', WorkflowStepSelectAction = 'workflow-step-select-action', + WorkflowStepView = 'workflow-step-view', WorkflowStepEdit = 'workflow-step-edit', } diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx index dab0a2104e12..6088034bbc7a 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx @@ -15,7 +15,10 @@ import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/Show import { TabList } from '@/ui/layout/tab/components/TabList'; import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -import { Workflow } from '@/workflow/components/Workflow'; +import { WorkflowVersionVisualizer } from '@/workflow/components/WorkflowVersionVisualizer'; +import { WorkflowVersionVisualizerEffect } from '@/workflow/components/WorkflowVersionVisualizerEffect'; +import { WorkflowVisualizer } from '@/workflow/components/WorkflowVisualizer'; +import { WorkflowVisualizerEffect } from '@/workflow/components/WorkflowVisualizerEffect'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; import { useState } from 'react'; @@ -130,6 +133,10 @@ export const ShowPageRightContainer = ({ isWorkflowEnabled && targetableObject.targetObjectNameSingular === CoreObjectNameSingular.Workflow; + const isWorkflowVersion = + isWorkflowEnabled && + targetableObject.targetObjectNameSingular === + CoreObjectNameSingular.WorkflowVersion; const shouldDisplayCalendarTab = isCompanyOrPerson; const shouldDisplayEmailsTab = emails && isCompanyOrPerson; @@ -162,7 +169,7 @@ export const ShowPageRightContainer = ({ id: 'timeline', title: 'Timeline', Icon: IconTimelineEvent, - hide: !timeline || isInRightDrawer || isWorkflow, + hide: !timeline || isInRightDrawer || isWorkflow || isWorkflowVersion, }, { id: 'tasks', @@ -174,7 +181,8 @@ export const ShowPageRightContainer = ({ CoreObjectNameSingular.Note || targetableObject.targetObjectNameSingular === CoreObjectNameSingular.Task || - isWorkflow, + isWorkflow || + isWorkflowVersion, }, { id: 'notes', @@ -186,13 +194,14 @@ export const ShowPageRightContainer = ({ CoreObjectNameSingular.Note || targetableObject.targetObjectNameSingular === CoreObjectNameSingular.Task || - isWorkflow, + isWorkflow || + isWorkflowVersion, }, { id: 'files', title: 'Files', Icon: IconPaperclip, - hide: !notes || isWorkflow, + hide: !notes || isWorkflow || isWorkflowVersion, }, { id: 'emails', @@ -212,6 +221,12 @@ export const ShowPageRightContainer = ({ Icon: IconSettings, hide: !isWorkflow, }, + { + id: 'workflowVersion', + title: 'Workflow Version', + Icon: IconSettings, + hide: !isWorkflowVersion, + }, ]; const renderActiveTabContent = () => { switch (activeTabId) { @@ -251,7 +266,25 @@ export const ShowPageRightContainer = ({ case 'calendar': return ; case 'workflow': - return ; + return ( + <> + + + + + ); + case 'workflowVersion': + return ( + <> + + + + + ); default: return <>; } diff --git a/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowHeader.tsx b/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowHeader.tsx index 4afb9839e7ea..cfea2f3b543e 100644 --- a/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowHeader.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowHeader.tsx @@ -15,7 +15,7 @@ import { assertWorkflowWithCurrentVersionIsDefined } from '../utils/assertWorkfl export const RecordShowPageWorkflowHeader = ({ workflowId, }: { - workflowId: string | undefined; + workflowId: string; }) => { const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId); diff --git a/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowVersionHeader.tsx b/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowVersionHeader.tsx new file mode 100644 index 000000000000..d7d66c2dc878 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/components/RecordShowPageWorkflowVersionHeader.tsx @@ -0,0 +1,127 @@ +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; +import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; +import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { Button } from '@/ui/input/button/components/Button'; +import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion'; +import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion'; +import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion'; +import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion'; +import { Workflow, WorkflowVersion } from '@/workflow/types/Workflow'; +import { IconPencil, IconPlayerStop, IconPower, isDefined } from 'twenty-ui'; + +export const RecordShowPageWorkflowVersionHeader = ({ + workflowVersionId, +}: { + workflowVersionId: string; +}) => { + const workflowVersion = useWorkflowVersion(workflowVersionId); + + const workflowVersionRelatedWorkflowQuery = useFindOneRecord< + Pick + >({ + objectNameSingular: CoreObjectNameSingular.Workflow, + objectRecordId: workflowVersion?.workflowId, + recordGqlFields: { + id: true, + lastPublishedVersionId: true, + }, + skip: !isDefined(workflowVersion), + }); + + // TODO: In the future, use the workflow.status property to determine if there is a draft version + const { + records: draftWorkflowVersions, + loading: loadingDraftWorkflowVersions, + } = useFindManyRecords({ + objectNameSingular: CoreObjectNameSingular.WorkflowVersion, + filter: { + workflowId: { + eq: workflowVersion?.workflow.id, + }, + status: { + eq: 'DRAFT', + }, + }, + skip: !isDefined(workflowVersion), + limit: 1, + }); + + const showUseAsDraftButton = + !loadingDraftWorkflowVersions && + isDefined(workflowVersion) && + !workflowVersionRelatedWorkflowQuery.loading && + isDefined(workflowVersionRelatedWorkflowQuery.record) && + workflowVersion.status !== 'DRAFT' && + workflowVersion.id !== + workflowVersionRelatedWorkflowQuery.record.lastPublishedVersionId; + + const hasAlreadyDraftVersion = + !loadingDraftWorkflowVersions && draftWorkflowVersions.length > 0; + + const isWaitingForWorkflowVersion = !isDefined(workflowVersion); + + const { activateWorkflowVersion } = useActivateWorkflowVersion(); + const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion(); + const { createNewWorkflowVersion } = useCreateNewWorkflowVersion(); + + const { updateOneRecord: updateOneWorkflowVersion } = + useUpdateOneRecord({ + objectNameSingular: CoreObjectNameSingular.WorkflowVersion, + }); + + return ( + <> + {showUseAsDraftButton ? ( +