@open-mercato/core 0.6.2-develop.3400.2.d948268f50 → 0.6.2-develop.3406.1.2b403f40da
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/workflow_definition/index.js +2 -0
- package/dist/generated/entities/workflow_definition/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/sales/workflows.js +180 -0
- package/dist/modules/sales/workflows.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js +172 -0
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +205 -0
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/route.js +91 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +32 -9
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/serialize.js +36 -1
- package/dist/modules/workflows/api/definitions/serialize.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +161 -52
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +37 -34
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +134 -41
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/cli.js +19 -121
- package/dist/modules/workflows/cli.js.map +2 -2
- package/dist/modules/workflows/components/formConfig.js +1 -1
- package/dist/modules/workflows/components/formConfig.js.map +2 -2
- package/dist/modules/workflows/data/entities.js +3 -0
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +2 -2
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/events.js +2 -0
- package/dist/modules/workflows/events.js.map +2 -2
- package/dist/modules/workflows/examples/guard-rules-example.json +6 -6
- package/dist/modules/workflows/examples/order-approval-guard-rules.json +2 -2
- package/dist/modules/workflows/frontend/checkout-demo/page.js +4 -4
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/dist/modules/workflows/lib/code-registry.js +40 -0
- package/dist/modules/workflows/lib/code-registry.js.map +7 -0
- package/dist/modules/workflows/lib/find-definition.js +58 -0
- package/dist/modules/workflows/lib/find-definition.js.map +7 -0
- package/dist/modules/workflows/lib/seeds.js +0 -3
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +1 -20
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +1 -19
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260414120000.js +13 -0
- package/dist/modules/workflows/migrations/Migration20260414120000.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260428102318.js +32 -0
- package/dist/modules/workflows/migrations/Migration20260428102318.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +1 -1
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +1 -1
- package/dist/modules/workflows/workflows.js +260 -0
- package/dist/modules/workflows/workflows.js.map +7 -0
- package/generated/entities/workflow_definition/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +7 -7
- package/src/modules/sales/workflows.ts +184 -0
- package/src/modules/workflows/api/definitions/[id]/customize/route.ts +197 -0
- package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +252 -0
- package/src/modules/workflows/api/definitions/[id]/route.ts +108 -4
- package/src/modules/workflows/api/definitions/route.ts +54 -10
- package/src/modules/workflows/api/definitions/serialize.ts +42 -0
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +135 -11
- package/src/modules/workflows/backend/definitions/page.tsx +45 -33
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +222 -97
- package/src/modules/workflows/cli.ts +32 -155
- package/src/modules/workflows/components/formConfig.tsx +2 -2
- package/src/modules/workflows/data/entities.ts +4 -1
- package/src/modules/workflows/data/validators.ts +4 -3
- package/src/modules/workflows/events.ts +2 -0
- package/src/modules/workflows/examples/guard-rules-example.json +6 -6
- package/src/modules/workflows/examples/order-approval-guard-rules.json +2 -2
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +4 -4
- package/src/modules/workflows/i18n/de.json +16 -0
- package/src/modules/workflows/i18n/en.json +16 -0
- package/src/modules/workflows/i18n/es.json +16 -0
- package/src/modules/workflows/i18n/pl.json +16 -0
- package/src/modules/workflows/lib/code-registry.ts +65 -0
- package/src/modules/workflows/lib/find-definition.ts +100 -0
- package/src/modules/workflows/lib/seeds.ts +4 -3
- package/src/modules/workflows/lib/start-validator.ts +2 -35
- package/src/modules/workflows/lib/workflow-executor.ts +2 -39
- package/src/modules/workflows/migrations/.snapshot-open-mercato.json +10 -0
- package/src/modules/workflows/migrations/Migration20260414120000.ts +13 -0
- package/src/modules/workflows/migrations/Migration20260428102318.ts +57 -0
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +1 -1
- package/src/modules/workflows/workflows.ts +278 -0
- package/dist/modules/workflows/examples/checkout-demo-definition.json +0 -486
- package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
- package/dist/modules/workflows/examples/simple-approval-definition.json +0 -134
- package/src/modules/workflows/examples/checkout-demo-definition.json +0 -486
- package/src/modules/workflows/examples/order-approval-definition.json +0 -257
- package/src/modules/workflows/examples/simple-approval-definition.json +0 -134
|
@@ -9,7 +9,7 @@ import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
|
9
9
|
import { Textarea } from "@open-mercato/ui/primitives/textarea";
|
|
10
10
|
import { Badge } from "@open-mercato/ui/primitives/badge";
|
|
11
11
|
import { emitSalesDocumentDataRefresh } from "@open-mercato/core/modules/sales/lib/frontend/documentDataEvents";
|
|
12
|
-
const WORKFLOW_ID = "
|
|
12
|
+
const WORKFLOW_ID = "sales.order-approval";
|
|
13
13
|
function OrderApprovalWidget({ data }) {
|
|
14
14
|
const t = useT();
|
|
15
15
|
const queryClient = useQueryClient();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/workflows/widgets/injection/order-approval/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { emitSalesDocumentDataRefresh } from '@open-mercato/core/modules/sales/lib/frontend/documentDataEvents'\n\ntype OrderRecord = {\n id: string\n orderNumber?: string\n status?: string\n statusEntryId?: string\n}\n\ntype WorkflowInstance = {\n id: string\n workflowId: string\n status: 'RUNNING' | 'PAUSED' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'WAITING_FOR_ACTIVITIES'\n currentStepId: string\n context: Record<string, any>\n}\n\ntype UserTask = {\n id: string\n taskName: string\n status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED'\n workflowInstanceId: string\n claimedBy: string | null\n}\n\ntype DictionaryEntry = {\n id: string\n value: string\n label: string\n color?: string\n icon?: string\n}\n\nconst WORKFLOW_ID = 'sales_order_approval_v1'\n\nexport default function OrderApprovalWidget({ data }: InjectionWidgetComponentProps<unknown, OrderRecord>) {\n const t = useT()\n const queryClient = useQueryClient()\n const orderId = data?.id\n\n const [decision, setDecision] = React.useState<'approve' | 'reject' | ''>('')\n const [comments, setComments] = React.useState('')\n const [error, setError] = React.useState<string | null>(null)\n\n // Track if we're waiting for workflow to process (for polling)\n const [isWaitingForProcessing, setIsWaitingForProcessing] = React.useState(false)\n\n // Fetch current order status directly (to detect when workflow updates it)\n // Use list endpoint with ID filter since single-item endpoint doesn't exist\n const { data: orderData } = useQuery({\n queryKey: ['order-status', orderId],\n queryFn: async () => {\n if (!orderId) return null\n const result = await apiCall<{ items: Array<{ id: string; status?: string | null }> }>(\n `/api/sales/orders?id=${orderId}&pageSize=1`\n )\n return result.ok && result.result?.items?.[0] ? result.result.items[0] : null\n },\n enabled: Boolean(orderId),\n staleTime: 5_000,\n // Poll when waiting for processing to detect status change\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n // Use fresh order status from query, fallback to prop data\n // The API returns status as a string value (e.g., \"approved\", \"pending_approval\")\n const currentOrderStatus = orderData?.status || data?.status\n\n // Fetch active workflow instances for this order\n const { data: instancesData, isLoading: instancesLoading } = useQuery({\n queryKey: ['workflow-instances', orderId],\n queryFn: async () => {\n if (!orderId) return { data: [] }\n const result = await apiCall<{ data: WorkflowInstance[] }>(\n `/api/workflows/instances?entityId=${orderId}&status=RUNNING,PAUSED,WAITING_FOR_ACTIVITIES`\n )\n return result.ok ? result.result : { data: [] }\n },\n enabled: Boolean(orderId),\n staleTime: 5_000,\n // Poll every 2 seconds when waiting for processing\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n const activeInstance = instancesData?.data?.find(\n (inst) => inst.workflowId === WORKFLOW_ID && !['COMPLETED', 'FAILED', 'CANCELLED'].includes(inst.status)\n )\n\n // Fetch pending user tasks for active instance\n const { data: tasksData, isLoading: tasksLoading } = useQuery({\n queryKey: ['workflow-tasks', activeInstance?.id],\n queryFn: async () => {\n if (!activeInstance?.id) return { data: [] }\n const result = await apiCall<{ data: UserTask[] }>(\n `/api/workflows/tasks?workflowInstanceId=${activeInstance.id}&status=PENDING,IN_PROGRESS`\n )\n return result.ok ? result.result : { data: [] }\n },\n enabled: Boolean(activeInstance?.id),\n staleTime: 5_000,\n // Poll every 2 seconds when waiting for processing\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n const pendingTask = tasksData?.data?.[0]\n\n // Auto-detect when workflow is processing (active instance but no pending task)\n const isProcessing = activeInstance && !pendingTask && !['COMPLETED', 'FAILED', 'CANCELLED'].includes(activeInstance.status)\n\n // Track previous status to detect changes\n const prevStatusRef = React.useRef<string | undefined>(data?.status)\n\n // Update polling state when processing state changes\n React.useEffect(() => {\n if (isProcessing) {\n setIsWaitingForProcessing(true)\n } else if (!activeInstance || activeInstance.status === 'COMPLETED') {\n setIsWaitingForProcessing(false)\n }\n }, [isProcessing, activeInstance])\n\n // When order status changes, emit refresh event to update the page\n React.useEffect(() => {\n const newStatus = orderData?.status\n const oldStatus = prevStatusRef.current\n\n if (newStatus && oldStatus && newStatus !== oldStatus) {\n // Status changed - emit document refresh event to reload the page data\n if (orderId) {\n emitSalesDocumentDataRefresh({ documentId: orderId, kind: 'order' })\n }\n }\n\n prevStatusRef.current = newStatus || oldStatus\n }, [orderData?.status, orderId])\n\n // First fetch dictionaries to find the order_status dictionary ID\n const { data: dictionariesData } = useQuery({\n queryKey: ['dictionaries'],\n queryFn: async () => {\n const result = await apiCall<{ items: Array<{ id: string; key: string }> }>(\n '/api/dictionaries'\n )\n return result.ok ? result.result : { items: [] }\n },\n staleTime: 60_000,\n })\n\n const orderStatusDictionaryId = React.useMemo(() => {\n const dict = dictionariesData?.items?.find(d => d.key === 'sales.order_status')\n return dict?.id\n }, [dictionariesData])\n\n // Fetch order status dictionary entries using the dictionary ID\n const { data: statusEntriesData } = useQuery({\n queryKey: ['dictionary-entries', orderStatusDictionaryId],\n queryFn: async () => {\n if (!orderStatusDictionaryId) return { items: [] }\n const result = await apiCall<{ items: DictionaryEntry[] }>(\n `/api/dictionaries/${orderStatusDictionaryId}/entries`\n )\n return result.ok ? result.result : { items: [] }\n },\n enabled: Boolean(orderStatusDictionaryId),\n staleTime: 60_000,\n })\n\n const findStatusId = React.useCallback((code: string) => {\n const entries = statusEntriesData?.items || []\n // The API returns 'value' as the code/key for dictionary entries\n const entry = entries.find(\n (e) => e.value === code || e.label?.toLowerCase() === code.toLowerCase()\n )\n return entry?.id\n }, [statusEntriesData])\n\n // Start workflow mutation\n const startWorkflowMutation = useMutation({\n mutationFn: async () => {\n const pendingApprovalStatusId = findStatusId('pending_approval')\n const approvedStatusId = findStatusId('approved')\n const rejectedStatusId = findStatusId('rejected')\n\n if (!pendingApprovalStatusId || !approvedStatusId || !rejectedStatusId) {\n throw new Error(t('workflows.orderApproval.missingStatuses', 'Missing order status entries. Please ensure pending_approval, approved, and rejected statuses exist in the sales.order_status dictionary.'))\n }\n\n const result = await apiCall<{ data: WorkflowInstance }>('/api/workflows/instances', {\n method: 'POST',\n body: JSON.stringify({\n workflowId: WORKFLOW_ID,\n initialContext: {\n orderId,\n pendingApprovalStatusId,\n approvedStatusId,\n rejectedStatusId,\n },\n metadata: {\n entityType: 'SalesOrder',\n entityId: orderId,\n },\n }),\n })\n\n if (!result.ok) {\n const errorResult = result.result as { error?: string } | null\n throw new Error(errorResult?.error || t('workflows.orderApproval.startError', 'Failed to start approval workflow'))\n }\n\n return result.result\n },\n onSuccess: () => {\n setError(null)\n queryClient.invalidateQueries({ queryKey: ['workflow-instances', orderId] })\n },\n onError: (err: Error) => {\n setError(err.message)\n },\n })\n\n // Complete task mutation\n const completeTaskMutation = useMutation({\n mutationFn: async ({ taskId, formData }: { taskId: string; formData: { decision: string; comments?: string } }) => {\n const result = await apiCall(`/api/workflows/tasks/${taskId}/complete`, {\n method: 'POST',\n body: JSON.stringify({ formData }),\n })\n\n if (!result.ok) {\n const errorResult = result.result as { error?: string } | null\n throw new Error(errorResult?.error || t('workflows.orderApproval.completeError', 'Failed to complete approval task'))\n }\n\n return result.result\n },\n onSuccess: () => {\n setError(null)\n setDecision('')\n setComments('')\n // Start polling immediately after submitting decision\n setIsWaitingForProcessing(true)\n queryClient.invalidateQueries({ queryKey: ['workflow-instances', orderId] })\n queryClient.invalidateQueries({ queryKey: ['workflow-tasks', activeInstance?.id] })\n // Also refresh the order data\n queryClient.invalidateQueries({ queryKey: ['sales-order', orderId] })\n },\n onError: (err: Error) => {\n setError(err.message)\n },\n })\n\n const handleStartWorkflow = () => {\n // Start polling after starting workflow\n setIsWaitingForProcessing(true)\n startWorkflowMutation.mutate()\n }\n\n const handleCompleteTask = () => {\n if (!pendingTask || !decision) return\n completeTaskMutation.mutate({\n taskId: pendingTask.id,\n formData: { decision, comments },\n })\n }\n\n // Handle keyboard shortcuts (Cmd/Ctrl+Enter to submit)\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (pendingTask && decision && !isSubmitting) {\n handleCompleteTask()\n }\n }\n }\n\n const isLoading = instancesLoading || tasksLoading\n const isSubmitting = startWorkflowMutation.isPending || completeTaskMutation.isPending\n\n // Don't render if no orderId\n if (!orderId) return null\n\n // Only show widget when order status is pending_approval\n const orderStatus = currentOrderStatus?.toLowerCase()\n if (orderStatus !== 'pending_approval') {\n return null\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center p-4\">\n <Spinner size=\"sm\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</span>\n </div>\n )\n }\n\n // Show workflow status badge\n const getStatusBadge = () => {\n if (!activeInstance) return null\n\n const statusVariants: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n RUNNING: 'default',\n PAUSED: 'secondary',\n WAITING_FOR_ACTIVITIES: 'secondary',\n COMPLETED: 'default',\n FAILED: 'destructive',\n CANCELLED: 'outline',\n }\n\n return (\n <Badge variant={statusVariants[activeInstance.status] || 'outline'}>\n {t(`workflows.instances.statuses.${activeInstance.status}`, activeInstance.status)}\n </Badge>\n )\n }\n\n return (\n <div className=\"space-y-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <div className=\"text-sm font-semibold text-foreground\">\n {t('workflows.orderApproval.groupLabel', 'Order Approval')}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.orderApproval.groupDescription', 'Review and approve or reject this order')}\n </p>\n </div>\n {getStatusBadge()}\n </div>\n\n {error && (\n <div className=\"rounded-md border border-red-200 bg-red-50 p-3 text-xs text-red-800\">\n {error}\n </div>\n )}\n\n {/* No active workflow - show request approval button */}\n {!activeInstance && (\n <div className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {t('workflows.orderApproval.noWorkflowActive', 'No approval workflow is active for this order.')}\n </p>\n <Button\n onClick={handleStartWorkflow}\n disabled={isSubmitting}\n variant=\"default\"\n size=\"sm\"\n >\n {isSubmitting && <Spinner size=\"sm\" className=\"mr-2\" />}\n {t('workflows.orderApproval.requestApproval', 'Request Approval')}\n </Button>\n </div>\n )}\n\n {/* Active workflow with pending task - show approve/reject UI */}\n {activeInstance && pendingTask && (\n <div className=\"space-y-3\" onKeyDown={handleKeyDown}>\n <div className=\"rounded-md border border-amber-200 bg-amber-50 p-3\">\n <p className=\"text-sm font-medium text-amber-800\">\n {t('workflows.orderApproval.pendingTitle', 'Pending Approval')}\n </p>\n <p className=\"text-xs text-amber-700 mt-1\">\n {t('workflows.orderApproval.pendingDescription', 'This order requires approval before processing.')}\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('workflows.orderApproval.decisionLabel', 'Decision')}\n </label>\n <div className=\"flex gap-2\">\n <Button\n variant={decision === 'approve' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setDecision('approve')}\n className={decision === 'approve' ? 'bg-emerald-600 hover:bg-emerald-700' : ''}\n >\n {t('workflows.orderApproval.approveButton', 'Approve')}\n </Button>\n <Button\n variant={decision === 'reject' ? 'destructive' : 'outline'}\n size=\"sm\"\n onClick={() => setDecision('reject')}\n >\n {t('workflows.orderApproval.rejectButton', 'Reject')}\n </Button>\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('workflows.orderApproval.commentsLabel', 'Comments')} <span className=\"text-muted-foreground font-normal\">({t('common.optional', 'optional')})</span>\n </label>\n <Textarea\n value={comments}\n onChange={(e) => setComments(e.target.value)}\n placeholder={t('workflows.orderApproval.commentsPlaceholder', 'Add optional comments...')}\n rows={2}\n />\n </div>\n\n <Button\n onClick={handleCompleteTask}\n disabled={!decision || isSubmitting}\n variant=\"default\"\n size=\"sm\"\n className=\"w-full\"\n >\n {isSubmitting && <Spinner size=\"sm\" className=\"mr-2\" />}\n {t('workflows.orderApproval.submitDecision', 'Submit Decision')}\n </Button>\n </div>\n )}\n\n {/* Active workflow but no pending task (processing) */}\n {activeInstance && !pendingTask && activeInstance.status !== 'COMPLETED' && (\n <div className=\"rounded-md border border-blue-200 bg-blue-50 p-3\">\n <p className=\"text-sm text-blue-800\">\n {t('workflows.orderApproval.processing', 'Workflow is processing...')}\n </p>\n </div>\n )}\n\n {/* Completed workflow */}\n {activeInstance && activeInstance.status === 'COMPLETED' && (\n <div className=\"rounded-md border border-emerald-200 bg-emerald-50 p-3\">\n <p className=\"text-sm text-emerald-800\">\n {t('workflows.orderApproval.completed', 'Approval workflow completed.')}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { emitSalesDocumentDataRefresh } from '@open-mercato/core/modules/sales/lib/frontend/documentDataEvents'\n\ntype OrderRecord = {\n id: string\n orderNumber?: string\n status?: string\n statusEntryId?: string\n}\n\ntype WorkflowInstance = {\n id: string\n workflowId: string\n status: 'RUNNING' | 'PAUSED' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'WAITING_FOR_ACTIVITIES'\n currentStepId: string\n context: Record<string, any>\n}\n\ntype UserTask = {\n id: string\n taskName: string\n status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED'\n workflowInstanceId: string\n claimedBy: string | null\n}\n\ntype DictionaryEntry = {\n id: string\n value: string\n label: string\n color?: string\n icon?: string\n}\n\nconst WORKFLOW_ID = 'sales.order-approval'\n\nexport default function OrderApprovalWidget({ data }: InjectionWidgetComponentProps<unknown, OrderRecord>) {\n const t = useT()\n const queryClient = useQueryClient()\n const orderId = data?.id\n\n const [decision, setDecision] = React.useState<'approve' | 'reject' | ''>('')\n const [comments, setComments] = React.useState('')\n const [error, setError] = React.useState<string | null>(null)\n\n // Track if we're waiting for workflow to process (for polling)\n const [isWaitingForProcessing, setIsWaitingForProcessing] = React.useState(false)\n\n // Fetch current order status directly (to detect when workflow updates it)\n // Use list endpoint with ID filter since single-item endpoint doesn't exist\n const { data: orderData } = useQuery({\n queryKey: ['order-status', orderId],\n queryFn: async () => {\n if (!orderId) return null\n const result = await apiCall<{ items: Array<{ id: string; status?: string | null }> }>(\n `/api/sales/orders?id=${orderId}&pageSize=1`\n )\n return result.ok && result.result?.items?.[0] ? result.result.items[0] : null\n },\n enabled: Boolean(orderId),\n staleTime: 5_000,\n // Poll when waiting for processing to detect status change\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n // Use fresh order status from query, fallback to prop data\n // The API returns status as a string value (e.g., \"approved\", \"pending_approval\")\n const currentOrderStatus = orderData?.status || data?.status\n\n // Fetch active workflow instances for this order\n const { data: instancesData, isLoading: instancesLoading } = useQuery({\n queryKey: ['workflow-instances', orderId],\n queryFn: async () => {\n if (!orderId) return { data: [] }\n const result = await apiCall<{ data: WorkflowInstance[] }>(\n `/api/workflows/instances?entityId=${orderId}&status=RUNNING,PAUSED,WAITING_FOR_ACTIVITIES`\n )\n return result.ok ? result.result : { data: [] }\n },\n enabled: Boolean(orderId),\n staleTime: 5_000,\n // Poll every 2 seconds when waiting for processing\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n const activeInstance = instancesData?.data?.find(\n (inst) => inst.workflowId === WORKFLOW_ID && !['COMPLETED', 'FAILED', 'CANCELLED'].includes(inst.status)\n )\n\n // Fetch pending user tasks for active instance\n const { data: tasksData, isLoading: tasksLoading } = useQuery({\n queryKey: ['workflow-tasks', activeInstance?.id],\n queryFn: async () => {\n if (!activeInstance?.id) return { data: [] }\n const result = await apiCall<{ data: UserTask[] }>(\n `/api/workflows/tasks?workflowInstanceId=${activeInstance.id}&status=PENDING,IN_PROGRESS`\n )\n return result.ok ? result.result : { data: [] }\n },\n enabled: Boolean(activeInstance?.id),\n staleTime: 5_000,\n // Poll every 2 seconds when waiting for processing\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n const pendingTask = tasksData?.data?.[0]\n\n // Auto-detect when workflow is processing (active instance but no pending task)\n const isProcessing = activeInstance && !pendingTask && !['COMPLETED', 'FAILED', 'CANCELLED'].includes(activeInstance.status)\n\n // Track previous status to detect changes\n const prevStatusRef = React.useRef<string | undefined>(data?.status)\n\n // Update polling state when processing state changes\n React.useEffect(() => {\n if (isProcessing) {\n setIsWaitingForProcessing(true)\n } else if (!activeInstance || activeInstance.status === 'COMPLETED') {\n setIsWaitingForProcessing(false)\n }\n }, [isProcessing, activeInstance])\n\n // When order status changes, emit refresh event to update the page\n React.useEffect(() => {\n const newStatus = orderData?.status\n const oldStatus = prevStatusRef.current\n\n if (newStatus && oldStatus && newStatus !== oldStatus) {\n // Status changed - emit document refresh event to reload the page data\n if (orderId) {\n emitSalesDocumentDataRefresh({ documentId: orderId, kind: 'order' })\n }\n }\n\n prevStatusRef.current = newStatus || oldStatus\n }, [orderData?.status, orderId])\n\n // First fetch dictionaries to find the order_status dictionary ID\n const { data: dictionariesData } = useQuery({\n queryKey: ['dictionaries'],\n queryFn: async () => {\n const result = await apiCall<{ items: Array<{ id: string; key: string }> }>(\n '/api/dictionaries'\n )\n return result.ok ? result.result : { items: [] }\n },\n staleTime: 60_000,\n })\n\n const orderStatusDictionaryId = React.useMemo(() => {\n const dict = dictionariesData?.items?.find(d => d.key === 'sales.order_status')\n return dict?.id\n }, [dictionariesData])\n\n // Fetch order status dictionary entries using the dictionary ID\n const { data: statusEntriesData } = useQuery({\n queryKey: ['dictionary-entries', orderStatusDictionaryId],\n queryFn: async () => {\n if (!orderStatusDictionaryId) return { items: [] }\n const result = await apiCall<{ items: DictionaryEntry[] }>(\n `/api/dictionaries/${orderStatusDictionaryId}/entries`\n )\n return result.ok ? result.result : { items: [] }\n },\n enabled: Boolean(orderStatusDictionaryId),\n staleTime: 60_000,\n })\n\n const findStatusId = React.useCallback((code: string) => {\n const entries = statusEntriesData?.items || []\n // The API returns 'value' as the code/key for dictionary entries\n const entry = entries.find(\n (e) => e.value === code || e.label?.toLowerCase() === code.toLowerCase()\n )\n return entry?.id\n }, [statusEntriesData])\n\n // Start workflow mutation\n const startWorkflowMutation = useMutation({\n mutationFn: async () => {\n const pendingApprovalStatusId = findStatusId('pending_approval')\n const approvedStatusId = findStatusId('approved')\n const rejectedStatusId = findStatusId('rejected')\n\n if (!pendingApprovalStatusId || !approvedStatusId || !rejectedStatusId) {\n throw new Error(t('workflows.orderApproval.missingStatuses', 'Missing order status entries. Please ensure pending_approval, approved, and rejected statuses exist in the sales.order_status dictionary.'))\n }\n\n const result = await apiCall<{ data: WorkflowInstance }>('/api/workflows/instances', {\n method: 'POST',\n body: JSON.stringify({\n workflowId: WORKFLOW_ID,\n initialContext: {\n orderId,\n pendingApprovalStatusId,\n approvedStatusId,\n rejectedStatusId,\n },\n metadata: {\n entityType: 'SalesOrder',\n entityId: orderId,\n },\n }),\n })\n\n if (!result.ok) {\n const errorResult = result.result as { error?: string } | null\n throw new Error(errorResult?.error || t('workflows.orderApproval.startError', 'Failed to start approval workflow'))\n }\n\n return result.result\n },\n onSuccess: () => {\n setError(null)\n queryClient.invalidateQueries({ queryKey: ['workflow-instances', orderId] })\n },\n onError: (err: Error) => {\n setError(err.message)\n },\n })\n\n // Complete task mutation\n const completeTaskMutation = useMutation({\n mutationFn: async ({ taskId, formData }: { taskId: string; formData: { decision: string; comments?: string } }) => {\n const result = await apiCall(`/api/workflows/tasks/${taskId}/complete`, {\n method: 'POST',\n body: JSON.stringify({ formData }),\n })\n\n if (!result.ok) {\n const errorResult = result.result as { error?: string } | null\n throw new Error(errorResult?.error || t('workflows.orderApproval.completeError', 'Failed to complete approval task'))\n }\n\n return result.result\n },\n onSuccess: () => {\n setError(null)\n setDecision('')\n setComments('')\n // Start polling immediately after submitting decision\n setIsWaitingForProcessing(true)\n queryClient.invalidateQueries({ queryKey: ['workflow-instances', orderId] })\n queryClient.invalidateQueries({ queryKey: ['workflow-tasks', activeInstance?.id] })\n // Also refresh the order data\n queryClient.invalidateQueries({ queryKey: ['sales-order', orderId] })\n },\n onError: (err: Error) => {\n setError(err.message)\n },\n })\n\n const handleStartWorkflow = () => {\n // Start polling after starting workflow\n setIsWaitingForProcessing(true)\n startWorkflowMutation.mutate()\n }\n\n const handleCompleteTask = () => {\n if (!pendingTask || !decision) return\n completeTaskMutation.mutate({\n taskId: pendingTask.id,\n formData: { decision, comments },\n })\n }\n\n // Handle keyboard shortcuts (Cmd/Ctrl+Enter to submit)\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (pendingTask && decision && !isSubmitting) {\n handleCompleteTask()\n }\n }\n }\n\n const isLoading = instancesLoading || tasksLoading\n const isSubmitting = startWorkflowMutation.isPending || completeTaskMutation.isPending\n\n // Don't render if no orderId\n if (!orderId) return null\n\n // Only show widget when order status is pending_approval\n const orderStatus = currentOrderStatus?.toLowerCase()\n if (orderStatus !== 'pending_approval') {\n return null\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center p-4\">\n <Spinner size=\"sm\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</span>\n </div>\n )\n }\n\n // Show workflow status badge\n const getStatusBadge = () => {\n if (!activeInstance) return null\n\n const statusVariants: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n RUNNING: 'default',\n PAUSED: 'secondary',\n WAITING_FOR_ACTIVITIES: 'secondary',\n COMPLETED: 'default',\n FAILED: 'destructive',\n CANCELLED: 'outline',\n }\n\n return (\n <Badge variant={statusVariants[activeInstance.status] || 'outline'}>\n {t(`workflows.instances.statuses.${activeInstance.status}`, activeInstance.status)}\n </Badge>\n )\n }\n\n return (\n <div className=\"space-y-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <div className=\"text-sm font-semibold text-foreground\">\n {t('workflows.orderApproval.groupLabel', 'Order Approval')}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.orderApproval.groupDescription', 'Review and approve or reject this order')}\n </p>\n </div>\n {getStatusBadge()}\n </div>\n\n {error && (\n <div className=\"rounded-md border border-red-200 bg-red-50 p-3 text-xs text-red-800\">\n {error}\n </div>\n )}\n\n {/* No active workflow - show request approval button */}\n {!activeInstance && (\n <div className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {t('workflows.orderApproval.noWorkflowActive', 'No approval workflow is active for this order.')}\n </p>\n <Button\n onClick={handleStartWorkflow}\n disabled={isSubmitting}\n variant=\"default\"\n size=\"sm\"\n >\n {isSubmitting && <Spinner size=\"sm\" className=\"mr-2\" />}\n {t('workflows.orderApproval.requestApproval', 'Request Approval')}\n </Button>\n </div>\n )}\n\n {/* Active workflow with pending task - show approve/reject UI */}\n {activeInstance && pendingTask && (\n <div className=\"space-y-3\" onKeyDown={handleKeyDown}>\n <div className=\"rounded-md border border-amber-200 bg-amber-50 p-3\">\n <p className=\"text-sm font-medium text-amber-800\">\n {t('workflows.orderApproval.pendingTitle', 'Pending Approval')}\n </p>\n <p className=\"text-xs text-amber-700 mt-1\">\n {t('workflows.orderApproval.pendingDescription', 'This order requires approval before processing.')}\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('workflows.orderApproval.decisionLabel', 'Decision')}\n </label>\n <div className=\"flex gap-2\">\n <Button\n variant={decision === 'approve' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setDecision('approve')}\n className={decision === 'approve' ? 'bg-emerald-600 hover:bg-emerald-700' : ''}\n >\n {t('workflows.orderApproval.approveButton', 'Approve')}\n </Button>\n <Button\n variant={decision === 'reject' ? 'destructive' : 'outline'}\n size=\"sm\"\n onClick={() => setDecision('reject')}\n >\n {t('workflows.orderApproval.rejectButton', 'Reject')}\n </Button>\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('workflows.orderApproval.commentsLabel', 'Comments')} <span className=\"text-muted-foreground font-normal\">({t('common.optional', 'optional')})</span>\n </label>\n <Textarea\n value={comments}\n onChange={(e) => setComments(e.target.value)}\n placeholder={t('workflows.orderApproval.commentsPlaceholder', 'Add optional comments...')}\n rows={2}\n />\n </div>\n\n <Button\n onClick={handleCompleteTask}\n disabled={!decision || isSubmitting}\n variant=\"default\"\n size=\"sm\"\n className=\"w-full\"\n >\n {isSubmitting && <Spinner size=\"sm\" className=\"mr-2\" />}\n {t('workflows.orderApproval.submitDecision', 'Submit Decision')}\n </Button>\n </div>\n )}\n\n {/* Active workflow but no pending task (processing) */}\n {activeInstance && !pendingTask && activeInstance.status !== 'COMPLETED' && (\n <div className=\"rounded-md border border-blue-200 bg-blue-50 p-3\">\n <p className=\"text-sm text-blue-800\">\n {t('workflows.orderApproval.processing', 'Workflow is processing...')}\n </p>\n </div>\n )}\n\n {/* Completed workflow */}\n {activeInstance && activeInstance.status === 'COMPLETED' && (\n <div className=\"rounded-md border border-emerald-200 bg-emerald-50 p-3\">\n <p className=\"text-sm text-emerald-800\">\n {t('workflows.orderApproval.completed', 'Approval workflow completed.')}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { defineWorkflow, createWorkflowsModuleConfig } from "@open-mercato/shared/modules/workflows";
|
|
2
|
+
const simpleApproval = defineWorkflow({
|
|
3
|
+
workflowId: "workflows.simple-approval",
|
|
4
|
+
workflowName: "Simple Approval Workflow",
|
|
5
|
+
description: "Basic three-step approval workflow: Start \u2192 Approval \u2192 End",
|
|
6
|
+
metadata: { category: "Testing", tags: ["test", "approval", "simple"], icon: "check-circle" },
|
|
7
|
+
steps: [
|
|
8
|
+
{ stepId: "start", stepName: "Start", stepType: "START", description: "Initialize workflow" },
|
|
9
|
+
{
|
|
10
|
+
stepId: "approval",
|
|
11
|
+
stepName: "Pending Approval",
|
|
12
|
+
stepType: "USER_TASK",
|
|
13
|
+
description: "Approve or reject request",
|
|
14
|
+
userTaskConfig: { assignedTo: "approver", slaDuration: "PT1H" }
|
|
15
|
+
},
|
|
16
|
+
{ stepId: "end", stepName: "Complete", stepType: "END", description: "Workflow complete" }
|
|
17
|
+
],
|
|
18
|
+
transitions: [
|
|
19
|
+
{
|
|
20
|
+
transitionId: "start_to_approval",
|
|
21
|
+
transitionName: "Submit for Approval",
|
|
22
|
+
fromStepId: "start",
|
|
23
|
+
toStepId: "approval",
|
|
24
|
+
trigger: "auto",
|
|
25
|
+
priority: 100,
|
|
26
|
+
activities: [
|
|
27
|
+
{
|
|
28
|
+
activityId: "notify_approver",
|
|
29
|
+
activityName: "Notify Approver",
|
|
30
|
+
activityType: "SEND_EMAIL",
|
|
31
|
+
config: {
|
|
32
|
+
to: "{{context.approver.email}}",
|
|
33
|
+
subject: "Approval Required: {{context.request.title}}",
|
|
34
|
+
template: "approval_request",
|
|
35
|
+
data: {
|
|
36
|
+
requestId: "{{context.request.id}}",
|
|
37
|
+
title: "{{context.request.title}}",
|
|
38
|
+
requester: "{{context.requester.name}}"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
timeout: "PT10S",
|
|
42
|
+
async: true,
|
|
43
|
+
retryPolicy: { maxAttempts: 3, initialIntervalMs: 2e3, backoffCoefficient: 2, maxIntervalMs: 2e4 }
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
activityId: "emit_approval_requested",
|
|
47
|
+
activityName: "Emit Approval Requested Event",
|
|
48
|
+
activityType: "EMIT_EVENT",
|
|
49
|
+
config: {
|
|
50
|
+
eventType: "approval.requested",
|
|
51
|
+
payload: { requestId: "{{context.request.id}}", workflowInstanceId: "{{workflow.instanceId}}" }
|
|
52
|
+
},
|
|
53
|
+
async: true
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
transitionId: "approval_to_end",
|
|
59
|
+
transitionName: "Approval Decision",
|
|
60
|
+
fromStepId: "approval",
|
|
61
|
+
toStepId: "end",
|
|
62
|
+
trigger: "manual",
|
|
63
|
+
priority: 100,
|
|
64
|
+
activities: [
|
|
65
|
+
{
|
|
66
|
+
activityId: "notify_requester",
|
|
67
|
+
activityName: "Notify Requester of Decision",
|
|
68
|
+
activityType: "SEND_EMAIL",
|
|
69
|
+
config: {
|
|
70
|
+
to: "{{context.requester.email}}",
|
|
71
|
+
subject: "Approval Decision: {{context.request.title}}",
|
|
72
|
+
template: "approval_decision",
|
|
73
|
+
data: {
|
|
74
|
+
requestId: "{{context.request.id}}",
|
|
75
|
+
title: "{{context.request.title}}",
|
|
76
|
+
decision: "{{task.result.approved}}",
|
|
77
|
+
comments: "{{task.result.comments}}",
|
|
78
|
+
approver: "{{task.completedBy}}"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
timeout: "PT10S",
|
|
82
|
+
async: true
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
activityId: "emit_approval_completed",
|
|
86
|
+
activityName: "Emit Approval Completed Event",
|
|
87
|
+
activityType: "EMIT_EVENT",
|
|
88
|
+
config: {
|
|
89
|
+
eventType: "approval.completed",
|
|
90
|
+
payload: {
|
|
91
|
+
requestId: "{{context.request.id}}",
|
|
92
|
+
approved: "{{task.result.approved}}",
|
|
93
|
+
approvedBy: "{{task.completedBy}}",
|
|
94
|
+
timestamp: "{{now}}"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async: true
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
const checkoutDemo = defineWorkflow({
|
|
104
|
+
workflowId: "workflows.checkout-demo",
|
|
105
|
+
workflowName: "Checkout with Payment Webhook",
|
|
106
|
+
description: "Realistic checkout workflow with signal-based payment confirmation",
|
|
107
|
+
metadata: { category: "E-commerce", tags: ["checkout", "demo", "payments", "signals"], icon: "ShoppingCart" },
|
|
108
|
+
steps: [
|
|
109
|
+
{
|
|
110
|
+
stepId: "start",
|
|
111
|
+
stepName: "Start",
|
|
112
|
+
stepType: "START",
|
|
113
|
+
description: "Checkout process initiated",
|
|
114
|
+
preConditions: [{
|
|
115
|
+
ruleId: "workflow_checkout_cart_not_empty",
|
|
116
|
+
required: true,
|
|
117
|
+
validationMessage: {
|
|
118
|
+
en: "Your shopping cart is empty. Please add items before starting checkout.",
|
|
119
|
+
pl: "Tw\xF3j koszyk jest pusty. Dodaj produkty przed rozpocz\u0119ciem zakupu."
|
|
120
|
+
}
|
|
121
|
+
}]
|
|
122
|
+
},
|
|
123
|
+
{ stepId: "cart_validation", stepName: "Cart Validation", stepType: "AUTOMATED", description: "Validate cart has items and inventory is available", timeout: "PT10S" },
|
|
124
|
+
{
|
|
125
|
+
stepId: "customer_info",
|
|
126
|
+
stepName: "Customer Information",
|
|
127
|
+
stepType: "USER_TASK",
|
|
128
|
+
description: "Collect customer shipping and contact information",
|
|
129
|
+
userTaskConfig: {
|
|
130
|
+
formSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
fullName: { type: "string", title: "Full Name", description: "Customer's full legal name" },
|
|
134
|
+
email: { type: "string", format: "email", title: "Email Address", description: "Email for order confirmation" },
|
|
135
|
+
phone: { type: "string", title: "Phone Number", description: "Contact number for delivery" },
|
|
136
|
+
comment: { type: "string", title: "Order Comment", maxLength: 500, description: "Special instructions or notes" }
|
|
137
|
+
},
|
|
138
|
+
required: ["fullName", "email"]
|
|
139
|
+
},
|
|
140
|
+
slaDuration: "PT24H"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{ stepId: "payment_initiation", stepName: "Initiate Payment", stepType: "AUTOMATED", description: "Send payment request to payment provider", timeout: "PT10S" },
|
|
144
|
+
{
|
|
145
|
+
stepId: "wait_payment_confirmation",
|
|
146
|
+
stepName: "Wait for Payment Confirmation",
|
|
147
|
+
stepType: "WAIT_FOR_SIGNAL",
|
|
148
|
+
description: "Waiting for payment provider webhook confirmation",
|
|
149
|
+
signalConfig: { signalName: "payment_confirmed", timeout: "PT5M" }
|
|
150
|
+
},
|
|
151
|
+
{ stepId: "order_confirmation", stepName: "Order Confirmation", stepType: "AUTOMATED", description: "Create order record and send confirmation", timeout: "PT15S" },
|
|
152
|
+
{ stepId: "end", stepName: "Complete", stepType: "END", description: "Checkout completed successfully" }
|
|
153
|
+
],
|
|
154
|
+
transitions: [
|
|
155
|
+
{
|
|
156
|
+
transitionId: "start_to_cart",
|
|
157
|
+
transitionName: "Initialize Checkout",
|
|
158
|
+
fromStepId: "start",
|
|
159
|
+
toStepId: "cart_validation",
|
|
160
|
+
trigger: "auto",
|
|
161
|
+
priority: 100,
|
|
162
|
+
activities: [{
|
|
163
|
+
activityId: "log_checkout_start",
|
|
164
|
+
activityName: "Log Checkout Start",
|
|
165
|
+
activityType: "EMIT_EVENT",
|
|
166
|
+
config: {
|
|
167
|
+
eventName: "checkout.started",
|
|
168
|
+
payload: { customerId: "{{context.customerId}}", cartId: "{{context.cartId}}", currency: "{{context.currency}}", timestamp: "{{now}}" }
|
|
169
|
+
},
|
|
170
|
+
async: false
|
|
171
|
+
}]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
transitionId: "cart_to_customer_info",
|
|
175
|
+
transitionName: "Collect Customer Information",
|
|
176
|
+
fromStepId: "cart_validation",
|
|
177
|
+
toStepId: "customer_info",
|
|
178
|
+
trigger: "auto",
|
|
179
|
+
priority: 100,
|
|
180
|
+
preConditions: [{ ruleId: "workflow_checkout_cart_not_empty", required: true }]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
transitionId: "customer_info_to_payment",
|
|
184
|
+
transitionName: "Initiate Payment",
|
|
185
|
+
fromStepId: "customer_info",
|
|
186
|
+
toStepId: "payment_initiation",
|
|
187
|
+
trigger: "auto",
|
|
188
|
+
priority: 100
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
transitionId: "payment_to_wait_confirmation",
|
|
192
|
+
transitionName: "Wait for Confirmation",
|
|
193
|
+
fromStepId: "payment_initiation",
|
|
194
|
+
toStepId: "wait_payment_confirmation",
|
|
195
|
+
trigger: "auto",
|
|
196
|
+
priority: 100
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
transitionId: "confirmation_to_order",
|
|
200
|
+
transitionName: "Payment Confirmed",
|
|
201
|
+
fromStepId: "wait_payment_confirmation",
|
|
202
|
+
toStepId: "order_confirmation",
|
|
203
|
+
trigger: "auto",
|
|
204
|
+
priority: 100
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
transitionId: "confirmation_to_end",
|
|
208
|
+
transitionName: "Finalize Order",
|
|
209
|
+
fromStepId: "order_confirmation",
|
|
210
|
+
toStepId: "end",
|
|
211
|
+
trigger: "auto",
|
|
212
|
+
priority: 100,
|
|
213
|
+
activities: [
|
|
214
|
+
{
|
|
215
|
+
activityId: "create_order",
|
|
216
|
+
activityName: "Create Order Record",
|
|
217
|
+
activityType: "CALL_API",
|
|
218
|
+
config: {
|
|
219
|
+
endpoint: "/api/sales/orders",
|
|
220
|
+
method: "POST",
|
|
221
|
+
body: {
|
|
222
|
+
customerEntityId: "{{context.customer.id}}",
|
|
223
|
+
currencyCode: "{{context.cart.currency}}",
|
|
224
|
+
placedAt: "{{now}}",
|
|
225
|
+
grandTotalGrossAmount: "{{context.cart.total}}"
|
|
226
|
+
},
|
|
227
|
+
validateTenantMatch: true
|
|
228
|
+
},
|
|
229
|
+
timeout: "PT10S",
|
|
230
|
+
async: false,
|
|
231
|
+
retryPolicy: { maxAttempts: 3, initialIntervalMs: 2e3, backoffCoefficient: 2, maxIntervalMs: 1e4 }
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
activityId: "send_confirmation_email",
|
|
235
|
+
activityName: "Send Order Confirmation Email",
|
|
236
|
+
activityType: "SEND_EMAIL",
|
|
237
|
+
config: {
|
|
238
|
+
to: "{{context.customer.email}}",
|
|
239
|
+
subject: "Order Confirmation",
|
|
240
|
+
template: "order_confirmation",
|
|
241
|
+
data: { customerName: "{{context.customer.name}}", total: "{{context.cart.total}}" }
|
|
242
|
+
},
|
|
243
|
+
timeout: "PT15S",
|
|
244
|
+
async: true,
|
|
245
|
+
retryPolicy: { maxAttempts: 3, initialIntervalMs: 2e3, backoffCoefficient: 2, maxIntervalMs: 2e4 }
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
});
|
|
251
|
+
const workflowsConfig = createWorkflowsModuleConfig({
|
|
252
|
+
moduleId: "workflows",
|
|
253
|
+
workflows: [simpleApproval, checkoutDemo]
|
|
254
|
+
});
|
|
255
|
+
var workflows_default = workflowsConfig;
|
|
256
|
+
export {
|
|
257
|
+
workflows_default as default,
|
|
258
|
+
workflowsConfig
|
|
259
|
+
};
|
|
260
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/workflows/workflows.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module \u2014 Code-Based Workflow Definitions\n *\n * Demo workflows shipped with the workflows module.\n * These are auto-discovered by the generator and loaded into the in-memory registry.\n */\n\nimport { defineWorkflow, createWorkflowsModuleConfig } from '@open-mercato/shared/modules/workflows'\n\n// ============================================================================\n// Simple Approval Workflow\n// ============================================================================\n\nconst simpleApproval = defineWorkflow({\n workflowId: 'workflows.simple-approval',\n workflowName: 'Simple Approval Workflow',\n description: 'Basic three-step approval workflow: Start \u2192 Approval \u2192 End',\n metadata: { category: 'Testing', tags: ['test', 'approval', 'simple'], icon: 'check-circle' },\n steps: [\n { stepId: 'start', stepName: 'Start', stepType: 'START', description: 'Initialize workflow' },\n {\n stepId: 'approval',\n stepName: 'Pending Approval',\n stepType: 'USER_TASK',\n description: 'Approve or reject request',\n userTaskConfig: { assignedTo: 'approver', slaDuration: 'PT1H' },\n },\n { stepId: 'end', stepName: 'Complete', stepType: 'END', description: 'Workflow complete' },\n ] as const,\n transitions: [\n {\n transitionId: 'start_to_approval',\n transitionName: 'Submit for Approval',\n fromStepId: 'start',\n toStepId: 'approval',\n trigger: 'auto',\n priority: 100,\n activities: [\n {\n activityId: 'notify_approver',\n activityName: 'Notify Approver',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.approver.email}}',\n subject: 'Approval Required: {{context.request.title}}',\n template: 'approval_request',\n data: {\n requestId: '{{context.request.id}}',\n title: '{{context.request.title}}',\n requester: '{{context.requester.name}}',\n },\n },\n timeout: 'PT10S',\n async: true,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 20000 },\n },\n {\n activityId: 'emit_approval_requested',\n activityName: 'Emit Approval Requested Event',\n activityType: 'EMIT_EVENT',\n config: {\n eventType: 'approval.requested',\n payload: { requestId: '{{context.request.id}}', workflowInstanceId: '{{workflow.instanceId}}' },\n },\n async: true,\n },\n ],\n },\n {\n transitionId: 'approval_to_end',\n transitionName: 'Approval Decision',\n fromStepId: 'approval',\n toStepId: 'end',\n trigger: 'manual',\n priority: 100,\n activities: [\n {\n activityId: 'notify_requester',\n activityName: 'Notify Requester of Decision',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.requester.email}}',\n subject: 'Approval Decision: {{context.request.title}}',\n template: 'approval_decision',\n data: {\n requestId: '{{context.request.id}}',\n title: '{{context.request.title}}',\n decision: '{{task.result.approved}}',\n comments: '{{task.result.comments}}',\n approver: '{{task.completedBy}}',\n },\n },\n timeout: 'PT10S',\n async: true,\n },\n {\n activityId: 'emit_approval_completed',\n activityName: 'Emit Approval Completed Event',\n activityType: 'EMIT_EVENT',\n config: {\n eventType: 'approval.completed',\n payload: {\n requestId: '{{context.request.id}}',\n approved: '{{task.result.approved}}',\n approvedBy: '{{task.completedBy}}',\n timestamp: '{{now}}',\n },\n },\n async: true,\n },\n ],\n },\n ],\n})\n\n// ============================================================================\n// Checkout Demo Workflow\n// ============================================================================\n\nconst checkoutDemo = defineWorkflow({\n workflowId: 'workflows.checkout-demo',\n workflowName: 'Checkout with Payment Webhook',\n description: 'Realistic checkout workflow with signal-based payment confirmation',\n metadata: { category: 'E-commerce', tags: ['checkout', 'demo', 'payments', 'signals'], icon: 'ShoppingCart' },\n steps: [\n {\n stepId: 'start',\n stepName: 'Start',\n stepType: 'START',\n description: 'Checkout process initiated',\n preConditions: [{\n ruleId: 'workflow_checkout_cart_not_empty',\n required: true,\n validationMessage: {\n en: 'Your shopping cart is empty. Please add items before starting checkout.',\n pl: 'Tw\u00F3j koszyk jest pusty. Dodaj produkty przed rozpocz\u0119ciem zakupu.',\n },\n }],\n },\n { stepId: 'cart_validation', stepName: 'Cart Validation', stepType: 'AUTOMATED', description: 'Validate cart has items and inventory is available', timeout: 'PT10S' },\n {\n stepId: 'customer_info',\n stepName: 'Customer Information',\n stepType: 'USER_TASK',\n description: 'Collect customer shipping and contact information',\n userTaskConfig: {\n formSchema: {\n type: 'object',\n properties: {\n fullName: { type: 'string', title: 'Full Name', description: \"Customer's full legal name\" },\n email: { type: 'string', format: 'email', title: 'Email Address', description: 'Email for order confirmation' },\n phone: { type: 'string', title: 'Phone Number', description: 'Contact number for delivery' },\n comment: { type: 'string', title: 'Order Comment', maxLength: 500, description: 'Special instructions or notes' },\n },\n required: ['fullName', 'email'],\n },\n slaDuration: 'PT24H',\n },\n },\n { stepId: 'payment_initiation', stepName: 'Initiate Payment', stepType: 'AUTOMATED', description: 'Send payment request to payment provider', timeout: 'PT10S' },\n {\n stepId: 'wait_payment_confirmation',\n stepName: 'Wait for Payment Confirmation',\n stepType: 'WAIT_FOR_SIGNAL',\n description: 'Waiting for payment provider webhook confirmation',\n signalConfig: { signalName: 'payment_confirmed', timeout: 'PT5M' },\n },\n { stepId: 'order_confirmation', stepName: 'Order Confirmation', stepType: 'AUTOMATED', description: 'Create order record and send confirmation', timeout: 'PT15S' },\n { stepId: 'end', stepName: 'Complete', stepType: 'END', description: 'Checkout completed successfully' },\n ] as const,\n transitions: [\n {\n transitionId: 'start_to_cart',\n transitionName: 'Initialize Checkout',\n fromStepId: 'start',\n toStepId: 'cart_validation',\n trigger: 'auto',\n priority: 100,\n activities: [{\n activityId: 'log_checkout_start',\n activityName: 'Log Checkout Start',\n activityType: 'EMIT_EVENT',\n config: {\n eventName: 'checkout.started',\n payload: { customerId: '{{context.customerId}}', cartId: '{{context.cartId}}', currency: '{{context.currency}}', timestamp: '{{now}}' },\n },\n async: false,\n }],\n },\n {\n transitionId: 'cart_to_customer_info',\n transitionName: 'Collect Customer Information',\n fromStepId: 'cart_validation',\n toStepId: 'customer_info',\n trigger: 'auto',\n priority: 100,\n preConditions: [{ ruleId: 'workflow_checkout_cart_not_empty', required: true }],\n },\n {\n transitionId: 'customer_info_to_payment',\n transitionName: 'Initiate Payment',\n fromStepId: 'customer_info',\n toStepId: 'payment_initiation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'payment_to_wait_confirmation',\n transitionName: 'Wait for Confirmation',\n fromStepId: 'payment_initiation',\n toStepId: 'wait_payment_confirmation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'confirmation_to_order',\n transitionName: 'Payment Confirmed',\n fromStepId: 'wait_payment_confirmation',\n toStepId: 'order_confirmation',\n trigger: 'auto',\n priority: 100,\n },\n {\n transitionId: 'confirmation_to_end',\n transitionName: 'Finalize Order',\n fromStepId: 'order_confirmation',\n toStepId: 'end',\n trigger: 'auto',\n priority: 100,\n activities: [\n {\n activityId: 'create_order',\n activityName: 'Create Order Record',\n activityType: 'CALL_API',\n config: {\n endpoint: '/api/sales/orders',\n method: 'POST',\n body: {\n customerEntityId: '{{context.customer.id}}',\n currencyCode: '{{context.cart.currency}}',\n placedAt: '{{now}}',\n grandTotalGrossAmount: '{{context.cart.total}}',\n },\n validateTenantMatch: true,\n },\n timeout: 'PT10S',\n async: false,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 10000 },\n },\n {\n activityId: 'send_confirmation_email',\n activityName: 'Send Order Confirmation Email',\n activityType: 'SEND_EMAIL',\n config: {\n to: '{{context.customer.email}}',\n subject: 'Order Confirmation',\n template: 'order_confirmation',\n data: { customerName: '{{context.customer.name}}', total: '{{context.cart.total}}' },\n },\n timeout: 'PT15S',\n async: true,\n retryPolicy: { maxAttempts: 3, initialIntervalMs: 2000, backoffCoefficient: 2, maxIntervalMs: 20000 },\n },\n ],\n },\n ],\n})\n\n// ============================================================================\n// Module Export\n// ============================================================================\n\nexport const workflowsConfig = createWorkflowsModuleConfig({\n moduleId: 'workflows',\n workflows: [simpleApproval, checkoutDemo],\n})\n\nexport default workflowsConfig\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -2703,6 +2703,7 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
|
|
|
2703
2703
|
"workflow_definition": {
|
|
2704
2704
|
"id": "id",
|
|
2705
2705
|
"workflow_id": "workflow_id",
|
|
2706
|
+
"code_workflow_id": "code_workflow_id",
|
|
2706
2707
|
"workflow_name": "workflow_name",
|
|
2707
2708
|
"description": "description",
|
|
2708
2709
|
"version": "version",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.2-develop.
|
|
3
|
+
"version": "0.6.2-develop.3406.1.2b403f40da",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -242,16 +242,16 @@
|
|
|
242
242
|
"zod": "^4.3.6"
|
|
243
243
|
},
|
|
244
244
|
"peerDependencies": {
|
|
245
|
-
"@open-mercato/ai-assistant": "0.6.2-develop.
|
|
246
|
-
"@open-mercato/shared": "0.6.2-develop.
|
|
247
|
-
"@open-mercato/ui": "0.6.2-develop.
|
|
245
|
+
"@open-mercato/ai-assistant": "0.6.2-develop.3406.1.2b403f40da",
|
|
246
|
+
"@open-mercato/shared": "0.6.2-develop.3406.1.2b403f40da",
|
|
247
|
+
"@open-mercato/ui": "0.6.2-develop.3406.1.2b403f40da",
|
|
248
248
|
"react": "^19.0.0",
|
|
249
249
|
"react-dom": "^19.0.0"
|
|
250
250
|
},
|
|
251
251
|
"devDependencies": {
|
|
252
|
-
"@open-mercato/ai-assistant": "0.6.2-develop.
|
|
253
|
-
"@open-mercato/shared": "0.6.2-develop.
|
|
254
|
-
"@open-mercato/ui": "0.6.2-develop.
|
|
252
|
+
"@open-mercato/ai-assistant": "0.6.2-develop.3406.1.2b403f40da",
|
|
253
|
+
"@open-mercato/shared": "0.6.2-develop.3406.1.2b403f40da",
|
|
254
|
+
"@open-mercato/ui": "0.6.2-develop.3406.1.2b403f40da",
|
|
255
255
|
"@testing-library/dom": "^10.4.1",
|
|
256
256
|
"@testing-library/jest-dom": "^6.9.1",
|
|
257
257
|
"@testing-library/react": "^16.3.1",
|