From 454dd85e0287e6a48764bbaa9fc1f375d5399aa9 Mon Sep 17 00:00:00 2001
From: Manas Kenge <110519001+Manas-Kenge@users.noreply.github.com>
Date: Fri, 22 Aug 2025 18:36:36 +0530
Subject: [PATCH 1/2] add assign dialog
---
.../manager/api/services/workflow/base.py | 30 ++++
.../manager/api/services/workflow/workflow.py | 1 +
.../src/actions/workflow.ts | 2 +-
.../components/Workflow/ActionsToolbar.tsx | 12 +-
.../components/Workflow/AssignWorkflow.tsx | 153 ++++++++++++++++++
.../src/types/workflow.ts | 12 ++
6 files changed, 208 insertions(+), 2 deletions(-)
create mode 100644 frontend/packages/volto-workflow-manager/src/components/Workflow/AssignWorkflow.tsx
diff --git a/backend/src/workflow/manager/api/services/workflow/base.py b/backend/src/workflow/manager/api/services/workflow/base.py
index 7dd08aa..f4c5bc5 100644
--- a/backend/src/workflow/manager/api/services/workflow/base.py
+++ b/backend/src/workflow/manager/api/services/workflow/base.py
@@ -40,6 +40,12 @@ def portal(self):
def portal_workflow(self):
"""Returns the portal_workflow tool."""
return getToolByName(self.context, "portal_workflow")
+
+ @property
+ @memoize
+ def portal_types(self):
+ """Returns the portal_types tool."""
+ return getToolByName(self.context, "portal_types")
# --- Selected Object Properties (Driven by __init__) ---
@@ -189,6 +195,30 @@ def assignable_types(self):
key=lambda v: v['title']
)
+ def get_assignable_types_for(self, workflow_id):
+ """
+ Returns a list of content types that do not currently have the
+ specified workflow assigned.
+ """
+ # Get the list of types already using this workflow
+ assigned_types = self.get_assigned_types_for(workflow_id)
+
+ # Get all user-friendly content types
+ vocab_factory = getUtility(IVocabularyFactory,
+ name="plone.app.vocabularies.ReallyUserFriendlyTypes")
+ all_types = vocab_factory(self.context)
+
+ assignable_types = []
+ for term in all_types:
+ # A type is assignable if its ID is not in the assigned list
+ if term.value not in assigned_types:
+ assignable_types.append({
+ "id": term.value,
+ "title": term.title
+ })
+
+ return sorted(assignable_types, key=lambda v: v['title'])
+
def get_assigned_types_for(self, workflow_id):
"""Returns a list of content type IDs assigned to a workflow."""
assigned = []
diff --git a/backend/src/workflow/manager/api/services/workflow/workflow.py b/backend/src/workflow/manager/api/services/workflow/workflow.py
index 9bd7e30..8609a1c 100644
--- a/backend/src/workflow/manager/api/services/workflow/workflow.py
+++ b/backend/src/workflow/manager/api/services/workflow/workflow.py
@@ -40,6 +40,7 @@ def _serialize_workflow(workflow, base):
],
"assigned_types": workflow_base.get_assigned_types_for(workflow.id),
"context_data": {
+ "assignable_types": workflow_base.get_assignable_types_for(workflow.id),
"managed_permissions": workflow_base.managed_permissions,
"available_roles": list(workflow.getAvailableRoles()),
"groups": workflow_base.getGroups()
diff --git a/frontend/packages/volto-workflow-manager/src/actions/workflow.ts b/frontend/packages/volto-workflow-manager/src/actions/workflow.ts
index 4b9ee91..18f37af 100644
--- a/frontend/packages/volto-workflow-manager/src/actions/workflow.ts
+++ b/frontend/packages/volto-workflow-manager/src/actions/workflow.ts
@@ -91,7 +91,7 @@ export function assignWorkflow(workflowId: string, contentType: string) {
type: ASSIGN_WORKFLOW,
request: {
op: 'post',
- path: `/@workflows/${workflowId}/@assign`, // Fixed path
+ path: `/@workflow-assign/${workflowId}`,
data: {
type_id: contentType,
},
diff --git a/frontend/packages/volto-workflow-manager/src/components/Workflow/ActionsToolbar.tsx b/frontend/packages/volto-workflow-manager/src/components/Workflow/ActionsToolbar.tsx
index b23131e..5702ee3 100644
--- a/frontend/packages/volto-workflow-manager/src/components/Workflow/ActionsToolbar.tsx
+++ b/frontend/packages/volto-workflow-manager/src/components/Workflow/ActionsToolbar.tsx
@@ -18,6 +18,7 @@ import blank from '@plone/volto/icons/blank.svg';
import CreateState from '../States/CreateState';
import CreateTransition from '../Transitions/CreateTransition';
import WorkflowValidation from './WorkflowValidation';
+import AssignWorkflow from './AssignWorkflow';
const messages = defineMessages({
validationSuccessTitle: {
@@ -50,6 +51,10 @@ const ActionsToolbar = ({ workflowId }: { workflowId: string }) => {
const intl = useIntl();
const [isCreateStateOpen, setCreateStateOpen] = useState(false);
const [isCreateTransitionOpen, setCreateTransitionOpen] = useState(false);
+ const [isAssignDialogOpen, setAssignDialogOpen] = useState(false);
+ const workflow = useAppSelector(
+ (state) => state.workflow.workflow.currentWorkflow,
+ );
const validation = useAppSelector((state) => state.workflow.validation);
const wasLoading = usePrevious(validation.loading);
@@ -121,7 +126,7 @@ const ActionsToolbar = ({ workflowId }: { workflowId: string }) => {
)}
{validation.loading ? 'Checking...' : 'Sanity Check'}
-
setAssignDialogOpen(true)}>
-
+
Assign