@open-mercato/core 0.6.5-develop.5382.1.f542de69af → 0.6.6-develop.5412.1.e2a52b14f0

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.
Files changed (138) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/crmFixtures.js +4 -0
  3. package/dist/helpers/integration/crmFixtures.js.map +2 -2
  4. package/dist/modules/attachments/api/route.js +2 -0
  5. package/dist/modules/attachments/api/route.js.map +2 -2
  6. package/dist/modules/attachments/lib/access.js +18 -0
  7. package/dist/modules/attachments/lib/access.js.map +2 -2
  8. package/dist/modules/auth/services/rbacService.js +3 -2
  9. package/dist/modules/auth/services/rbacService.js.map +2 -2
  10. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
  11. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
  12. package/dist/modules/customers/api/deals/route.js +43 -2
  13. package/dist/modules/customers/api/deals/route.js.map +2 -2
  14. package/dist/modules/customers/api/deals/summary/route.js +402 -0
  15. package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
  16. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
  17. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
  18. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
  19. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
  20. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
  21. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/deals/page.js +221 -56
  23. package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
  24. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
  25. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  26. package/dist/modules/customers/cli.js +15 -9
  27. package/dist/modules/customers/cli.js.map +2 -2
  28. package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
  29. package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
  30. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
  31. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
  32. package/dist/modules/customers/components/detail/DealForm.js +100 -17
  33. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  34. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
  35. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  36. package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
  37. package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
  38. package/dist/modules/customers/lib/dealsMetrics.js +82 -0
  39. package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
  40. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
  41. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
  42. package/dist/modules/directory/utils/organizationScope.js +59 -27
  43. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  44. package/dist/modules/entities/api/entities.js +7 -0
  45. package/dist/modules/entities/api/entities.js.map +2 -2
  46. package/dist/modules/entities/api/records.js +26 -15
  47. package/dist/modules/entities/api/records.js.map +2 -2
  48. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
  49. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  50. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
  51. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
  52. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
  53. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  54. package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
  55. package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
  56. package/dist/modules/query_index/lib/engine.js +4 -2
  57. package/dist/modules/query_index/lib/engine.js.map +2 -2
  58. package/dist/modules/staff/api/team-members.js +9 -2
  59. package/dist/modules/staff/api/team-members.js.map +2 -2
  60. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
  61. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  62. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
  63. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  64. package/dist/modules/staff/commands/team-members.js +1 -1
  65. package/dist/modules/staff/commands/team-members.js.map +2 -2
  66. package/dist/modules/staff/components/TeamMemberForm.js +1 -1
  67. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  68. package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
  69. package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
  70. package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
  71. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  72. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
  73. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  74. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
  75. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  76. package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
  77. package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
  78. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
  79. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
  80. package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
  81. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
  82. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
  83. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
  84. package/package.json +8 -8
  85. package/src/helpers/integration/crmFixtures.ts +21 -1
  86. package/src/modules/attachments/AGENTS.md +79 -0
  87. package/src/modules/attachments/api/route.ts +2 -0
  88. package/src/modules/attachments/lib/access.ts +36 -0
  89. package/src/modules/auth/services/rbacService.ts +11 -2
  90. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
  91. package/src/modules/customers/api/deals/route.ts +51 -2
  92. package/src/modules/customers/api/deals/summary/route.ts +496 -0
  93. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
  94. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
  95. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
  96. package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
  97. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
  98. package/src/modules/customers/cli.ts +15 -15
  99. package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
  100. package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
  101. package/src/modules/customers/components/detail/DealForm.tsx +121 -19
  102. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
  103. package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
  104. package/src/modules/customers/i18n/de.json +43 -0
  105. package/src/modules/customers/i18n/en.json +43 -0
  106. package/src/modules/customers/i18n/es.json +43 -0
  107. package/src/modules/customers/i18n/pl.json +43 -0
  108. package/src/modules/customers/lib/dealsMetrics.ts +159 -0
  109. package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
  110. package/src/modules/directory/utils/organizationScope.ts +85 -30
  111. package/src/modules/entities/api/entities.ts +11 -0
  112. package/src/modules/entities/api/records.ts +46 -25
  113. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
  114. package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
  115. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
  116. package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
  117. package/src/modules/entities/i18n/de.json +1 -0
  118. package/src/modules/entities/i18n/en.json +1 -0
  119. package/src/modules/entities/i18n/es.json +1 -0
  120. package/src/modules/entities/i18n/pl.json +1 -0
  121. package/src/modules/query_index/lib/engine.ts +11 -5
  122. package/src/modules/staff/api/team-members.ts +9 -2
  123. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
  124. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
  125. package/src/modules/staff/commands/team-members.ts +5 -2
  126. package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
  127. package/src/modules/staff/i18n/de.json +1 -0
  128. package/src/modules/staff/i18n/en.json +1 -0
  129. package/src/modules/staff/i18n/es.json +1 -0
  130. package/src/modules/staff/i18n/pl.json +1 -0
  131. package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
  132. package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
  133. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
  134. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
  135. package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
  136. package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
  137. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
  138. package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
@@ -5,7 +5,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } f
5
5
  import { Badge } from "@open-mercato/ui/primitives/badge";
6
6
  import { Alert, AlertDescription } from "@open-mercato/ui/primitives/alert";
7
7
  import { Button } from "@open-mercato/ui/primitives/button";
8
- import { Info, Trash2 } from "lucide-react";
8
+ import { Trash2 } from "lucide-react";
9
9
  import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
10
10
  import { JsonBuilder } from "@open-mercato/ui/backend/JsonBuilder";
11
11
  import { FormFieldArrayEditor } from "./fields/FormFieldArrayEditor.js";
@@ -70,10 +70,7 @@ function NodeEditDialogCrudForm({ node, isOpen, onClose, onSave, onDelete }) {
70
70
  id: "info",
71
71
  column: 1,
72
72
  bare: true,
73
- component: () => /* @__PURE__ */ jsxs(Alert, { variant: "default", className: "border-blue-200 bg-blue-50", children: [
74
- /* @__PURE__ */ jsx(Info, { className: "size-4" }),
75
- /* @__PURE__ */ jsx(AlertDescription, { children: "End nodes cannot be edited. They mark the completion of the workflow." })
76
- ] })
73
+ component: () => /* @__PURE__ */ jsx(Alert, { variant: "info", children: /* @__PURE__ */ jsx(AlertDescription, { children: "End nodes cannot be edited. They mark the completion of the workflow." }) })
77
74
  }
78
75
  ];
79
76
  }
@@ -83,10 +80,7 @@ function NodeEditDialogCrudForm({ node, isOpen, onClose, onSave, onDelete }) {
83
80
  id: "info",
84
81
  column: 1,
85
82
  bare: true,
86
- component: () => /* @__PURE__ */ jsxs(Alert, { variant: "default", className: "border-blue-200 bg-blue-50 mb-4", children: [
87
- /* @__PURE__ */ jsx(Info, { className: "size-4" }),
88
- /* @__PURE__ */ jsx(AlertDescription, { children: "Start nodes mark the beginning of the workflow. You can define pre-conditions that must pass before the workflow can be started." })
89
- ] })
83
+ component: () => /* @__PURE__ */ jsx(Alert, { variant: "info", className: "mb-4", children: /* @__PURE__ */ jsx(AlertDescription, { children: "Start nodes mark the beginning of the workflow. You can define pre-conditions that must pass before the workflow can be started." }) })
90
84
  },
91
85
  {
92
86
  id: "preConditions",
@@ -395,10 +389,7 @@ function NodeEditDialogCrudForm({ node, isOpen, onClose, onSave, onDelete }) {
395
389
  ] })
396
390
  ] }),
397
391
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto min-h-0 px-6", children: [
398
- showJsonSchemaWarning && /* @__PURE__ */ jsxs(Alert, { variant: "default", className: "border-blue-200 bg-blue-50 mb-4", children: [
399
- /* @__PURE__ */ jsx(Info, { className: "size-4" }),
400
- /* @__PURE__ */ jsx(AlertDescription, { className: "text-xs", children: 'This form uses JSON Schema format. Fields have been converted for visual editing. When you save, it will be converted to the simplified format. To preserve the original JSON Schema, edit it in the "Advanced Configuration (JSON)" section.' })
401
- ] }),
392
+ showJsonSchemaWarning && /* @__PURE__ */ jsx(Alert, { variant: "info", className: "mb-4", children: /* @__PURE__ */ jsx(AlertDescription, { className: "text-xs", children: 'This form uses JSON Schema format. Fields have been converted for visual editing. When you save, it will be converted to the simplified format. To preserve the original JSON Schema, edit it in the "Advanced Configuration (JSON)" section.' }) }),
402
393
  /* @__PURE__ */ jsx(
403
394
  CrudForm,
404
395
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/workflows/components/NodeEditDialogCrudForm.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport { Node } from '@xyflow/react'\nimport { useState, useEffect, useCallback, useMemo } from 'react'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@open-mercato/ui/primitives/dialog'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Info, Trash2 } from 'lucide-react'\nimport { CrudForm, type CrudFormGroup, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'\nimport { JsonBuilder } from '@open-mercato/ui/backend/JsonBuilder'\nimport { FormFieldArrayEditor } from './fields/FormFieldArrayEditor'\nimport { ActivityArrayEditor } from './fields/ActivityArrayEditor'\nimport { MappingArrayEditor } from './fields/MappingArrayEditor'\nimport { WorkflowSelectorField } from './fields/WorkflowSelectorField'\nimport { StartPreConditionsEditor } from './fields/StartPreConditionsEditor'\nimport { nodeToFormValues, formValuesToNodeUpdates, isJsonSchemaFormat, type NodeFormValues } from '../lib/nodeFormTransforms'\nimport { sanitizeId } from '../lib/graph-utils'\n\n/**\n * JsonConfigEditor - Custom field wrapper for JsonBuilder\n */\nfunction JsonConfigEditor({ value, setValue, disabled }: CrudCustomFieldRenderProps) {\n return (\n <JsonBuilder\n value={value || {}}\n onChange={setValue}\n disabled={disabled}\n />\n )\n}\n\nexport interface NodeEditDialogCrudFormProps {\n node: Node | null\n isOpen: boolean\n onClose: () => void\n onSave: (nodeId: string, updates: Partial<Node['data']>) => void\n onDelete?: (nodeId: string) => void\n}\n\n/**\n * NodeEditDialogCrudForm - CrudForm-based modal dialog for editing step properties\n *\n * Migrated from NodeEditDialog to use CrudForm for:\n * - UI coherence with other admin forms\n * - Custom fields support (future enhancement)\n * - Standardized validation and error handling\n * - Consistent keyboard shortcuts\n *\n * Handles 7 node types with dynamic groups:\n * - start: Non-editable (alert only)\n * - end: Non-editable (alert only)\n * - userTask: Assignment fields + form builder\n * - automated: Activity type + activities array\n * - subWorkflow: Workflow selector + input/output mappings\n * - waitForSignal: Signal name + timeout\n * - decision: Basic fields only\n */\nexport function NodeEditDialogCrudForm({ node, isOpen, onClose, onSave, onDelete }: NodeEditDialogCrudFormProps) {\n const [initialValues, setInitialValues] = useState<Partial<NodeFormValues>>({})\n const [showJsonSchemaWarning, setShowJsonSchemaWarning] = useState(false)\n\n // Load node data when dialog opens\n useEffect(() => {\n if (node && isOpen) {\n const values = nodeToFormValues(node)\n setInitialValues(values)\n setShowJsonSchemaWarning(isJsonSchemaFormat(node))\n }\n }, [node, isOpen])\n\n const handleSubmit = useCallback(async (values: Record<string, unknown>) => {\n if (!node) return\n\n // Validate and sanitize step ID\n const sanitizedId = sanitizeId(node.id)\n if (sanitizedId !== node.id) {\n if (typeof window !== 'undefined') {\n window.alert(\n `\u26A0\uFE0F Step ID was sanitized from \"${node.id}\" to \"${sanitizedId}\" to match schema requirements (lowercase letters, numbers, hyphens, and underscores only).`\n )\n }\n }\n\n try {\n const updates = formValuesToNodeUpdates(values as unknown as NodeFormValues, node)\n onSave(node.id, updates)\n onClose()\n } catch (error) {\n // Error will be displayed in form (e.g., invalid JSON)\n throw error\n }\n }, [node, onSave, onClose])\n\n const handleDelete = useCallback(() => {\n if (!node || !onDelete) return\n onDelete(node.id)\n }, [node, onDelete])\n\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose()\n }\n }, [onClose])\n\n // Dynamic groups based on node type\n const groups: CrudFormGroup[] = useMemo(() => {\n if (!node) return []\n\n // End nodes are non-editable\n if (node.type === 'end') {\n return [\n {\n id: 'info',\n column: 1,\n bare: true,\n component: () => (\n <Alert variant=\"default\" className=\"border-blue-200 bg-blue-50\">\n <Info className=\"size-4\" />\n <AlertDescription>\n End nodes cannot be edited. They mark the completion of the workflow.\n </AlertDescription>\n </Alert>\n ),\n },\n ]\n }\n\n // Start nodes: allow editing pre-conditions\n if (node.type === 'start') {\n return [\n {\n id: 'info',\n column: 1,\n bare: true,\n component: () => (\n <Alert variant=\"default\" className=\"border-blue-200 bg-blue-50 mb-4\">\n <Info className=\"size-4\" />\n <AlertDescription>\n Start nodes mark the beginning of the workflow. You can define pre-conditions that must pass before the workflow can be started.\n </AlertDescription>\n </Alert>\n ),\n },\n {\n id: 'preConditions',\n title: 'Pre-Conditions',\n column: 1,\n description: 'Business rules that must pass before the workflow can start',\n fields: ['preConditions'],\n },\n ]\n }\n\n const baseGroups: CrudFormGroup[] = [\n {\n id: 'basic',\n title: 'Basic Information',\n column: 1,\n fields: ['stepName', 'description', 'timeout'],\n },\n ]\n\n // UserTask specific groups\n if (node.type === 'userTask') {\n return [\n ...baseGroups,\n {\n id: 'userTask',\n title: 'User Task Configuration',\n column: 1,\n fields: ['assignedTo', 'assignedToRoles', 'formKey'],\n },\n {\n id: 'formFields',\n title: 'Form Fields',\n column: 1,\n description: 'Define the form structure for this user task',\n fields: ['formFields'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration (userTaskConfig, retryPolicy, etc.)',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // Automated specific groups\n if (node.type === 'automated') {\n return [\n ...baseGroups,\n {\n id: 'automated',\n title: 'Automated Task Configuration',\n column: 1,\n fields: ['activityType', 'activityId'],\n },\n {\n id: 'stepActivities',\n title: 'Step Activities',\n column: 1,\n description: 'Activities executed as part of this automated step',\n fields: ['stepActivities'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration (retryPolicy, etc.)',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // SubWorkflow specific groups\n if (node.type === 'subWorkflow') {\n return [\n ...baseGroups,\n {\n id: 'subWorkflow',\n title: 'Sub-Workflow Configuration',\n column: 1,\n fields: ['subWorkflowId', 'subWorkflowVersion'],\n },\n {\n id: 'mappings',\n title: 'Data Mappings',\n column: 1,\n description: 'Map data between parent and sub-workflow',\n fields: ['inputMappings', 'outputMappings'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // WaitForSignal specific groups\n if (node.type === 'waitForSignal') {\n return [\n ...baseGroups,\n {\n id: 'signal',\n title: 'Signal Configuration',\n column: 1,\n fields: ['signalName', 'signalTimeout'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // Decision and other types: just basic fields + advanced\n return [\n ...baseGroups,\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration',\n fields: ['advancedConfig'],\n },\n ]\n }, [node])\n\n // Define all possible form fields (only relevant ones are used based on groups)\n const fields: CrudField[] = useMemo(() => [\n // Basic fields\n {\n id: 'stepName',\n label: 'Step Name',\n type: 'text',\n placeholder: 'Enter step name',\n required: true,\n description: 'Display name for this step',\n },\n {\n id: 'description',\n label: 'Description',\n type: 'textarea',\n placeholder: 'Enter step description',\n description: 'Optional description of what this step does',\n },\n {\n id: 'timeout',\n label: 'Timeout',\n type: 'text',\n placeholder: 'PT30S or 30000',\n description: 'ISO 8601 duration (e.g., PT30S) or milliseconds',\n },\n\n // UserTask fields\n {\n id: 'assignedTo',\n label: 'Assigned To',\n type: 'text',\n placeholder: 'user@example.com or userId',\n description: 'User email or ID to assign this task to',\n },\n {\n id: 'assignedToRoles',\n label: 'Assigned To Roles',\n type: 'text',\n placeholder: 'admin, manager',\n description: 'Comma-separated list of roles that can claim this task',\n },\n {\n id: 'formKey',\n label: 'Form Key',\n type: 'text',\n placeholder: 'approval_form',\n description: 'Optional form key for external form rendering',\n },\n {\n id: 'formFields',\n label: 'Form Fields',\n type: 'custom',\n component: (props) => (\n <FormFieldArrayEditor\n {...props}\n value={props.value as any}\n isJsonSchemaFormat={showJsonSchemaWarning}\n />\n ),\n },\n\n // Automated fields\n {\n id: 'activityType',\n label: 'Activity Type',\n type: 'select',\n options: [\n { value: 'SEND_EMAIL', label: 'Send Email' },\n { value: 'CALL_API', label: 'Call API' },\n { value: 'UPDATE_ENTITY', label: 'Update Entity' },\n { value: 'EMIT_EVENT', label: 'Emit Event' },\n { value: 'CALL_WEBHOOK', label: 'Call Webhook' },\n { value: 'EXECUTE_FUNCTION', label: 'Execute Function' },\n { value: 'WAIT', label: 'Wait' },\n ],\n description: 'Type of activity to execute',\n },\n {\n id: 'activityId',\n label: 'Activity ID',\n type: 'text',\n placeholder: 'send_welcome_email',\n description: 'Unique identifier for this activity',\n },\n {\n id: 'stepActivities',\n label: 'Step Activities',\n type: 'custom',\n component: (props) => <ActivityArrayEditor {...props} value={props.value as any} />,\n },\n\n // SubWorkflow fields\n {\n id: 'subWorkflowId',\n label: 'Sub-Workflow',\n type: 'custom',\n component: (props) => <WorkflowSelectorField {...props} value={props.value as any} />,\n },\n {\n id: 'subWorkflowVersion',\n label: 'Version',\n type: 'number',\n placeholder: '1',\n description: 'Specific version of the sub-workflow to invoke',\n },\n {\n id: 'inputMappings',\n label: 'Input Mappings',\n type: 'custom',\n component: (props) => (\n <MappingArrayEditor\n {...props}\n value={props.value as any}\n label=\"Input Mappings\"\n description=\"Map parent workflow data to sub-workflow input\"\n />\n ),\n },\n {\n id: 'outputMappings',\n label: 'Output Mappings',\n type: 'custom',\n component: (props) => (\n <MappingArrayEditor\n {...props}\n value={props.value as any}\n label=\"Output Mappings\"\n description=\"Map sub-workflow output back to parent workflow\"\n />\n ),\n },\n\n // WaitForSignal fields\n {\n id: 'signalName',\n label: 'Signal Name',\n type: 'text',\n placeholder: 'approval_received',\n description: 'Name of the signal to wait for',\n },\n {\n id: 'signalTimeout',\n label: 'Signal Timeout',\n type: 'text',\n placeholder: 'PT5M',\n description: 'How long to wait for the signal (ISO 8601 duration)',\n },\n\n // Advanced configuration\n {\n id: 'advancedConfig',\n label: 'Advanced Configuration (JSON)',\n type: 'custom',\n description: 'Additional JSON configuration merged with the step data',\n component: (props) => <JsonConfigEditor {...props} />,\n },\n\n // Start node pre-conditions\n {\n id: 'preConditions',\n label: 'Pre-Conditions',\n type: 'custom',\n description: 'Business rules that must pass before the workflow can start',\n component: (props) => <StartPreConditionsEditor {...props} value={props.value as any} />,\n },\n ], [showJsonSchemaWarning])\n\n if (!isOpen || !node) return null\n\n const nodeType = node.type || 'unknown'\n const nodeTypeLabel = nodeType.charAt(0).toUpperCase() + nodeType.slice(1).replace(/([A-Z])/g, ' $1')\n\n const canDelete = !!onDelete\n\n return (\n <Dialog open={isOpen} onOpenChange={onClose}>\n <DialogContent\n className=\"sm:max-w-7xl max-h-[90vh] overflow-hidden flex flex-col !p-0 [&_.grid]:!grid-cols-1\"\n onKeyDown={handleKeyDown}\n >\n <DialogHeader className=\"flex-shrink-0 p-6 pb-4 border-b border-border/70\">\n <div className=\"flex items-center gap-2 mb-2\">\n <DialogTitle>Edit Step</DialogTitle>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {nodeTypeLabel}\n </Badge>\n </div>\n <div className=\"space-y-1\">\n <DialogDescription>\n Configure step properties and behavior\n </DialogDescription>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span className=\"font-medium\">ID:</span>\n <code className=\"px-1.5 py-0.5 rounded bg-muted font-mono\">{node.id}</code>\n </div>\n </div>\n </DialogHeader>\n\n <div className=\"flex-1 overflow-y-auto min-h-0 px-6\">\n {/* JSON Schema Conversion Warning */}\n {showJsonSchemaWarning && (\n <Alert variant=\"default\" className=\"border-blue-200 bg-blue-50 mb-4\">\n <Info className=\"size-4\" />\n <AlertDescription className=\"text-xs\">\n This form uses JSON Schema format. Fields have been converted for visual editing.\n When you save, it will be converted to the simplified format. To preserve the original JSON Schema,\n edit it in the &#34;Advanced Configuration (JSON)&#34; section.\n </AlertDescription>\n </Alert>\n )}\n\n <CrudForm\n fields={fields}\n groups={groups}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n embedded={true}\n submitLabel=\"Save Step\"\n extraActions={\n canDelete ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleDelete}\n className=\"text-red-600 border-red-200 hover:bg-red-50\"\n >\n <Trash2 className=\"size-4 mr-2\" />\n Delete Step\n </Button>\n ) : undefined\n }\n />\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport { Node } from '@xyflow/react'\nimport { useState, useEffect, useCallback, useMemo } from 'react'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@open-mercato/ui/primitives/dialog'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Trash2 } from 'lucide-react'\nimport { CrudForm, type CrudFormGroup, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'\nimport { JsonBuilder } from '@open-mercato/ui/backend/JsonBuilder'\nimport { FormFieldArrayEditor } from './fields/FormFieldArrayEditor'\nimport { ActivityArrayEditor } from './fields/ActivityArrayEditor'\nimport { MappingArrayEditor } from './fields/MappingArrayEditor'\nimport { WorkflowSelectorField } from './fields/WorkflowSelectorField'\nimport { StartPreConditionsEditor } from './fields/StartPreConditionsEditor'\nimport { nodeToFormValues, formValuesToNodeUpdates, isJsonSchemaFormat, type NodeFormValues } from '../lib/nodeFormTransforms'\nimport { sanitizeId } from '../lib/graph-utils'\n\n/**\n * JsonConfigEditor - Custom field wrapper for JsonBuilder\n */\nfunction JsonConfigEditor({ value, setValue, disabled }: CrudCustomFieldRenderProps) {\n return (\n <JsonBuilder\n value={value || {}}\n onChange={setValue}\n disabled={disabled}\n />\n )\n}\n\nexport interface NodeEditDialogCrudFormProps {\n node: Node | null\n isOpen: boolean\n onClose: () => void\n onSave: (nodeId: string, updates: Partial<Node['data']>) => void\n onDelete?: (nodeId: string) => void\n}\n\n/**\n * NodeEditDialogCrudForm - CrudForm-based modal dialog for editing step properties\n *\n * Migrated from NodeEditDialog to use CrudForm for:\n * - UI coherence with other admin forms\n * - Custom fields support (future enhancement)\n * - Standardized validation and error handling\n * - Consistent keyboard shortcuts\n *\n * Handles 7 node types with dynamic groups:\n * - start: Non-editable (alert only)\n * - end: Non-editable (alert only)\n * - userTask: Assignment fields + form builder\n * - automated: Activity type + activities array\n * - subWorkflow: Workflow selector + input/output mappings\n * - waitForSignal: Signal name + timeout\n * - decision: Basic fields only\n */\nexport function NodeEditDialogCrudForm({ node, isOpen, onClose, onSave, onDelete }: NodeEditDialogCrudFormProps) {\n const [initialValues, setInitialValues] = useState<Partial<NodeFormValues>>({})\n const [showJsonSchemaWarning, setShowJsonSchemaWarning] = useState(false)\n\n // Load node data when dialog opens\n useEffect(() => {\n if (node && isOpen) {\n const values = nodeToFormValues(node)\n setInitialValues(values)\n setShowJsonSchemaWarning(isJsonSchemaFormat(node))\n }\n }, [node, isOpen])\n\n const handleSubmit = useCallback(async (values: Record<string, unknown>) => {\n if (!node) return\n\n // Validate and sanitize step ID\n const sanitizedId = sanitizeId(node.id)\n if (sanitizedId !== node.id) {\n if (typeof window !== 'undefined') {\n window.alert(\n `\u26A0\uFE0F Step ID was sanitized from \"${node.id}\" to \"${sanitizedId}\" to match schema requirements (lowercase letters, numbers, hyphens, and underscores only).`\n )\n }\n }\n\n try {\n const updates = formValuesToNodeUpdates(values as unknown as NodeFormValues, node)\n onSave(node.id, updates)\n onClose()\n } catch (error) {\n // Error will be displayed in form (e.g., invalid JSON)\n throw error\n }\n }, [node, onSave, onClose])\n\n const handleDelete = useCallback(() => {\n if (!node || !onDelete) return\n onDelete(node.id)\n }, [node, onDelete])\n\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose()\n }\n }, [onClose])\n\n // Dynamic groups based on node type\n const groups: CrudFormGroup[] = useMemo(() => {\n if (!node) return []\n\n // End nodes are non-editable\n if (node.type === 'end') {\n return [\n {\n id: 'info',\n column: 1,\n bare: true,\n component: () => (\n <Alert variant=\"info\">\n <AlertDescription>\n End nodes cannot be edited. They mark the completion of the workflow.\n </AlertDescription>\n </Alert>\n ),\n },\n ]\n }\n\n // Start nodes: allow editing pre-conditions\n if (node.type === 'start') {\n return [\n {\n id: 'info',\n column: 1,\n bare: true,\n component: () => (\n <Alert variant=\"info\" className=\"mb-4\">\n <AlertDescription>\n Start nodes mark the beginning of the workflow. You can define pre-conditions that must pass before the workflow can be started.\n </AlertDescription>\n </Alert>\n ),\n },\n {\n id: 'preConditions',\n title: 'Pre-Conditions',\n column: 1,\n description: 'Business rules that must pass before the workflow can start',\n fields: ['preConditions'],\n },\n ]\n }\n\n const baseGroups: CrudFormGroup[] = [\n {\n id: 'basic',\n title: 'Basic Information',\n column: 1,\n fields: ['stepName', 'description', 'timeout'],\n },\n ]\n\n // UserTask specific groups\n if (node.type === 'userTask') {\n return [\n ...baseGroups,\n {\n id: 'userTask',\n title: 'User Task Configuration',\n column: 1,\n fields: ['assignedTo', 'assignedToRoles', 'formKey'],\n },\n {\n id: 'formFields',\n title: 'Form Fields',\n column: 1,\n description: 'Define the form structure for this user task',\n fields: ['formFields'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration (userTaskConfig, retryPolicy, etc.)',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // Automated specific groups\n if (node.type === 'automated') {\n return [\n ...baseGroups,\n {\n id: 'automated',\n title: 'Automated Task Configuration',\n column: 1,\n fields: ['activityType', 'activityId'],\n },\n {\n id: 'stepActivities',\n title: 'Step Activities',\n column: 1,\n description: 'Activities executed as part of this automated step',\n fields: ['stepActivities'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration (retryPolicy, etc.)',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // SubWorkflow specific groups\n if (node.type === 'subWorkflow') {\n return [\n ...baseGroups,\n {\n id: 'subWorkflow',\n title: 'Sub-Workflow Configuration',\n column: 1,\n fields: ['subWorkflowId', 'subWorkflowVersion'],\n },\n {\n id: 'mappings',\n title: 'Data Mappings',\n column: 1,\n description: 'Map data between parent and sub-workflow',\n fields: ['inputMappings', 'outputMappings'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // WaitForSignal specific groups\n if (node.type === 'waitForSignal') {\n return [\n ...baseGroups,\n {\n id: 'signal',\n title: 'Signal Configuration',\n column: 1,\n fields: ['signalName', 'signalTimeout'],\n },\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration',\n fields: ['advancedConfig'],\n },\n ]\n }\n\n // Decision and other types: just basic fields + advanced\n return [\n ...baseGroups,\n {\n id: 'advanced',\n title: 'Advanced Configuration',\n column: 1,\n description: 'Additional JSON configuration',\n fields: ['advancedConfig'],\n },\n ]\n }, [node])\n\n // Define all possible form fields (only relevant ones are used based on groups)\n const fields: CrudField[] = useMemo(() => [\n // Basic fields\n {\n id: 'stepName',\n label: 'Step Name',\n type: 'text',\n placeholder: 'Enter step name',\n required: true,\n description: 'Display name for this step',\n },\n {\n id: 'description',\n label: 'Description',\n type: 'textarea',\n placeholder: 'Enter step description',\n description: 'Optional description of what this step does',\n },\n {\n id: 'timeout',\n label: 'Timeout',\n type: 'text',\n placeholder: 'PT30S or 30000',\n description: 'ISO 8601 duration (e.g., PT30S) or milliseconds',\n },\n\n // UserTask fields\n {\n id: 'assignedTo',\n label: 'Assigned To',\n type: 'text',\n placeholder: 'user@example.com or userId',\n description: 'User email or ID to assign this task to',\n },\n {\n id: 'assignedToRoles',\n label: 'Assigned To Roles',\n type: 'text',\n placeholder: 'admin, manager',\n description: 'Comma-separated list of roles that can claim this task',\n },\n {\n id: 'formKey',\n label: 'Form Key',\n type: 'text',\n placeholder: 'approval_form',\n description: 'Optional form key for external form rendering',\n },\n {\n id: 'formFields',\n label: 'Form Fields',\n type: 'custom',\n component: (props) => (\n <FormFieldArrayEditor\n {...props}\n value={props.value as any}\n isJsonSchemaFormat={showJsonSchemaWarning}\n />\n ),\n },\n\n // Automated fields\n {\n id: 'activityType',\n label: 'Activity Type',\n type: 'select',\n options: [\n { value: 'SEND_EMAIL', label: 'Send Email' },\n { value: 'CALL_API', label: 'Call API' },\n { value: 'UPDATE_ENTITY', label: 'Update Entity' },\n { value: 'EMIT_EVENT', label: 'Emit Event' },\n { value: 'CALL_WEBHOOK', label: 'Call Webhook' },\n { value: 'EXECUTE_FUNCTION', label: 'Execute Function' },\n { value: 'WAIT', label: 'Wait' },\n ],\n description: 'Type of activity to execute',\n },\n {\n id: 'activityId',\n label: 'Activity ID',\n type: 'text',\n placeholder: 'send_welcome_email',\n description: 'Unique identifier for this activity',\n },\n {\n id: 'stepActivities',\n label: 'Step Activities',\n type: 'custom',\n component: (props) => <ActivityArrayEditor {...props} value={props.value as any} />,\n },\n\n // SubWorkflow fields\n {\n id: 'subWorkflowId',\n label: 'Sub-Workflow',\n type: 'custom',\n component: (props) => <WorkflowSelectorField {...props} value={props.value as any} />,\n },\n {\n id: 'subWorkflowVersion',\n label: 'Version',\n type: 'number',\n placeholder: '1',\n description: 'Specific version of the sub-workflow to invoke',\n },\n {\n id: 'inputMappings',\n label: 'Input Mappings',\n type: 'custom',\n component: (props) => (\n <MappingArrayEditor\n {...props}\n value={props.value as any}\n label=\"Input Mappings\"\n description=\"Map parent workflow data to sub-workflow input\"\n />\n ),\n },\n {\n id: 'outputMappings',\n label: 'Output Mappings',\n type: 'custom',\n component: (props) => (\n <MappingArrayEditor\n {...props}\n value={props.value as any}\n label=\"Output Mappings\"\n description=\"Map sub-workflow output back to parent workflow\"\n />\n ),\n },\n\n // WaitForSignal fields\n {\n id: 'signalName',\n label: 'Signal Name',\n type: 'text',\n placeholder: 'approval_received',\n description: 'Name of the signal to wait for',\n },\n {\n id: 'signalTimeout',\n label: 'Signal Timeout',\n type: 'text',\n placeholder: 'PT5M',\n description: 'How long to wait for the signal (ISO 8601 duration)',\n },\n\n // Advanced configuration\n {\n id: 'advancedConfig',\n label: 'Advanced Configuration (JSON)',\n type: 'custom',\n description: 'Additional JSON configuration merged with the step data',\n component: (props) => <JsonConfigEditor {...props} />,\n },\n\n // Start node pre-conditions\n {\n id: 'preConditions',\n label: 'Pre-Conditions',\n type: 'custom',\n description: 'Business rules that must pass before the workflow can start',\n component: (props) => <StartPreConditionsEditor {...props} value={props.value as any} />,\n },\n ], [showJsonSchemaWarning])\n\n if (!isOpen || !node) return null\n\n const nodeType = node.type || 'unknown'\n const nodeTypeLabel = nodeType.charAt(0).toUpperCase() + nodeType.slice(1).replace(/([A-Z])/g, ' $1')\n\n const canDelete = !!onDelete\n\n return (\n <Dialog open={isOpen} onOpenChange={onClose}>\n <DialogContent\n className=\"sm:max-w-7xl max-h-[90vh] overflow-hidden flex flex-col !p-0 [&_.grid]:!grid-cols-1\"\n onKeyDown={handleKeyDown}\n >\n <DialogHeader className=\"flex-shrink-0 p-6 pb-4 border-b border-border/70\">\n <div className=\"flex items-center gap-2 mb-2\">\n <DialogTitle>Edit Step</DialogTitle>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {nodeTypeLabel}\n </Badge>\n </div>\n <div className=\"space-y-1\">\n <DialogDescription>\n Configure step properties and behavior\n </DialogDescription>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span className=\"font-medium\">ID:</span>\n <code className=\"px-1.5 py-0.5 rounded bg-muted font-mono\">{node.id}</code>\n </div>\n </div>\n </DialogHeader>\n\n <div className=\"flex-1 overflow-y-auto min-h-0 px-6\">\n {/* JSON Schema Conversion Warning */}\n {showJsonSchemaWarning && (\n <Alert variant=\"info\" className=\"mb-4\">\n <AlertDescription className=\"text-xs\">\n This form uses JSON Schema format. Fields have been converted for visual editing.\n When you save, it will be converted to the simplified format. To preserve the original JSON Schema,\n edit it in the &#34;Advanced Configuration (JSON)&#34; section.\n </AlertDescription>\n </Alert>\n )}\n\n <CrudForm\n fields={fields}\n groups={groups}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n embedded={true}\n submitLabel=\"Save Step\"\n extraActions={\n canDelete ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleDelete}\n className=\"text-red-600 border-red-200 hover:bg-red-50\"\n >\n <Trash2 className=\"size-4 mr-2\" />\n Delete Step\n </Button>\n ) : undefined\n }\n />\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -184,10 +184,7 @@ function WorkflowGraphImpl({
184
184
  }
185
185
  ),
186
186
  !editable && !isCompactViewport && /* @__PURE__ */ jsx(Panel, { position: "top-left", style: { margin: 10 }, children: /* @__PURE__ */ jsx("div", { className: "bg-card rounded-lg shadow-sm border border-border px-4 py-2", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground font-medium", children: t("workflows.graph.visualization") }) }) }),
187
- editable && !isCompactViewport && /* @__PURE__ */ jsx(Panel, { position: "top-left", style: { margin: 10 }, children: /* @__PURE__ */ jsxs(Alert, { variant: "info", className: "max-w-sm", children: [
188
- /* @__PURE__ */ jsx(Edit3, { className: "size-4" }),
189
- /* @__PURE__ */ jsx(AlertDescription, { className: "font-medium", children: t("workflows.graph.editModeInfo") })
190
- ] }) })
187
+ editable && !isCompactViewport && /* @__PURE__ */ jsx(Panel, { position: "top-left", style: { margin: 10 }, children: /* @__PURE__ */ jsx(Alert, { variant: "info", icon: /* @__PURE__ */ jsx(Edit3, { "aria-hidden": "true" }), className: "max-w-sm", children: /* @__PURE__ */ jsx(AlertDescription, { className: "font-medium", children: t("workflows.graph.editModeInfo") }) }) })
191
188
  ]
192
189
  }
193
190
  ) });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/workflows/components/WorkflowGraphImpl.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport '@xyflow/react/dist/style.css'\n\nimport { useCallback, useMemo, useEffect, useState } from 'react'\nimport {\n ReactFlow,\n Node,\n Edge,\n Controls,\n Background,\n BackgroundVariant,\n MiniMap,\n Panel,\n useNodesState,\n useEdgesState,\n addEdge,\n Connection,\n ConnectionMode,\n MarkerType,\n} from '@xyflow/react'\nimport {StartNode, EndNode, UserTaskNode, AutomatedNode, SubWorkflowNode, WaitForSignalNode, WaitForTimerNode, ParallelForkNode, ParallelJoinNode} from './nodes'\nimport { WorkflowTransitionEdge } from './WorkflowTransitionEdge'\nimport { STATUS_COLORS } from '../lib/status-colors'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Edit3 } from 'lucide-react'\nimport { useTheme } from '@open-mercato/ui/theme'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport interface WorkflowGraphImplProps {\n initialNodes?: Node[]\n initialEdges?: Edge[]\n onNodesChange?: (changes: any[]) => void\n onEdgesChange?: (changes: any[]) => void\n onNodeClick?: (event: React.MouseEvent, node: Node) => void\n onEdgeClick?: (event: React.MouseEvent, edge: Edge) => void\n onConnect?: (connection: Connection) => void\n editable?: boolean\n className?: string\n height?: string\n}\n\nexport default function WorkflowGraphImpl({\n initialNodes = [],\n initialEdges = [],\n onNodesChange: onNodesChangeProp,\n onEdgesChange: onEdgesChangeProp,\n onNodeClick: onNodeClickProp,\n onEdgeClick: onEdgeClickProp,\n onConnect: onConnectProp,\n editable = false,\n className = '',\n height = '600px',\n}: WorkflowGraphImplProps) {\n const t = useT()\n const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)\n const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)\n\n const { resolvedTheme } = useTheme()\n const isDark = resolvedTheme === 'dark'\n const backgroundDotColor = isDark ? '#374151' : '#e5e7eb'\n const [isCompactViewport, setIsCompactViewport] = useState(false)\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n const mediaQuery = window.matchMedia('(max-width: 1279px)')\n const updateViewportMode = () => setIsCompactViewport(mediaQuery.matches)\n\n updateViewportMode()\n mediaQuery.addEventListener('change', updateViewportMode)\n\n return () => {\n mediaQuery.removeEventListener('change', updateViewportMode)\n }\n }, [])\n\n useEffect(() => {\n setNodes(initialNodes)\n }, [initialNodes, setNodes])\n\n useEffect(() => {\n setEdges(initialEdges)\n }, [initialEdges, setEdges])\n\n const onConnect = useCallback(\n (connection: Connection) => {\n if (onConnectProp) {\n onConnectProp(connection)\n } else {\n const newEdge = {\n ...connection,\n type: 'workflowTransition',\n animated: false,\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 16,\n height: 16,\n color: '#9ca3af',\n },\n }\n setEdges((eds) => addEdge(newEdge, eds))\n }\n },\n [setEdges, onConnectProp]\n )\n\n const handleNodesChange = useCallback(\n (changes: any) => {\n onNodesChange(changes)\n if (onNodesChangeProp) {\n onNodesChangeProp(changes)\n }\n },\n [onNodesChange, onNodesChangeProp]\n )\n\n const handleEdgesChange = useCallback(\n (changes: any) => {\n onEdgesChange(changes)\n if (onEdgesChangeProp) {\n onEdgesChangeProp(changes)\n }\n },\n [onEdgesChange, onEdgesChangeProp]\n )\n\n const nodeTypes = useMemo(\n () => ({\n start: StartNode,\n end: EndNode,\n userTask: UserTaskNode,\n automated: AutomatedNode,\n subWorkflow: SubWorkflowNode,\n waitForSignal: WaitForSignalNode,\n waitForTimer: WaitForTimerNode,\n parallelFork: ParallelForkNode,\n parallelJoin: ParallelJoinNode,\n }),\n []\n )\n\n const edgeTypes = useMemo(\n () => ({\n workflowTransition: WorkflowTransitionEdge,\n }),\n []\n )\n\n return (\n <div className={`workflow-graph-container ${className}`} style={{ height }}>\n <ReactFlow\n nodes={nodes}\n edges={edges}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n onNodesChange={handleNodesChange}\n onEdgesChange={handleEdgesChange}\n onConnect={editable ? onConnect : undefined}\n onNodeClick={onNodeClickProp}\n onEdgeClick={onEdgeClickProp}\n connectionMode={ConnectionMode.Loose}\n fitView\n fitViewOptions={{\n padding: 0.2,\n maxZoom: isCompactViewport ? 0.9 : 1,\n }}\n minZoom={0.1}\n maxZoom={2}\n defaultEdgeOptions={{\n type: 'workflowTransition',\n animated: false,\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 16,\n height: 16,\n color: '#9ca3af',\n },\n }}\n nodesDraggable={editable}\n nodesConnectable={editable}\n elementsSelectable={editable}\n proOptions={{ hideAttribution: true }}\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={16}\n size={1}\n color={backgroundDotColor}\n />\n\n <Controls\n showZoom={true}\n showFitView={true}\n showInteractive={false}\n position={isCompactViewport ? 'bottom-right' : 'top-right'}\n className={`!bg-card !border-border !shadow-md [&>button]:!bg-card [&>button]:!border-border [&>button]:!fill-foreground [&>button:hover]:!bg-muted ${isCompactViewport ? 'scale-90 origin-bottom-right' : ''}`}\n />\n\n {!isCompactViewport && (\n <MiniMap\n nodeStrokeWidth={3}\n nodeColor={(node) => {\n const status = (node.data?.status || 'not_started') as keyof typeof STATUS_COLORS\n return STATUS_COLORS[status]?.hex || STATUS_COLORS.not_started.hex\n }}\n maskColor=\"rgba(0, 0, 0, 0.1)\"\n position=\"bottom-left\"\n className=\"!bg-card !border !border-border !rounded-lg\"\n />\n )}\n\n {!editable && !isCompactViewport && (\n <Panel position=\"top-left\" style={{ margin: 10 }}>\n <div className=\"bg-card rounded-lg shadow-sm border border-border px-4 py-2\">\n <p className=\"text-sm text-muted-foreground font-medium\">\n {t('workflows.graph.visualization')}\n </p>\n </div>\n </Panel>\n )}\n\n {editable && !isCompactViewport && (\n <Panel position=\"top-left\" style={{ margin: 10 }}>\n <Alert variant=\"info\" className=\"max-w-sm\">\n <Edit3 className=\"size-4\" />\n <AlertDescription className=\"font-medium\">\n {t('workflows.graph.editModeInfo')}\n </AlertDescription>\n </Alert>\n </Panel>\n )}\n </ReactFlow>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport '@xyflow/react/dist/style.css'\n\nimport { useCallback, useMemo, useEffect, useState } from 'react'\nimport {\n ReactFlow,\n Node,\n Edge,\n Controls,\n Background,\n BackgroundVariant,\n MiniMap,\n Panel,\n useNodesState,\n useEdgesState,\n addEdge,\n Connection,\n ConnectionMode,\n MarkerType,\n} from '@xyflow/react'\nimport {StartNode, EndNode, UserTaskNode, AutomatedNode, SubWorkflowNode, WaitForSignalNode, WaitForTimerNode, ParallelForkNode, ParallelJoinNode} from './nodes'\nimport { WorkflowTransitionEdge } from './WorkflowTransitionEdge'\nimport { STATUS_COLORS } from '../lib/status-colors'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Edit3 } from 'lucide-react'\nimport { useTheme } from '@open-mercato/ui/theme'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport interface WorkflowGraphImplProps {\n initialNodes?: Node[]\n initialEdges?: Edge[]\n onNodesChange?: (changes: any[]) => void\n onEdgesChange?: (changes: any[]) => void\n onNodeClick?: (event: React.MouseEvent, node: Node) => void\n onEdgeClick?: (event: React.MouseEvent, edge: Edge) => void\n onConnect?: (connection: Connection) => void\n editable?: boolean\n className?: string\n height?: string\n}\n\nexport default function WorkflowGraphImpl({\n initialNodes = [],\n initialEdges = [],\n onNodesChange: onNodesChangeProp,\n onEdgesChange: onEdgesChangeProp,\n onNodeClick: onNodeClickProp,\n onEdgeClick: onEdgeClickProp,\n onConnect: onConnectProp,\n editable = false,\n className = '',\n height = '600px',\n}: WorkflowGraphImplProps) {\n const t = useT()\n const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)\n const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)\n\n const { resolvedTheme } = useTheme()\n const isDark = resolvedTheme === 'dark'\n const backgroundDotColor = isDark ? '#374151' : '#e5e7eb'\n const [isCompactViewport, setIsCompactViewport] = useState(false)\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n const mediaQuery = window.matchMedia('(max-width: 1279px)')\n const updateViewportMode = () => setIsCompactViewport(mediaQuery.matches)\n\n updateViewportMode()\n mediaQuery.addEventListener('change', updateViewportMode)\n\n return () => {\n mediaQuery.removeEventListener('change', updateViewportMode)\n }\n }, [])\n\n useEffect(() => {\n setNodes(initialNodes)\n }, [initialNodes, setNodes])\n\n useEffect(() => {\n setEdges(initialEdges)\n }, [initialEdges, setEdges])\n\n const onConnect = useCallback(\n (connection: Connection) => {\n if (onConnectProp) {\n onConnectProp(connection)\n } else {\n const newEdge = {\n ...connection,\n type: 'workflowTransition',\n animated: false,\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 16,\n height: 16,\n color: '#9ca3af',\n },\n }\n setEdges((eds) => addEdge(newEdge, eds))\n }\n },\n [setEdges, onConnectProp]\n )\n\n const handleNodesChange = useCallback(\n (changes: any) => {\n onNodesChange(changes)\n if (onNodesChangeProp) {\n onNodesChangeProp(changes)\n }\n },\n [onNodesChange, onNodesChangeProp]\n )\n\n const handleEdgesChange = useCallback(\n (changes: any) => {\n onEdgesChange(changes)\n if (onEdgesChangeProp) {\n onEdgesChangeProp(changes)\n }\n },\n [onEdgesChange, onEdgesChangeProp]\n )\n\n const nodeTypes = useMemo(\n () => ({\n start: StartNode,\n end: EndNode,\n userTask: UserTaskNode,\n automated: AutomatedNode,\n subWorkflow: SubWorkflowNode,\n waitForSignal: WaitForSignalNode,\n waitForTimer: WaitForTimerNode,\n parallelFork: ParallelForkNode,\n parallelJoin: ParallelJoinNode,\n }),\n []\n )\n\n const edgeTypes = useMemo(\n () => ({\n workflowTransition: WorkflowTransitionEdge,\n }),\n []\n )\n\n return (\n <div className={`workflow-graph-container ${className}`} style={{ height }}>\n <ReactFlow\n nodes={nodes}\n edges={edges}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n onNodesChange={handleNodesChange}\n onEdgesChange={handleEdgesChange}\n onConnect={editable ? onConnect : undefined}\n onNodeClick={onNodeClickProp}\n onEdgeClick={onEdgeClickProp}\n connectionMode={ConnectionMode.Loose}\n fitView\n fitViewOptions={{\n padding: 0.2,\n maxZoom: isCompactViewport ? 0.9 : 1,\n }}\n minZoom={0.1}\n maxZoom={2}\n defaultEdgeOptions={{\n type: 'workflowTransition',\n animated: false,\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 16,\n height: 16,\n color: '#9ca3af',\n },\n }}\n nodesDraggable={editable}\n nodesConnectable={editable}\n elementsSelectable={editable}\n proOptions={{ hideAttribution: true }}\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={16}\n size={1}\n color={backgroundDotColor}\n />\n\n <Controls\n showZoom={true}\n showFitView={true}\n showInteractive={false}\n position={isCompactViewport ? 'bottom-right' : 'top-right'}\n className={`!bg-card !border-border !shadow-md [&>button]:!bg-card [&>button]:!border-border [&>button]:!fill-foreground [&>button:hover]:!bg-muted ${isCompactViewport ? 'scale-90 origin-bottom-right' : ''}`}\n />\n\n {!isCompactViewport && (\n <MiniMap\n nodeStrokeWidth={3}\n nodeColor={(node) => {\n const status = (node.data?.status || 'not_started') as keyof typeof STATUS_COLORS\n return STATUS_COLORS[status]?.hex || STATUS_COLORS.not_started.hex\n }}\n maskColor=\"rgba(0, 0, 0, 0.1)\"\n position=\"bottom-left\"\n className=\"!bg-card !border !border-border !rounded-lg\"\n />\n )}\n\n {!editable && !isCompactViewport && (\n <Panel position=\"top-left\" style={{ margin: 10 }}>\n <div className=\"bg-card rounded-lg shadow-sm border border-border px-4 py-2\">\n <p className=\"text-sm text-muted-foreground font-medium\">\n {t('workflows.graph.visualization')}\n </p>\n </div>\n </Panel>\n )}\n\n {editable && !isCompactViewport && (\n <Panel position=\"top-left\" style={{ margin: 10 }}>\n <Alert variant=\"info\" icon={<Edit3 aria-hidden=\"true\" />} className=\"max-w-sm\">\n <AlertDescription className=\"font-medium\">\n {t('workflows.graph.editModeInfo')}\n </AlertDescription>\n </Alert>\n </Panel>\n )}\n </ReactFlow>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -15,7 +15,7 @@ import {
15
15
  SelectValue
16
16
  } from "@open-mercato/ui/primitives/select";
17
17
  import { Alert, AlertDescription } from "@open-mercato/ui/primitives/alert";
18
- import { ChevronDown, Plus, Trash2, Info } from "lucide-react";
18
+ import { ChevronDown, Plus, Trash2 } from "lucide-react";
19
19
  function FormFieldArrayEditor({
20
20
  id,
21
21
  value = [],
@@ -96,10 +96,7 @@ function FormFieldArrayEditor({
96
96
  }
97
97
  )
98
98
  ] }),
99
- isJsonSchemaFormat && /* @__PURE__ */ jsxs(Alert, { variant: "default", className: "border-blue-200 bg-blue-50", children: [
100
- /* @__PURE__ */ jsx(Info, { className: "size-4" }),
101
- /* @__PURE__ */ jsx(AlertDescription, { className: "text-xs", children: t("workflows.fieldEditors.formFields.jsonSchemaNotice") })
102
- ] }),
99
+ isJsonSchemaFormat && /* @__PURE__ */ jsx(Alert, { variant: "info", children: /* @__PURE__ */ jsx(AlertDescription, { className: "text-xs", children: t("workflows.fieldEditors.formFields.jsonSchemaNotice") }) }),
103
100
  formFields.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-sm text-muted-foreground bg-muted rounded-lg border", children: t("workflows.fieldEditors.formFields.emptyState") }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: formFields.map((field, index) => {
104
101
  const isExpanded = expandedIndices.has(index);
105
102
  return /* @__PURE__ */ jsxs("div", { className: "border border-gray-200 rounded-lg bg-gray-50", children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/workflows/components/fields/FormFieldArrayEditor.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport { useState } from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { ChevronDown, Plus, Trash2, Info } from 'lucide-react'\nimport type { CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'\n\n/**\n * Form field definition structure\n */\nexport interface FormField {\n name: string\n type: string\n label: string\n required: boolean\n placeholder?: string\n options?: string[]\n defaultValue?: string\n}\n\ninterface FormFieldArrayEditorProps extends CrudCustomFieldRenderProps {\n value: FormField[]\n isJsonSchemaFormat?: boolean\n}\n\n/**\n * FormFieldArrayEditor - Custom field component for managing UserTask form fields\n *\n * Provides an interface to add, edit, and remove form field definitions for user tasks.\n * Supports 12 field types with conditional options for select/radio types.\n *\n * Displays a warning banner if the form was converted from JSON Schema format.\n *\n * Used by NodeEditDialog (UserTask type only)\n */\nexport function FormFieldArrayEditor({\n id,\n value = [],\n error,\n setValue,\n disabled,\n isJsonSchemaFormat = false,\n}: FormFieldArrayEditorProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [expandedIndices, setExpandedIndices] = useState<Set<number>>(new Set())\n\n const formFields = Array.isArray(value) ? value : []\n\n const toggleExpanded = (index: number) => {\n const newExpanded = new Set(expandedIndices)\n if (newExpanded.has(index)) {\n newExpanded.delete(index)\n } else {\n newExpanded.add(index)\n }\n setExpandedIndices(newExpanded)\n }\n\n const addFormField = () => {\n const newField: FormField = {\n name: `field_${Date.now()}`,\n type: 'text',\n label: t('workflows.form.newField'),\n required: false,\n placeholder: '',\n }\n const newFields = [...formFields, newField]\n setValue(newFields)\n\n // Auto-expand the newly added field\n const newExpanded = new Set(expandedIndices)\n newExpanded.add(formFields.length)\n setExpandedIndices(newExpanded)\n }\n\n const removeFormField = async (index: number) => {\n const confirmed = await confirm({\n title: t('workflows.fieldEditors.formFields.removeField'),\n text: t('workflows.fieldEditors.formFields.confirmRemove'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n const newFields = formFields.filter((_, i) => i !== index)\n setValue(newFields)\n\n // Remove from expanded set\n const newExpanded = new Set(expandedIndices)\n newExpanded.delete(index)\n setExpandedIndices(newExpanded)\n }\n\n const updateFormField = (index: number, fieldKey: keyof FormField, fieldValue: any) => {\n const updated = [...formFields]\n updated[index] = { ...updated[index], [fieldKey]: fieldValue }\n setValue(updated)\n }\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <Label className=\"text-sm font-semibold\">{t('workflows.fieldEditors.formFields.title')} ({formFields.length})</Label>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {t('workflows.fieldEditors.formFields.description')}\n </p>\n {error && <p className=\"text-xs text-red-600 mt-1\">{error}</p>}\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={addFormField}\n disabled={disabled}\n className=\"w-full sm:w-auto\"\n >\n <Plus className=\"size-3 mr-1\" />\n {t('workflows.fieldEditors.formFields.addField')}\n </Button>\n </div>\n\n {/* JSON Schema Format Notice */}\n {isJsonSchemaFormat && (\n <Alert variant=\"default\" className=\"border-blue-200 bg-blue-50\">\n <Info className=\"size-4\" />\n <AlertDescription className=\"text-xs\">\n {t('workflows.fieldEditors.formFields.jsonSchemaNotice')}\n </AlertDescription>\n </Alert>\n )}\n\n {formFields.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-muted-foreground bg-muted rounded-lg border\">\n {t('workflows.fieldEditors.formFields.emptyState')}\n </div>\n ) : (\n <div className=\"space-y-2\">\n {formFields.map((field, index) => {\n const isExpanded = expandedIndices.has(index)\n return (\n <div key={index} className=\"border border-gray-200 rounded-lg bg-gray-50\">\n {/* Collapsed Header */}\n <button\n type=\"button\"\n onClick={() => toggleExpanded(index)}\n disabled={disabled}\n className=\"w-full px-4 py-3 text-left flex items-center justify-between hover:bg-gray-100 transition-colors rounded-t-lg disabled:opacity-50\"\n >\n <div className=\"flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-gray-900\">\n {field.label || field.name}\n </span>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {field.type}\n </Badge>\n {field.required && (\n <Badge variant=\"destructive\" className=\"text-xs text-white\">\n {t('workflows.form.required')}\n </Badge>\n )}\n </div>\n <p className=\"text-xs text-gray-600 mt-1\">\n Field name: <code className=\"bg-white px-1 rounded\">{field.name}</code>\n </p>\n </div>\n <ChevronDown\n className={`w-5 h-5 text-gray-400 transition-transform ${isExpanded ? 'rotate-180' : ''}`}\n />\n </button>\n\n {/* Expanded Content */}\n {isExpanded && (\n <div className=\"px-4 pb-4 space-y-3 border-t border-gray-200 bg-white\">\n {/* Field Name */}\n <div className=\"pt-3\">\n <Label htmlFor={`${id}-${index}-name`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.fieldName')} *\n </Label>\n <Input\n id={`${id}-${index}-name`}\n type=\"text\"\n value={field.name}\n onChange={(e) => updateFormField(index, 'name', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.fieldNamePlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('workflows.fieldEditors.formFields.fieldNameHint')}</p>\n </div>\n\n {/* Field Label */}\n <div>\n <Label htmlFor={`${id}-${index}-label`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.fieldLabel')} *\n </Label>\n <Input\n id={`${id}-${index}-label`}\n type=\"text\"\n value={field.label}\n onChange={(e) => updateFormField(index, 'label', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.fieldLabelPlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('workflows.fieldEditors.formFields.fieldLabelHint')}</p>\n </div>\n\n {/* Field Type */}\n <div>\n <Label htmlFor={`${id}-${index}-type`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.fieldType')} *\n </Label>\n <Select\n value={field.type}\n onValueChange={(value) => updateFormField(index, 'type', value)}\n disabled={disabled}\n >\n <SelectTrigger id={`${id}-${index}-type`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"text\">{t('workflows.form.fieldTypes.text')}</SelectItem>\n <SelectItem value=\"number\">{t('workflows.form.fieldTypes.number')}</SelectItem>\n <SelectItem value=\"email\">{t('workflows.form.fieldTypes.email')}</SelectItem>\n <SelectItem value=\"tel\">{t('workflows.form.fieldTypes.tel')}</SelectItem>\n <SelectItem value=\"url\">{t('workflows.form.fieldTypes.url')}</SelectItem>\n <SelectItem value=\"textarea\">{t('workflows.form.fieldTypes.textarea')}</SelectItem>\n <SelectItem value=\"select\">{t('workflows.form.fieldTypes.select')}</SelectItem>\n <SelectItem value=\"radio\">{t('workflows.form.fieldTypes.radio')}</SelectItem>\n <SelectItem value=\"checkbox\">{t('workflows.form.fieldTypes.checkbox')}</SelectItem>\n <SelectItem value=\"date\">{t('workflows.form.fieldTypes.date')}</SelectItem>\n <SelectItem value=\"time\">{t('workflows.form.fieldTypes.time')}</SelectItem>\n <SelectItem value=\"datetime-local\">{t('workflows.form.fieldTypes.datetime-local')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {/* Placeholder */}\n <div>\n <Label htmlFor={`${id}-${index}-placeholder`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.placeholder')}\n </Label>\n <Input\n id={`${id}-${index}-placeholder`}\n type=\"text\"\n value={field.placeholder || ''}\n onChange={(e) => updateFormField(index, 'placeholder', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.placeholderPlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n </div>\n\n {/* Default Value */}\n <div>\n <Label htmlFor={`${id}-${index}-defaultValue`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.defaultValue')}\n </Label>\n <Input\n id={`${id}-${index}-defaultValue`}\n type=\"text\"\n value={field.defaultValue || ''}\n onChange={(e) => updateFormField(index, 'defaultValue', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.defaultValuePlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n </div>\n\n {/* Options (for select/radio) */}\n {(field.type === 'select' || field.type === 'radio') && (\n <div>\n <Label htmlFor={`${id}-${index}-options`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.options')}\n </Label>\n <Input\n id={`${id}-${index}-options`}\n type=\"text\"\n value={field.options?.join(', ') || ''}\n onChange={(e) =>\n updateFormField(\n index,\n 'options',\n e.target.value\n .split(',')\n .map((o) => o.trim())\n .filter(Boolean)\n )\n }\n placeholder={t('workflows.fieldEditors.formFields.optionsPlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('workflows.fieldEditors.formFields.optionsHint')}</p>\n </div>\n )}\n\n {/* Required Checkbox */}\n <div>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id={`${id}-${index}-required`}\n checked={field.required}\n onChange={(e) => updateFormField(index, 'required', e.target.checked)}\n className=\"h-4 w-4 rounded border-gray-300 text-blue-600 focus-visible:ring-ring\"\n disabled={disabled}\n />\n <Label htmlFor={`${id}-${index}-required`} className=\"text-xs font-medium cursor-pointer\">\n {t('workflows.fieldEditors.formFields.requiredField')}\n </Label>\n </div>\n </div>\n\n {/* Delete Button */}\n <div className=\"border-t border-gray-200 pt-3\">\n <Button\n type=\"button\"\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => removeFormField(index)}\n disabled={disabled}\n >\n <Trash2 className=\"size-4 mr-1\" />\n {t('workflows.fieldEditors.formFields.removeField')}\n </Button>\n </div>\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
5
- "mappings": ";AAoHU,SACA,KADA;AAlHV,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,wBAAwB;AACjC,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,OAAO,wBAAwB;AACxC,SAAS,aAAa,MAAM,QAAQ,YAAY;AA+BzC,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,QAAQ,CAAC;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AACvB,GAA8B;AAC5B,QAAM,IAAI,KAAK;AACf,QAAM,EAAE,SAAS,qBAAqB,IAAI,iBAAiB;AAC3D,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAsB,oBAAI,IAAI,CAAC;AAE7E,QAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AAEnD,QAAM,iBAAiB,CAAC,UAAkB;AACxC,UAAM,cAAc,IAAI,IAAI,eAAe;AAC3C,QAAI,YAAY,IAAI,KAAK,GAAG;AAC1B,kBAAY,OAAO,KAAK;AAAA,IAC1B,OAAO;AACL,kBAAY,IAAI,KAAK;AAAA,IACvB;AACA,uBAAmB,WAAW;AAAA,EAChC;AAEA,QAAM,eAAe,MAAM;AACzB,UAAM,WAAsB;AAAA,MAC1B,MAAM,SAAS,KAAK,IAAI,CAAC;AAAA,MACzB,MAAM;AAAA,MACN,OAAO,EAAE,yBAAyB;AAAA,MAClC,UAAU;AAAA,MACV,aAAa;AAAA,IACf;AACA,UAAM,YAAY,CAAC,GAAG,YAAY,QAAQ;AAC1C,aAAS,SAAS;AAGlB,UAAM,cAAc,IAAI,IAAI,eAAe;AAC3C,gBAAY,IAAI,WAAW,MAAM;AACjC,uBAAmB,WAAW;AAAA,EAChC;AAEA,QAAM,kBAAkB,OAAO,UAAkB;AAC/C,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,OAAO,EAAE,+CAA+C;AAAA,MACxD,MAAM,EAAE,iDAAiD;AAAA,MACzD,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,UAAW;AAEhB,UAAM,YAAY,WAAW,OAAO,CAAC,GAAG,MAAM,MAAM,KAAK;AACzD,aAAS,SAAS;AAGlB,UAAM,cAAc,IAAI,IAAI,eAAe;AAC3C,gBAAY,OAAO,KAAK;AACxB,uBAAmB,WAAW;AAAA,EAChC;AAEA,QAAM,kBAAkB,CAAC,OAAe,UAA2B,eAAoB;AACrF,UAAM,UAAU,CAAC,GAAG,UAAU;AAC9B,YAAQ,KAAK,IAAI,EAAE,GAAG,QAAQ,KAAK,GAAG,CAAC,QAAQ,GAAG,WAAW;AAC7D,aAAS,OAAO;AAAA,EAClB;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,sEACb;AAAA,2BAAC,SACC;AAAA,6BAAC,SAAM,WAAU,yBAAyB;AAAA,YAAE,yCAAyC;AAAA,UAAE;AAAA,UAAG,WAAW;AAAA,UAAO;AAAA,WAAC;AAAA,QAC7G,oBAAC,OAAE,WAAU,wCACV,YAAE,+CAA+C,GACpD;AAAA,QACC,SAAS,oBAAC,OAAE,WAAU,6BAA6B,iBAAM;AAAA,SAC5D;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS;AAAA,UACT;AAAA,UACA,WAAU;AAAA,UAEV;AAAA,gCAAC,QAAK,WAAU,eAAc;AAAA,YAC7B,EAAE,4CAA4C;AAAA;AAAA;AAAA,MACjD;AAAA,OACF;AAAA,IAGC,sBACC,qBAAC,SAAM,SAAQ,WAAU,WAAU,8BACjC;AAAA,0BAAC,QAAK,WAAU,UAAS;AAAA,MACzB,oBAAC,oBAAiB,WAAU,WACzB,YAAE,oDAAoD,GACzD;AAAA,OACF;AAAA,IAGD,WAAW,WAAW,IACrB,oBAAC,SAAI,WAAU,4EACZ,YAAE,8CAA8C,GACnD,IAEA,oBAAC,SAAI,WAAU,aACZ,qBAAW,IAAI,CAAC,OAAO,UAAU;AAChC,YAAM,aAAa,gBAAgB,IAAI,KAAK;AAC5C,aACE,qBAAC,SAAgB,WAAU,gDAEzB;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,eAAe,KAAK;AAAA,YACnC;AAAA,YACA,WAAU;AAAA,YAEV;AAAA,mCAAC,SAAI,WAAU,UACb;AAAA,qCAAC,SAAI,WAAU,qCACb;AAAA,sCAAC,UAAK,WAAU,uCACb,gBAAM,SAAS,MAAM,MACxB;AAAA,kBACA,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,gBAAM,MACT;AAAA,kBACC,MAAM,YACL,oBAAC,SAAM,SAAQ,eAAc,WAAU,sBACpC,YAAE,yBAAyB,GAC9B;AAAA,mBAEJ;AAAA,gBACA,qBAAC,OAAE,WAAU,8BAA6B;AAAA;AAAA,kBAC5B,oBAAC,UAAK,WAAU,yBAAyB,gBAAM,MAAK;AAAA,mBAClE;AAAA,iBACF;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAW,8CAA8C,aAAa,eAAe,EAAE;AAAA;AAAA,cACzF;AAAA;AAAA;AAAA,QACF;AAAA,QAGC,cACC,qBAAC,SAAI,WAAU,yDAEb;AAAA,+BAAC,SAAI,WAAU,QACb;AAAA,iCAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,SAAS,WAAU,4BAC9C;AAAA,gBAAE,6CAA6C;AAAA,cAAE;AAAA,eACpD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI,GAAG,EAAE,IAAI,KAAK;AAAA,gBAClB,MAAK;AAAA,gBACL,OAAO,MAAM;AAAA,gBACb,UAAU,CAAC,MAAM,gBAAgB,OAAO,QAAQ,EAAE,OAAO,KAAK;AAAA,gBAC9D,aAAa,EAAE,wDAAwD;AAAA,gBACvE,WAAU;AAAA,gBACV;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,OAAE,WAAU,wCAAwC,YAAE,iDAAiD,GAAE;AAAA,aAC5G;AAAA,UAGA,qBAAC,SACC;AAAA,iCAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,UAAU,WAAU,4BAC/C;AAAA,gBAAE,8CAA8C;AAAA,cAAE;AAAA,eACrD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI,GAAG,EAAE,IAAI,KAAK;AAAA,gBAClB,MAAK;AAAA,gBACL,OAAO,MAAM;AAAA,gBACb,UAAU,CAAC,MAAM,gBAAgB,OAAO,SAAS,EAAE,OAAO,KAAK;AAAA,gBAC/D,aAAa,EAAE,yDAAyD;AAAA,gBACxE,WAAU;AAAA,gBACV;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,OAAE,WAAU,wCAAwC,YAAE,kDAAkD,GAAE;AAAA,aAC7G;AAAA,UAGA,qBAAC,SACC;AAAA,iCAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,SAAS,WAAU,4BAC9C;AAAA,gBAAE,6CAA6C;AAAA,cAAE;AAAA,eACpD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO,MAAM;AAAA,gBACb,eAAe,CAACA,WAAU,gBAAgB,OAAO,QAAQA,MAAK;AAAA,gBAC9D;AAAA,gBAEA;AAAA,sCAAC,iBAAc,IAAI,GAAG,EAAE,IAAI,KAAK,SAC/B,8BAAC,eAAY,GACf;AAAA,kBACA,qBAAC,iBACC;AAAA,wCAAC,cAAW,OAAM,QAAQ,YAAE,gCAAgC,GAAE;AAAA,oBAC9D,oBAAC,cAAW,OAAM,UAAU,YAAE,kCAAkC,GAAE;AAAA,oBAClE,oBAAC,cAAW,OAAM,SAAS,YAAE,iCAAiC,GAAE;AAAA,oBAChE,oBAAC,cAAW,OAAM,OAAO,YAAE,+BAA+B,GAAE;AAAA,oBAC5D,oBAAC,cAAW,OAAM,OAAO,YAAE,+BAA+B,GAAE;AAAA,oBAC5D,oBAAC,cAAW,OAAM,YAAY,YAAE,oCAAoC,GAAE;AAAA,oBACtE,oBAAC,cAAW,OAAM,UAAU,YAAE,kCAAkC,GAAE;AAAA,oBAClE,oBAAC,cAAW,OAAM,SAAS,YAAE,iCAAiC,GAAE;AAAA,oBAChE,oBAAC,cAAW,OAAM,YAAY,YAAE,oCAAoC,GAAE;AAAA,oBACtE,oBAAC,cAAW,OAAM,QAAQ,YAAE,gCAAgC,GAAE;AAAA,oBAC9D,oBAAC,cAAW,OAAM,QAAQ,YAAE,gCAAgC,GAAE;AAAA,oBAC9D,oBAAC,cAAW,OAAM,kBAAkB,YAAE,0CAA0C,GAAE;AAAA,qBACpF;AAAA;AAAA;AAAA,YACF;AAAA,aACF;AAAA,UAGA,qBAAC,SACC;AAAA,gCAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,gBAAgB,WAAU,4BACrD,YAAE,+CAA+C,GACpD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI,GAAG,EAAE,IAAI,KAAK;AAAA,gBAClB,MAAK;AAAA,gBACL,OAAO,MAAM,eAAe;AAAA,gBAC5B,UAAU,CAAC,MAAM,gBAAgB,OAAO,eAAe,EAAE,OAAO,KAAK;AAAA,gBACrE,aAAa,EAAE,0DAA0D;AAAA,gBACzE,WAAU;AAAA,gBACV;AAAA;AAAA,YACF;AAAA,aACF;AAAA,UAGA,qBAAC,SACC;AAAA,gCAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,iBAAiB,WAAU,4BACtD,YAAE,gDAAgD,GACrD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI,GAAG,EAAE,IAAI,KAAK;AAAA,gBAClB,MAAK;AAAA,gBACL,OAAO,MAAM,gBAAgB;AAAA,gBAC7B,UAAU,CAAC,MAAM,gBAAgB,OAAO,gBAAgB,EAAE,OAAO,KAAK;AAAA,gBACtE,aAAa,EAAE,2DAA2D;AAAA,gBAC1E,WAAU;AAAA,gBACV;AAAA;AAAA,YACF;AAAA,aACF;AAAA,WAGE,MAAM,SAAS,YAAY,MAAM,SAAS,YAC1C,qBAAC,SACC;AAAA,gCAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,YAAY,WAAU,4BACjD,YAAE,2CAA2C,GAChD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI,GAAG,EAAE,IAAI,KAAK;AAAA,gBAClB,MAAK;AAAA,gBACL,OAAO,MAAM,SAAS,KAAK,IAAI,KAAK;AAAA,gBACpC,UAAU,CAAC,MACT;AAAA,kBACE;AAAA,kBACA;AAAA,kBACA,EAAE,OAAO,MACN,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAAA,gBACnB;AAAA,gBAEF,aAAa,EAAE,sDAAsD;AAAA,gBACrE,WAAU;AAAA,gBACV;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,OAAE,WAAU,wCAAwC,YAAE,+CAA+C,GAAE;AAAA,aAC1G;AAAA,UAIF,oBAAC,SACC,+BAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,IAAI,GAAG,EAAE,IAAI,KAAK;AAAA,gBAClB,SAAS,MAAM;AAAA,gBACf,UAAU,CAAC,MAAM,gBAAgB,OAAO,YAAY,EAAE,OAAO,OAAO;AAAA,gBACpE,WAAU;AAAA,gBACV;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,SAAM,SAAS,GAAG,EAAE,IAAI,KAAK,aAAa,WAAU,sCAClD,YAAE,iDAAiD,GACtD;AAAA,aACF,GACF;AAAA,UAGA,oBAAC,SAAI,WAAU,iCACb;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS,MAAM,gBAAgB,KAAK;AAAA,cACpC;AAAA,cAEA;AAAA,oCAAC,UAAO,WAAU,eAAc;AAAA,gBAC/B,EAAE,+CAA+C;AAAA;AAAA;AAAA,UACpD,GACF;AAAA,WACF;AAAA,WA5LM,KA8LV;AAAA,IAEJ,CAAC,GACH;AAAA,IAED;AAAA,KACH;AAEJ;",
4
+ "sourcesContent": ["'use client'\n\nimport { useState } from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { ChevronDown, Plus, Trash2 } from 'lucide-react'\nimport type { CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'\n\n/**\n * Form field definition structure\n */\nexport interface FormField {\n name: string\n type: string\n label: string\n required: boolean\n placeholder?: string\n options?: string[]\n defaultValue?: string\n}\n\ninterface FormFieldArrayEditorProps extends CrudCustomFieldRenderProps {\n value: FormField[]\n isJsonSchemaFormat?: boolean\n}\n\n/**\n * FormFieldArrayEditor - Custom field component for managing UserTask form fields\n *\n * Provides an interface to add, edit, and remove form field definitions for user tasks.\n * Supports 12 field types with conditional options for select/radio types.\n *\n * Displays a warning banner if the form was converted from JSON Schema format.\n *\n * Used by NodeEditDialog (UserTask type only)\n */\nexport function FormFieldArrayEditor({\n id,\n value = [],\n error,\n setValue,\n disabled,\n isJsonSchemaFormat = false,\n}: FormFieldArrayEditorProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [expandedIndices, setExpandedIndices] = useState<Set<number>>(new Set())\n\n const formFields = Array.isArray(value) ? value : []\n\n const toggleExpanded = (index: number) => {\n const newExpanded = new Set(expandedIndices)\n if (newExpanded.has(index)) {\n newExpanded.delete(index)\n } else {\n newExpanded.add(index)\n }\n setExpandedIndices(newExpanded)\n }\n\n const addFormField = () => {\n const newField: FormField = {\n name: `field_${Date.now()}`,\n type: 'text',\n label: t('workflows.form.newField'),\n required: false,\n placeholder: '',\n }\n const newFields = [...formFields, newField]\n setValue(newFields)\n\n // Auto-expand the newly added field\n const newExpanded = new Set(expandedIndices)\n newExpanded.add(formFields.length)\n setExpandedIndices(newExpanded)\n }\n\n const removeFormField = async (index: number) => {\n const confirmed = await confirm({\n title: t('workflows.fieldEditors.formFields.removeField'),\n text: t('workflows.fieldEditors.formFields.confirmRemove'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n const newFields = formFields.filter((_, i) => i !== index)\n setValue(newFields)\n\n // Remove from expanded set\n const newExpanded = new Set(expandedIndices)\n newExpanded.delete(index)\n setExpandedIndices(newExpanded)\n }\n\n const updateFormField = (index: number, fieldKey: keyof FormField, fieldValue: any) => {\n const updated = [...formFields]\n updated[index] = { ...updated[index], [fieldKey]: fieldValue }\n setValue(updated)\n }\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <Label className=\"text-sm font-semibold\">{t('workflows.fieldEditors.formFields.title')} ({formFields.length})</Label>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {t('workflows.fieldEditors.formFields.description')}\n </p>\n {error && <p className=\"text-xs text-red-600 mt-1\">{error}</p>}\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={addFormField}\n disabled={disabled}\n className=\"w-full sm:w-auto\"\n >\n <Plus className=\"size-3 mr-1\" />\n {t('workflows.fieldEditors.formFields.addField')}\n </Button>\n </div>\n\n {/* JSON Schema Format Notice */}\n {isJsonSchemaFormat && (\n <Alert variant=\"info\">\n <AlertDescription className=\"text-xs\">\n {t('workflows.fieldEditors.formFields.jsonSchemaNotice')}\n </AlertDescription>\n </Alert>\n )}\n\n {formFields.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-muted-foreground bg-muted rounded-lg border\">\n {t('workflows.fieldEditors.formFields.emptyState')}\n </div>\n ) : (\n <div className=\"space-y-2\">\n {formFields.map((field, index) => {\n const isExpanded = expandedIndices.has(index)\n return (\n <div key={index} className=\"border border-gray-200 rounded-lg bg-gray-50\">\n {/* Collapsed Header */}\n <button\n type=\"button\"\n onClick={() => toggleExpanded(index)}\n disabled={disabled}\n className=\"w-full px-4 py-3 text-left flex items-center justify-between hover:bg-gray-100 transition-colors rounded-t-lg disabled:opacity-50\"\n >\n <div className=\"flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-gray-900\">\n {field.label || field.name}\n </span>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {field.type}\n </Badge>\n {field.required && (\n <Badge variant=\"destructive\" className=\"text-xs text-white\">\n {t('workflows.form.required')}\n </Badge>\n )}\n </div>\n <p className=\"text-xs text-gray-600 mt-1\">\n Field name: <code className=\"bg-white px-1 rounded\">{field.name}</code>\n </p>\n </div>\n <ChevronDown\n className={`w-5 h-5 text-gray-400 transition-transform ${isExpanded ? 'rotate-180' : ''}`}\n />\n </button>\n\n {/* Expanded Content */}\n {isExpanded && (\n <div className=\"px-4 pb-4 space-y-3 border-t border-gray-200 bg-white\">\n {/* Field Name */}\n <div className=\"pt-3\">\n <Label htmlFor={`${id}-${index}-name`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.fieldName')} *\n </Label>\n <Input\n id={`${id}-${index}-name`}\n type=\"text\"\n value={field.name}\n onChange={(e) => updateFormField(index, 'name', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.fieldNamePlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('workflows.fieldEditors.formFields.fieldNameHint')}</p>\n </div>\n\n {/* Field Label */}\n <div>\n <Label htmlFor={`${id}-${index}-label`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.fieldLabel')} *\n </Label>\n <Input\n id={`${id}-${index}-label`}\n type=\"text\"\n value={field.label}\n onChange={(e) => updateFormField(index, 'label', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.fieldLabelPlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('workflows.fieldEditors.formFields.fieldLabelHint')}</p>\n </div>\n\n {/* Field Type */}\n <div>\n <Label htmlFor={`${id}-${index}-type`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.fieldType')} *\n </Label>\n <Select\n value={field.type}\n onValueChange={(value) => updateFormField(index, 'type', value)}\n disabled={disabled}\n >\n <SelectTrigger id={`${id}-${index}-type`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"text\">{t('workflows.form.fieldTypes.text')}</SelectItem>\n <SelectItem value=\"number\">{t('workflows.form.fieldTypes.number')}</SelectItem>\n <SelectItem value=\"email\">{t('workflows.form.fieldTypes.email')}</SelectItem>\n <SelectItem value=\"tel\">{t('workflows.form.fieldTypes.tel')}</SelectItem>\n <SelectItem value=\"url\">{t('workflows.form.fieldTypes.url')}</SelectItem>\n <SelectItem value=\"textarea\">{t('workflows.form.fieldTypes.textarea')}</SelectItem>\n <SelectItem value=\"select\">{t('workflows.form.fieldTypes.select')}</SelectItem>\n <SelectItem value=\"radio\">{t('workflows.form.fieldTypes.radio')}</SelectItem>\n <SelectItem value=\"checkbox\">{t('workflows.form.fieldTypes.checkbox')}</SelectItem>\n <SelectItem value=\"date\">{t('workflows.form.fieldTypes.date')}</SelectItem>\n <SelectItem value=\"time\">{t('workflows.form.fieldTypes.time')}</SelectItem>\n <SelectItem value=\"datetime-local\">{t('workflows.form.fieldTypes.datetime-local')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {/* Placeholder */}\n <div>\n <Label htmlFor={`${id}-${index}-placeholder`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.placeholder')}\n </Label>\n <Input\n id={`${id}-${index}-placeholder`}\n type=\"text\"\n value={field.placeholder || ''}\n onChange={(e) => updateFormField(index, 'placeholder', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.placeholderPlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n </div>\n\n {/* Default Value */}\n <div>\n <Label htmlFor={`${id}-${index}-defaultValue`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.defaultValue')}\n </Label>\n <Input\n id={`${id}-${index}-defaultValue`}\n type=\"text\"\n value={field.defaultValue || ''}\n onChange={(e) => updateFormField(index, 'defaultValue', e.target.value)}\n placeholder={t('workflows.fieldEditors.formFields.defaultValuePlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n </div>\n\n {/* Options (for select/radio) */}\n {(field.type === 'select' || field.type === 'radio') && (\n <div>\n <Label htmlFor={`${id}-${index}-options`} className=\"text-xs font-medium mb-1\">\n {t('workflows.fieldEditors.formFields.options')}\n </Label>\n <Input\n id={`${id}-${index}-options`}\n type=\"text\"\n value={field.options?.join(', ') || ''}\n onChange={(e) =>\n updateFormField(\n index,\n 'options',\n e.target.value\n .split(',')\n .map((o) => o.trim())\n .filter(Boolean)\n )\n }\n placeholder={t('workflows.fieldEditors.formFields.optionsPlaceholder')}\n className=\"text-xs\"\n disabled={disabled}\n />\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('workflows.fieldEditors.formFields.optionsHint')}</p>\n </div>\n )}\n\n {/* Required Checkbox */}\n <div>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id={`${id}-${index}-required`}\n checked={field.required}\n onChange={(e) => updateFormField(index, 'required', e.target.checked)}\n className=\"h-4 w-4 rounded border-gray-300 text-blue-600 focus-visible:ring-ring\"\n disabled={disabled}\n />\n <Label htmlFor={`${id}-${index}-required`} className=\"text-xs font-medium cursor-pointer\">\n {t('workflows.fieldEditors.formFields.requiredField')}\n </Label>\n </div>\n </div>\n\n {/* Delete Button */}\n <div className=\"border-t border-gray-200 pt-3\">\n <Button\n type=\"button\"\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => removeFormField(index)}\n disabled={disabled}\n >\n <Trash2 className=\"size-4 mr-1\" />\n {t('workflows.fieldEditors.formFields.removeField')}\n </Button>\n </div>\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["value"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.5-develop.5382.1.f542de69af",
3
+ "version": "0.6.6-develop.5412.1.e2a52b14f0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -245,16 +245,16 @@
245
245
  "zod": "^4.4.3"
246
246
  },
247
247
  "peerDependencies": {
248
- "@open-mercato/ai-assistant": "0.6.5-develop.5382.1.f542de69af",
249
- "@open-mercato/shared": "0.6.5-develop.5382.1.f542de69af",
250
- "@open-mercato/ui": "0.6.5-develop.5382.1.f542de69af",
248
+ "@open-mercato/ai-assistant": "0.6.6-develop.5412.1.e2a52b14f0",
249
+ "@open-mercato/shared": "0.6.6-develop.5412.1.e2a52b14f0",
250
+ "@open-mercato/ui": "0.6.6-develop.5412.1.e2a52b14f0",
251
251
  "react": "^19.0.0",
252
252
  "react-dom": "^19.0.0"
253
253
  },
254
254
  "devDependencies": {
255
- "@open-mercato/ai-assistant": "0.6.5-develop.5382.1.f542de69af",
256
- "@open-mercato/shared": "0.6.5-develop.5382.1.f542de69af",
257
- "@open-mercato/ui": "0.6.5-develop.5382.1.f542de69af",
255
+ "@open-mercato/ai-assistant": "0.6.6-develop.5412.1.e2a52b14f0",
256
+ "@open-mercato/shared": "0.6.6-develop.5412.1.e2a52b14f0",
257
+ "@open-mercato/ui": "0.6.6-develop.5412.1.e2a52b14f0",
258
258
  "@testing-library/dom": "^10.4.1",
259
259
  "@testing-library/jest-dom": "^6.9.1",
260
260
  "@testing-library/react": "^16.3.1",
@@ -280,5 +280,5 @@
280
280
  "url": "https://github.com/open-mercato/open-mercato",
281
281
  "directory": "packages/core"
282
282
  },
283
- "stableVersion": "0.6.4"
283
+ "stableVersion": "0.6.5"
284
284
  }
@@ -71,7 +71,23 @@ export async function createPersonFixture(
71
71
  export async function createDealFixture(
72
72
  request: APIRequestContext,
73
73
  token: string,
74
- input: { title: string; companyIds?: string[]; personIds?: string[]; pipelineId?: string; pipelineStageId?: string; valueAmount?: number; valueCurrency?: string },
74
+ input: {
75
+ title: string;
76
+ companyIds?: string[];
77
+ personIds?: string[];
78
+ pipelineId?: string;
79
+ pipelineStageId?: string;
80
+ valueAmount?: number;
81
+ valueCurrency?: string;
82
+ // Optional deal lifecycle fields — all valid on `dealCreateSchema`. Forwarded so KPI/summary
83
+ // tests can seed won/lost/overdue/owned deals across quarters (see TC-CRM-082). `status` is a
84
+ // free-form dictionary value (e.g. 'open', 'in_progress', 'win', 'loose'); `expectedCloseAt`
85
+ // accepts an ISO string (the schema coerces it to a Date); `closureOutcome` is 'won' | 'lost'.
86
+ status?: string;
87
+ expectedCloseAt?: string;
88
+ ownerUserId?: string;
89
+ closureOutcome?: 'won' | 'lost';
90
+ },
75
91
  ): Promise<string> {
76
92
  const data: Record<string, unknown> = { title: input.title };
77
93
  if (input.companyIds?.length) data.companyIds = input.companyIds;
@@ -80,6 +96,10 @@ export async function createDealFixture(
80
96
  if (input.pipelineStageId) data.pipelineStageId = input.pipelineStageId;
81
97
  if (input.valueAmount !== undefined) data.valueAmount = input.valueAmount;
82
98
  if (input.valueCurrency) data.valueCurrency = input.valueCurrency;
99
+ if (input.status) data.status = input.status;
100
+ if (input.expectedCloseAt) data.expectedCloseAt = input.expectedCloseAt;
101
+ if (input.ownerUserId) data.ownerUserId = input.ownerUserId;
102
+ if (input.closureOutcome) data.closureOutcome = input.closureOutcome;
83
103
  return createEntity(request, token, '/api/customers/deals', data, ['dealId', 'id', 'entityId']);
84
104
  }
85
105
 
@@ -0,0 +1,79 @@
1
+ # Attachments Module — Agent Guidelines
2
+
3
+ The `attachments` module owns file uploads, storage drivers, partitions, OCR, and
4
+ the `attachments` table. Every attachment row carries a `tenant_id` /
5
+ `organization_id` scope pair that governs cross-tenant access.
6
+
7
+ ## Scope & Access Policy
8
+
9
+ `attachments.tenant_id` and `attachments.organization_id` are **nullable** at the
10
+ DB level, but only two scope shapes are valid:
11
+
12
+ | Shape | `tenant_id` | `organization_id` | Meaning | Who can read it |
13
+ |-------|-------------|-------------------|---------|-----------------|
14
+ | **Scoped** | set | set | Belongs to one tenant + org | Same-scope principals + superadmin |
15
+ | **Global** | null | null | Legacy "global attachment" | Any authenticated principal (and unauthenticated only on a `is_public` partition) |
16
+ | **Partial-null** ❌ | set / null | null / set | **Invalid — never create** | Nobody (fail-closed) except superadmin |
17
+
18
+ The "both-or-neither" rule is the legitimate-unscoped use case referenced by
19
+ [#2109](https://github.com/open-mercato/open-mercato/issues/2109): a fully-global
20
+ (both-null) attachment is intentionally supported by `isSameScope`, so the columns
21
+ cannot simply be made `NOT NULL` without breaking that semantic or backfilling a
22
+ sentinel tenant onto legacy rows.
23
+
24
+ ### Why partial-null is dangerous
25
+
26
+ `isSameScope` (`lib/access.ts`) deliberately **fails closed** on partial-null rows
27
+ (#2107): a row with one scope column set and the other null matches no principal's
28
+ auth and is unreadable by everyone except a superadmin. Such a row is therefore
29
+ *dead data* — it can only ever leak through a future code path that reads or
30
+ exports attachments **without** going through `checkAttachmentAccess` (a new export
31
+ endpoint, webhook delivery, OCR worker, or migration backfill). That is exactly the
32
+ fail-open class the access fix closed at read time; the creation guard closes it at
33
+ write time.
34
+
35
+ ## Always
36
+
37
+ - **MUST call `assertAttachmentScopeInvariant({ tenantId, organizationId })` from
38
+ `lib/access.ts` before persisting any new `Attachment` row.** It throws on a
39
+ partial-null scope and accepts both fully-scoped and fully-global rows. The
40
+ attachments upload route (`api/route.ts`) already guards its creation site.
41
+ - **MUST gate every attachment read through `checkAttachmentAccess`** (`lib/access.ts`)
42
+ so tenant scoping and partition visibility are enforced consistently.
43
+ - When copying/cloning attachments across records, **carry the source row's scope
44
+ pair as a unit** (both columns together) rather than overriding one column with a
45
+ possibly-null value.
46
+
47
+ ## Never
48
+
49
+ - **Never create a partial-null attachment** (one scope column set, the other null).
50
+ - **Never read or expose attachment rows without `checkAttachmentAccess`** — bypassing
51
+ it reintroduces the cross-tenant fail-open class.
52
+
53
+ ## Known cross-module creation paths
54
+
55
+ These paths create `Attachment` rows from other modules and must preserve the
56
+ both-or-neither invariant (audited for #2109):
57
+
58
+ - `packages/core/src/modules/attachments/api/route.ts` — primary upload; scope comes
59
+ from authenticated request context (both set). **Guarded.**
60
+ - `packages/core/src/modules/sync_excel/lib/upload-storage.ts` — both scopes are
61
+ required inputs (type-enforced). Safe.
62
+ - `packages/core/src/modules/catalog/seed/examples.ts` — both scopes required on
63
+ `SeedScope`. Safe.
64
+ - `packages/core/src/modules/catalog/commands/variants.ts` — clones variant media to
65
+ the product; inherits the source/variant scope pair (`?? null` only collapses to
66
+ the global both-null shape).
67
+ - `packages/core/src/modules/messages/lib/attachments.ts`
68
+ (`copyAttachmentsForForwardMessages`) — copies forwarded message attachments and
69
+ accepts a nullable `targetOrganizationId` with a non-null `tenantId`. This is the
70
+ one path that can construct a partial-null row; copy the **source attachment's**
71
+ scope pair when wiring new callers, and apply the creation guard if this path is
72
+ refactored.
73
+
74
+ ## Validation Commands
75
+
76
+ ```bash
77
+ yarn workspace @open-mercato/core test -- access
78
+ yarn workspace @open-mercato/core build
79
+ ```
@@ -12,6 +12,7 @@ import { requestOcrProcessing } from '../lib/ocrQueue'
12
12
  import { StorageDriverFactory } from '../lib/drivers'
13
13
  import { OcrService, shouldUseLlmOcr } from '../lib/ocrService'
14
14
  import { clearAttachmentThumbnailCache } from '../lib/thumbnailCache'
15
+ import { assertAttachmentScopeInvariant } from '../lib/access'
15
16
  import {
16
17
  mergeAttachmentMetadata,
17
18
  normalizeAttachmentAssignments,
@@ -421,6 +422,7 @@ export async function POST(req: Request) {
421
422
  }
422
423
  const metadata = mergeAttachmentMetadata(null, { assignments, tags })
423
424
  const attachmentId = randomUUID()
425
+ assertAttachmentScopeInvariant({ tenantId: auth.tenantId, organizationId: auth.orgId })
424
426
  const att = em.create(Attachment, {
425
427
  id: attachmentId,
426
428
  entityId,