@open-mercato/core 0.4.2-canary-5d2c419a9b → 0.4.2-canary-9e0237de8e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/entities/workflow_event_trigger/index.js +33 -0
- package/dist/generated/entities/workflow_event_trigger/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/auth/events.js +30 -0
- package/dist/modules/auth/events.js.map +7 -0
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js +145 -0
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +7 -0
- package/dist/modules/business_rules/data/validators.js +34 -0
- package/dist/modules/business_rules/data/validators.js.map +2 -2
- package/dist/modules/business_rules/index.js +21 -1
- package/dist/modules/business_rules/index.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +182 -1
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/catalog/events.js +34 -0
- package/dist/modules/catalog/events.js.map +7 -0
- package/dist/modules/customers/events.js +49 -0
- package/dist/modules/customers/events.js.map +7 -0
- package/dist/modules/directory/events.js +23 -0
- package/dist/modules/directory/events.js.map +7 -0
- package/dist/modules/sales/acl.js +1 -0
- package/dist/modules/sales/acl.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +12 -0
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +62 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/events.js +63 -0
- package/dist/modules/sales/events.js.map +7 -0
- package/dist/modules/sales/lib/dictionaries.js +3 -0
- package/dist/modules/sales/lib/dictionaries.js.map +2 -2
- package/dist/modules/sales/lib/frontend/documentDataEvents.js +25 -0
- package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +7 -0
- package/dist/modules/workflows/acl.js +2 -0
- package/dist/modules/workflows/acl.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +18 -6
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/route.js +6 -1
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +9 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +24 -15
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +150 -132
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +5 -6
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/cli.js +81 -3
- package/dist/modules/workflows/cli.js.map +3 -3
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +481 -0
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +7 -0
- package/dist/modules/workflows/components/EventTriggersEditor.js +553 -0
- package/dist/modules/workflows/components/EventTriggersEditor.js.map +7 -0
- package/dist/modules/workflows/data/entities.js +64 -1
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +115 -0
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/events.js +38 -0
- package/dist/modules/workflows/events.js.map +7 -0
- package/dist/modules/workflows/examples/checkout-demo-definition.json +1 -5
- package/dist/modules/workflows/examples/order-approval-definition.json +257 -0
- package/dist/modules/workflows/examples/order-approval-guard-rules.json +32 -0
- package/dist/modules/workflows/lib/activity-executor.js +75 -13
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +308 -0
- package/dist/modules/workflows/lib/event-trigger-service.js.map +7 -0
- package/dist/modules/workflows/lib/graph-utils.js +71 -2
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +17 -4
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +33 -23
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +157 -45
- package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
- package/dist/modules/workflows/migrations/Migration20260123143500.js +36 -0
- package/dist/modules/workflows/migrations/Migration20260123143500.js.map +7 -0
- package/dist/modules/workflows/subscribers/event-trigger.js +78 -0
- package/dist/modules/workflows/subscribers/event-trigger.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +323 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js +17 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +7 -0
- package/dist/modules/workflows/widgets/injection-table.js +19 -0
- package/dist/modules/workflows/widgets/injection-table.js.map +7 -0
- package/generated/entities/workflow_event_trigger/index.ts +15 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +2 -2
- package/src/modules/auth/events.ts +39 -0
- package/src/modules/business_rules/api/execute/[ruleId]/route.ts +163 -0
- package/src/modules/business_rules/data/validators.ts +40 -0
- package/src/modules/business_rules/index.ts +25 -0
- package/src/modules/business_rules/lib/rule-engine.ts +281 -1
- package/src/modules/catalog/events.ts +45 -0
- package/src/modules/customers/events.ts +63 -0
- package/src/modules/directory/events.ts +31 -0
- package/src/modules/sales/acl.ts +1 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +16 -0
- package/src/modules/sales/commands/documents.ts +75 -1
- package/src/modules/sales/events.ts +82 -0
- package/src/modules/sales/lib/dictionaries.ts +3 -0
- package/src/modules/sales/lib/frontend/documentDataEvents.ts +28 -0
- package/src/modules/workflows/acl.ts +2 -0
- package/src/modules/workflows/api/__tests__/instances.route.test.ts +5 -2
- package/src/modules/workflows/api/instances/route.ts +21 -7
- package/src/modules/workflows/api/tasks/route.ts +7 -1
- package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +9 -0
- package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/create/page.tsx +9 -0
- package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +21 -3
- package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/tasks/page.tsx +5 -6
- package/src/modules/workflows/cli.ts +111 -0
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +581 -0
- package/src/modules/workflows/components/EventTriggersEditor.tsx +664 -0
- package/src/modules/workflows/data/entities.ts +124 -0
- package/src/modules/workflows/data/validators.ts +138 -0
- package/src/modules/workflows/events.ts +49 -0
- package/src/modules/workflows/examples/checkout-demo-definition.json +1 -5
- package/src/modules/workflows/examples/order-approval-definition.json +257 -0
- package/src/modules/workflows/examples/order-approval-guard-rules.json +32 -0
- package/src/modules/workflows/i18n/en.json +71 -0
- package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +43 -36
- package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +170 -90
- package/src/modules/workflows/lib/activity-executor.ts +129 -16
- package/src/modules/workflows/lib/event-trigger-service.ts +557 -0
- package/src/modules/workflows/lib/graph-utils.ts +117 -2
- package/src/modules/workflows/lib/seeds.ts +29 -8
- package/src/modules/workflows/lib/start-validator.ts +38 -28
- package/src/modules/workflows/lib/transition-handler.ts +208 -55
- package/src/modules/workflows/migrations/Migration20260123143500.ts +38 -0
- package/src/modules/workflows/subscribers/event-trigger.ts +109 -0
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +446 -0
- package/src/modules/workflows/widgets/injection/order-approval/widget.ts +16 -0
- package/src/modules/workflows/widgets/injection-table.ts +21 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260123143500 extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`
|
|
5
|
+
create table "workflow_event_triggers" (
|
|
6
|
+
"id" uuid not null default gen_random_uuid(),
|
|
7
|
+
"name" varchar(255) not null,
|
|
8
|
+
"description" text null,
|
|
9
|
+
"workflow_definition_id" uuid not null,
|
|
10
|
+
"event_pattern" varchar(255) not null,
|
|
11
|
+
"config" jsonb null,
|
|
12
|
+
"enabled" bool not null default true,
|
|
13
|
+
"priority" int4 not null default 0,
|
|
14
|
+
"tenant_id" uuid not null,
|
|
15
|
+
"organization_id" uuid not null,
|
|
16
|
+
"created_by" varchar(255) null,
|
|
17
|
+
"updated_by" varchar(255) null,
|
|
18
|
+
"created_at" timestamptz(6) not null,
|
|
19
|
+
"updated_at" timestamptz(6) not null,
|
|
20
|
+
"deleted_at" timestamptz(6) null,
|
|
21
|
+
constraint "workflow_event_triggers_pkey" primary key ("id")
|
|
22
|
+
);
|
|
23
|
+
`);
|
|
24
|
+
this.addSql(`create index "workflow_event_triggers_event_pattern_idx" on "workflow_event_triggers" ("event_pattern", "enabled");`);
|
|
25
|
+
this.addSql(`create index "workflow_event_triggers_definition_idx" on "workflow_event_triggers" ("workflow_definition_id");`);
|
|
26
|
+
this.addSql(`create index "workflow_event_triggers_tenant_org_idx" on "workflow_event_triggers" ("tenant_id", "organization_id");`);
|
|
27
|
+
this.addSql(`create index "workflow_event_triggers_enabled_priority_idx" on "workflow_event_triggers" ("enabled", "priority");`);
|
|
28
|
+
}
|
|
29
|
+
async down() {
|
|
30
|
+
this.addSql(`drop table if exists "workflow_event_triggers" cascade;`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
Migration20260123143500
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=Migration20260123143500.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/workflows/migrations/Migration20260123143500.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260123143500 extends Migration {\n\n override async up(): Promise<void> {\n // Create workflow_event_triggers table\n this.addSql(`\n create table \"workflow_event_triggers\" (\n \"id\" uuid not null default gen_random_uuid(),\n \"name\" varchar(255) not null,\n \"description\" text null,\n \"workflow_definition_id\" uuid not null,\n \"event_pattern\" varchar(255) not null,\n \"config\" jsonb null,\n \"enabled\" bool not null default true,\n \"priority\" int4 not null default 0,\n \"tenant_id\" uuid not null,\n \"organization_id\" uuid not null,\n \"created_by\" varchar(255) null,\n \"updated_by\" varchar(255) null,\n \"created_at\" timestamptz(6) not null,\n \"updated_at\" timestamptz(6) not null,\n \"deleted_at\" timestamptz(6) null,\n constraint \"workflow_event_triggers_pkey\" primary key (\"id\")\n );\n `);\n\n // Create indexes\n this.addSql(`create index \"workflow_event_triggers_event_pattern_idx\" on \"workflow_event_triggers\" (\"event_pattern\", \"enabled\");`);\n this.addSql(`create index \"workflow_event_triggers_definition_idx\" on \"workflow_event_triggers\" (\"workflow_definition_id\");`);\n this.addSql(`create index \"workflow_event_triggers_tenant_org_idx\" on \"workflow_event_triggers\" (\"tenant_id\", \"organization_id\");`);\n this.addSql(`create index \"workflow_event_triggers_enabled_priority_idx\" on \"workflow_event_triggers\" (\"enabled\", \"priority\");`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop table if exists \"workflow_event_triggers\" cascade;`);\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EAErD,MAAe,KAAoB;AAEjC,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAmBX;AAGD,SAAK,OAAO,qHAAqH;AACjI,SAAK,OAAO,gHAAgH;AAC5H,SAAK,OAAO,sHAAsH;AAClI,SAAK,OAAO,mHAAmH;AAAA,EACjI;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,yDAAyD;AAAA,EACvE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
event: "*",
|
|
3
|
+
// Subscribe to ALL events
|
|
4
|
+
persistent: true,
|
|
5
|
+
// Ensure reliability
|
|
6
|
+
id: "workflows:event-trigger"
|
|
7
|
+
};
|
|
8
|
+
const EXCLUDED_EVENT_PREFIXES = [
|
|
9
|
+
"query_index.",
|
|
10
|
+
// Internal indexing events
|
|
11
|
+
"search.",
|
|
12
|
+
// Internal search events
|
|
13
|
+
"workflows.",
|
|
14
|
+
// Workflow internal events (avoid recursion)
|
|
15
|
+
"cache.",
|
|
16
|
+
// Cache events
|
|
17
|
+
"queue."
|
|
18
|
+
// Queue events
|
|
19
|
+
];
|
|
20
|
+
function isExcludedEvent(eventName) {
|
|
21
|
+
return EXCLUDED_EVENT_PREFIXES.some((prefix) => eventName.startsWith(prefix));
|
|
22
|
+
}
|
|
23
|
+
async function handle(payload, ctx) {
|
|
24
|
+
const eventName = ctx.eventName;
|
|
25
|
+
if (!eventName) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (isExcludedEvent(eventName)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const eventPayload = payload && typeof payload === "object" ? payload : {};
|
|
32
|
+
const tenantId = eventPayload?.tenantId;
|
|
33
|
+
const organizationId = eventPayload?.organizationId;
|
|
34
|
+
if (!tenantId || !organizationId) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let em;
|
|
38
|
+
let container;
|
|
39
|
+
try {
|
|
40
|
+
em = ctx.resolve("em");
|
|
41
|
+
container = {
|
|
42
|
+
resolve: ctx.resolve,
|
|
43
|
+
// Provide minimal AwilixContainer interface for type compatibility
|
|
44
|
+
cradle: new Proxy({}, {
|
|
45
|
+
get: (_target, prop) => ctx.resolve(prop)
|
|
46
|
+
})
|
|
47
|
+
};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn(`[workflow-trigger] Cannot resolve dependencies for event "${eventName}":`, error);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const { processEventTriggers } = await import("../lib/event-trigger-service.js");
|
|
53
|
+
try {
|
|
54
|
+
const result = await processEventTriggers(em, container, {
|
|
55
|
+
eventName,
|
|
56
|
+
payload: eventPayload,
|
|
57
|
+
tenantId,
|
|
58
|
+
organizationId
|
|
59
|
+
});
|
|
60
|
+
if (result.triggered > 0) {
|
|
61
|
+
console.log(
|
|
62
|
+
`[workflow-trigger] Triggered ${result.triggered} workflow(s) for "${eventName}"` + (result.skipped > 0 ? ` (${result.skipped} skipped)` : "") + (result.errors.length > 0 ? ` (${result.errors.length} errors)` : "")
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (result.errors.length > 0) {
|
|
66
|
+
for (const err of result.errors) {
|
|
67
|
+
console.error(`[workflow-trigger] Trigger ${err.triggerId} failed:`, err.error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`[workflow-trigger] Error processing triggers for "${eventName}":`, error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
handle as default,
|
|
76
|
+
metadata
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=event-trigger.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/workflows/subscribers/event-trigger.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module - Event Trigger Subscriber\n *\n * Wildcard subscriber that listens to all events and evaluates\n * workflow event triggers. When a matching trigger is found,\n * the corresponding workflow is started with mapped context.\n */\n\nimport type { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\n\nexport const metadata = {\n event: '*', // Subscribe to ALL events\n persistent: true, // Ensure reliability\n id: 'workflows:event-trigger',\n}\n\n// Events that should never trigger workflows (internal/system events)\nconst EXCLUDED_EVENT_PREFIXES = [\n 'query_index.', // Internal indexing events\n 'search.', // Internal search events\n 'workflows.', // Workflow internal events (avoid recursion)\n 'cache.', // Cache events\n 'queue.', // Queue events\n]\n\n/**\n * Check if an event should be excluded from trigger processing.\n */\nfunction isExcludedEvent(eventName: string): boolean {\n return EXCLUDED_EVENT_PREFIXES.some(prefix => eventName.startsWith(prefix))\n}\n\nexport default async function handle(\n payload: unknown,\n ctx: { resolve: <T = unknown>(name: string) => T; eventName?: string }\n): Promise<void> {\n const eventName = ctx.eventName\n if (!eventName) {\n // Skip if no event name (shouldn't happen, but be safe)\n return\n }\n\n // Skip excluded events\n if (isExcludedEvent(eventName)) {\n return\n }\n\n // Ensure payload is an object\n const eventPayload = (payload && typeof payload === 'object' ? payload : {}) as Record<string, unknown>\n\n // Extract tenant/org from payload\n const tenantId = eventPayload?.tenantId as string | undefined\n const organizationId = eventPayload?.organizationId as string | undefined\n\n // Skip events without tenant context\n if (!tenantId || !organizationId) {\n return\n }\n\n // Get dependencies from container\n let em: EntityManager\n let container: AwilixContainer\n\n try {\n em = ctx.resolve<EntityManager>('em')\n // Create a minimal container wrapper using the resolve function\n // This avoids the need to register 'container' as a self-reference in DI\n container = {\n resolve: ctx.resolve,\n // Provide minimal AwilixContainer interface for type compatibility\n cradle: new Proxy({}, {\n get: (_target, prop: string) => ctx.resolve(prop),\n }),\n } as unknown as AwilixContainer\n } catch (error) {\n // DI not available - skip\n console.warn(`[workflow-trigger] Cannot resolve dependencies for event \"${eventName}\":`, error)\n return\n }\n\n // Import service dynamically to avoid circular dependencies\n const { processEventTriggers } = await import('../lib/event-trigger-service')\n\n try {\n const result = await processEventTriggers(em, container, {\n eventName,\n payload: eventPayload,\n tenantId,\n organizationId,\n })\n\n if (result.triggered > 0) {\n console.log(\n `[workflow-trigger] Triggered ${result.triggered} workflow(s) for \"${eventName}\"` +\n (result.skipped > 0 ? ` (${result.skipped} skipped)` : '') +\n (result.errors.length > 0 ? ` (${result.errors.length} errors)` : '')\n )\n }\n\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n console.error(`[workflow-trigger] Trigger ${err.triggerId} failed:`, err.error)\n }\n }\n } catch (error) {\n console.error(`[workflow-trigger] Error processing triggers for \"${eventName}\":`, error)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAWO,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA;AAAA,EACP,YAAY;AAAA;AAAA,EACZ,IAAI;AACN;AAGA,MAAM,0BAA0B;AAAA,EAC9B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAKA,SAAS,gBAAgB,WAA4B;AACnD,SAAO,wBAAwB,KAAK,YAAU,UAAU,WAAW,MAAM,CAAC;AAC5E;AAEA,eAAO,OACL,SACA,KACe;AACf,QAAM,YAAY,IAAI;AACtB,MAAI,CAAC,WAAW;AAEd;AAAA,EACF;AAGA,MAAI,gBAAgB,SAAS,GAAG;AAC9B;AAAA,EACF;AAGA,QAAM,eAAgB,WAAW,OAAO,YAAY,WAAW,UAAU,CAAC;AAG1E,QAAM,WAAW,cAAc;AAC/B,QAAM,iBAAiB,cAAc;AAGrC,MAAI,CAAC,YAAY,CAAC,gBAAgB;AAChC;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,SAAK,IAAI,QAAuB,IAAI;AAGpC,gBAAY;AAAA,MACV,SAAS,IAAI;AAAA;AAAA,MAEb,QAAQ,IAAI,MAAM,CAAC,GAAG;AAAA,QACpB,KAAK,CAAC,SAAS,SAAiB,IAAI,QAAQ,IAAI;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF,SAAS,OAAO;AAEd,YAAQ,KAAK,6DAA6D,SAAS,MAAM,KAAK;AAC9F;AAAA,EACF;AAGA,QAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,8BAA8B;AAE5E,MAAI;AACF,UAAM,SAAS,MAAM,qBAAqB,IAAI,WAAW;AAAA,MACvD;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,OAAO,YAAY,GAAG;AACxB,cAAQ;AAAA,QACN,gCAAgC,OAAO,SAAS,qBAAqB,SAAS,OAC7E,OAAO,UAAU,IAAI,KAAK,OAAO,OAAO,cAAc,OACtD,OAAO,OAAO,SAAS,IAAI,KAAK,OAAO,OAAO,MAAM,aAAa;AAAA,MACpE;AAAA,IACF;AAEA,QAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,iBAAW,OAAO,OAAO,QAAQ;AAC/B,gBAAQ,MAAM,8BAA8B,IAAI,SAAS,YAAY,IAAI,KAAK;AAAA,MAChF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,qDAAqD,SAAS,MAAM,KAAK;AAAA,EACzF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
7
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
8
|
+
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
9
|
+
import { Textarea } from "@open-mercato/ui/primitives/textarea";
|
|
10
|
+
import { Badge } from "@open-mercato/ui/primitives/badge";
|
|
11
|
+
import { emitSalesDocumentDataRefresh } from "@open-mercato/core/modules/sales/lib/frontend/documentDataEvents";
|
|
12
|
+
const WORKFLOW_ID = "sales_order_approval_v1";
|
|
13
|
+
function OrderApprovalWidget({ data }) {
|
|
14
|
+
const t = useT();
|
|
15
|
+
const queryClient = useQueryClient();
|
|
16
|
+
const orderId = data?.id;
|
|
17
|
+
const [decision, setDecision] = React.useState("");
|
|
18
|
+
const [comments, setComments] = React.useState("");
|
|
19
|
+
const [error, setError] = React.useState(null);
|
|
20
|
+
const [isWaitingForProcessing, setIsWaitingForProcessing] = React.useState(false);
|
|
21
|
+
const { data: orderData } = useQuery({
|
|
22
|
+
queryKey: ["order-status", orderId],
|
|
23
|
+
queryFn: async () => {
|
|
24
|
+
if (!orderId) return null;
|
|
25
|
+
const result = await apiCall(
|
|
26
|
+
`/api/sales/orders?id=${orderId}&pageSize=1`
|
|
27
|
+
);
|
|
28
|
+
return result.ok && result.result?.items?.[0] ? result.result.items[0] : null;
|
|
29
|
+
},
|
|
30
|
+
enabled: Boolean(orderId),
|
|
31
|
+
staleTime: 5e3,
|
|
32
|
+
// Poll when waiting for processing to detect status change
|
|
33
|
+
refetchInterval: isWaitingForProcessing ? 2e3 : false
|
|
34
|
+
});
|
|
35
|
+
const currentOrderStatus = orderData?.status || data?.status;
|
|
36
|
+
const { data: instancesData, isLoading: instancesLoading } = useQuery({
|
|
37
|
+
queryKey: ["workflow-instances", orderId],
|
|
38
|
+
queryFn: async () => {
|
|
39
|
+
if (!orderId) return { data: [] };
|
|
40
|
+
const result = await apiCall(
|
|
41
|
+
`/api/workflows/instances?entityId=${orderId}&status=RUNNING,PAUSED,WAITING_FOR_ACTIVITIES`
|
|
42
|
+
);
|
|
43
|
+
return result.ok ? result.result : { data: [] };
|
|
44
|
+
},
|
|
45
|
+
enabled: Boolean(orderId),
|
|
46
|
+
staleTime: 5e3,
|
|
47
|
+
// Poll every 2 seconds when waiting for processing
|
|
48
|
+
refetchInterval: isWaitingForProcessing ? 2e3 : false
|
|
49
|
+
});
|
|
50
|
+
const activeInstance = instancesData?.data?.find(
|
|
51
|
+
(inst) => inst.workflowId === WORKFLOW_ID && !["COMPLETED", "FAILED", "CANCELLED"].includes(inst.status)
|
|
52
|
+
);
|
|
53
|
+
const { data: tasksData, isLoading: tasksLoading } = useQuery({
|
|
54
|
+
queryKey: ["workflow-tasks", activeInstance?.id],
|
|
55
|
+
queryFn: async () => {
|
|
56
|
+
if (!activeInstance?.id) return { data: [] };
|
|
57
|
+
const result = await apiCall(
|
|
58
|
+
`/api/workflows/tasks?workflowInstanceId=${activeInstance.id}&status=PENDING,IN_PROGRESS`
|
|
59
|
+
);
|
|
60
|
+
return result.ok ? result.result : { data: [] };
|
|
61
|
+
},
|
|
62
|
+
enabled: Boolean(activeInstance?.id),
|
|
63
|
+
staleTime: 5e3,
|
|
64
|
+
// Poll every 2 seconds when waiting for processing
|
|
65
|
+
refetchInterval: isWaitingForProcessing ? 2e3 : false
|
|
66
|
+
});
|
|
67
|
+
const pendingTask = tasksData?.data?.[0];
|
|
68
|
+
const isProcessing = activeInstance && !pendingTask && !["COMPLETED", "FAILED", "CANCELLED"].includes(activeInstance.status);
|
|
69
|
+
const prevStatusRef = React.useRef(data?.status);
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
if (isProcessing) {
|
|
72
|
+
setIsWaitingForProcessing(true);
|
|
73
|
+
} else if (!activeInstance || activeInstance.status === "COMPLETED") {
|
|
74
|
+
setIsWaitingForProcessing(false);
|
|
75
|
+
}
|
|
76
|
+
}, [isProcessing, activeInstance]);
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
const newStatus = orderData?.status;
|
|
79
|
+
const oldStatus = prevStatusRef.current;
|
|
80
|
+
if (newStatus && oldStatus && newStatus !== oldStatus) {
|
|
81
|
+
if (orderId) {
|
|
82
|
+
emitSalesDocumentDataRefresh({ documentId: orderId, kind: "order" });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
prevStatusRef.current = newStatus || oldStatus;
|
|
86
|
+
}, [orderData?.status, orderId]);
|
|
87
|
+
const { data: dictionariesData } = useQuery({
|
|
88
|
+
queryKey: ["dictionaries"],
|
|
89
|
+
queryFn: async () => {
|
|
90
|
+
const result = await apiCall(
|
|
91
|
+
"/api/dictionaries"
|
|
92
|
+
);
|
|
93
|
+
return result.ok ? result.result : { items: [] };
|
|
94
|
+
},
|
|
95
|
+
staleTime: 6e4
|
|
96
|
+
});
|
|
97
|
+
const orderStatusDictionaryId = React.useMemo(() => {
|
|
98
|
+
const dict = dictionariesData?.items?.find((d) => d.key === "sales.order_status");
|
|
99
|
+
return dict?.id;
|
|
100
|
+
}, [dictionariesData]);
|
|
101
|
+
const { data: statusEntriesData } = useQuery({
|
|
102
|
+
queryKey: ["dictionary-entries", orderStatusDictionaryId],
|
|
103
|
+
queryFn: async () => {
|
|
104
|
+
if (!orderStatusDictionaryId) return { items: [] };
|
|
105
|
+
const result = await apiCall(
|
|
106
|
+
`/api/dictionaries/${orderStatusDictionaryId}/entries`
|
|
107
|
+
);
|
|
108
|
+
return result.ok ? result.result : { items: [] };
|
|
109
|
+
},
|
|
110
|
+
enabled: Boolean(orderStatusDictionaryId),
|
|
111
|
+
staleTime: 6e4
|
|
112
|
+
});
|
|
113
|
+
const findStatusId = React.useCallback((code) => {
|
|
114
|
+
const entries = statusEntriesData?.items || [];
|
|
115
|
+
const entry = entries.find(
|
|
116
|
+
(e) => e.value === code || e.label?.toLowerCase() === code.toLowerCase()
|
|
117
|
+
);
|
|
118
|
+
return entry?.id;
|
|
119
|
+
}, [statusEntriesData]);
|
|
120
|
+
const startWorkflowMutation = useMutation({
|
|
121
|
+
mutationFn: async () => {
|
|
122
|
+
const pendingApprovalStatusId = findStatusId("pending_approval");
|
|
123
|
+
const approvedStatusId = findStatusId("approved");
|
|
124
|
+
const rejectedStatusId = findStatusId("rejected");
|
|
125
|
+
if (!pendingApprovalStatusId || !approvedStatusId || !rejectedStatusId) {
|
|
126
|
+
throw new Error(t("workflows.orderApproval.missingStatuses", "Missing order status entries. Please ensure pending_approval, approved, and rejected statuses exist in the sales.order_status dictionary."));
|
|
127
|
+
}
|
|
128
|
+
const result = await apiCall("/api/workflows/instances", {
|
|
129
|
+
method: "POST",
|
|
130
|
+
body: JSON.stringify({
|
|
131
|
+
workflowId: WORKFLOW_ID,
|
|
132
|
+
initialContext: {
|
|
133
|
+
orderId,
|
|
134
|
+
pendingApprovalStatusId,
|
|
135
|
+
approvedStatusId,
|
|
136
|
+
rejectedStatusId
|
|
137
|
+
},
|
|
138
|
+
metadata: {
|
|
139
|
+
entityType: "SalesOrder",
|
|
140
|
+
entityId: orderId
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
});
|
|
144
|
+
if (!result.ok) {
|
|
145
|
+
const errorResult = result.result;
|
|
146
|
+
throw new Error(errorResult?.error || t("workflows.orderApproval.startError", "Failed to start approval workflow"));
|
|
147
|
+
}
|
|
148
|
+
return result.result;
|
|
149
|
+
},
|
|
150
|
+
onSuccess: () => {
|
|
151
|
+
setError(null);
|
|
152
|
+
queryClient.invalidateQueries({ queryKey: ["workflow-instances", orderId] });
|
|
153
|
+
},
|
|
154
|
+
onError: (err) => {
|
|
155
|
+
setError(err.message);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
const completeTaskMutation = useMutation({
|
|
159
|
+
mutationFn: async ({ taskId, formData }) => {
|
|
160
|
+
const result = await apiCall(`/api/workflows/tasks/${taskId}/complete`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
body: JSON.stringify({ formData })
|
|
163
|
+
});
|
|
164
|
+
if (!result.ok) {
|
|
165
|
+
const errorResult = result.result;
|
|
166
|
+
throw new Error(errorResult?.error || t("workflows.orderApproval.completeError", "Failed to complete approval task"));
|
|
167
|
+
}
|
|
168
|
+
return result.result;
|
|
169
|
+
},
|
|
170
|
+
onSuccess: () => {
|
|
171
|
+
setError(null);
|
|
172
|
+
setDecision("");
|
|
173
|
+
setComments("");
|
|
174
|
+
setIsWaitingForProcessing(true);
|
|
175
|
+
queryClient.invalidateQueries({ queryKey: ["workflow-instances", orderId] });
|
|
176
|
+
queryClient.invalidateQueries({ queryKey: ["workflow-tasks", activeInstance?.id] });
|
|
177
|
+
queryClient.invalidateQueries({ queryKey: ["sales-order", orderId] });
|
|
178
|
+
},
|
|
179
|
+
onError: (err) => {
|
|
180
|
+
setError(err.message);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
const handleStartWorkflow = () => {
|
|
184
|
+
setIsWaitingForProcessing(true);
|
|
185
|
+
startWorkflowMutation.mutate();
|
|
186
|
+
};
|
|
187
|
+
const handleCompleteTask = () => {
|
|
188
|
+
if (!pendingTask || !decision) return;
|
|
189
|
+
completeTaskMutation.mutate({
|
|
190
|
+
taskId: pendingTask.id,
|
|
191
|
+
formData: { decision, comments }
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
const handleKeyDown = (e) => {
|
|
195
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
if (pendingTask && decision && !isSubmitting) {
|
|
198
|
+
handleCompleteTask();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const isLoading = instancesLoading || tasksLoading;
|
|
203
|
+
const isSubmitting = startWorkflowMutation.isPending || completeTaskMutation.isPending;
|
|
204
|
+
if (!orderId) return null;
|
|
205
|
+
const orderStatus = currentOrderStatus?.toLowerCase();
|
|
206
|
+
if (orderStatus !== "pending_approval") {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
if (isLoading) {
|
|
210
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center p-4", children: [
|
|
211
|
+
/* @__PURE__ */ jsx(Spinner, { size: "sm" }),
|
|
212
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 text-sm text-muted-foreground", children: t("common.loading", "Loading...") })
|
|
213
|
+
] });
|
|
214
|
+
}
|
|
215
|
+
const getStatusBadge = () => {
|
|
216
|
+
if (!activeInstance) return null;
|
|
217
|
+
const statusVariants = {
|
|
218
|
+
RUNNING: "default",
|
|
219
|
+
PAUSED: "secondary",
|
|
220
|
+
WAITING_FOR_ACTIVITIES: "secondary",
|
|
221
|
+
COMPLETED: "default",
|
|
222
|
+
FAILED: "destructive",
|
|
223
|
+
CANCELLED: "outline"
|
|
224
|
+
};
|
|
225
|
+
return /* @__PURE__ */ jsx(Badge, { variant: statusVariants[activeInstance.status] || "outline", children: t(`workflows.instances.statuses.${activeInstance.status}`, activeInstance.status) });
|
|
226
|
+
};
|
|
227
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border bg-card p-4 shadow-sm", children: [
|
|
228
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
229
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
230
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: t("workflows.orderApproval.groupLabel", "Order Approval") }),
|
|
231
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.orderApproval.groupDescription", "Review and approve or reject this order") })
|
|
232
|
+
] }),
|
|
233
|
+
getStatusBadge()
|
|
234
|
+
] }),
|
|
235
|
+
error && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-3 text-xs text-red-800", children: error }),
|
|
236
|
+
!activeInstance && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
237
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("workflows.orderApproval.noWorkflowActive", "No approval workflow is active for this order.") }),
|
|
238
|
+
/* @__PURE__ */ jsxs(
|
|
239
|
+
Button,
|
|
240
|
+
{
|
|
241
|
+
onClick: handleStartWorkflow,
|
|
242
|
+
disabled: isSubmitting,
|
|
243
|
+
variant: "default",
|
|
244
|
+
size: "sm",
|
|
245
|
+
children: [
|
|
246
|
+
isSubmitting && /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "mr-2" }),
|
|
247
|
+
t("workflows.orderApproval.requestApproval", "Request Approval")
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
] }),
|
|
252
|
+
activeInstance && pendingTask && /* @__PURE__ */ jsxs("div", { className: "space-y-3", onKeyDown: handleKeyDown, children: [
|
|
253
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-amber-200 bg-amber-50 p-3", children: [
|
|
254
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-amber-800", children: t("workflows.orderApproval.pendingTitle", "Pending Approval") }),
|
|
255
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-amber-700 mt-1", children: t("workflows.orderApproval.pendingDescription", "This order requires approval before processing.") })
|
|
256
|
+
] }),
|
|
257
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
258
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: t("workflows.orderApproval.decisionLabel", "Decision") }),
|
|
259
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
260
|
+
/* @__PURE__ */ jsx(
|
|
261
|
+
Button,
|
|
262
|
+
{
|
|
263
|
+
variant: decision === "approve" ? "default" : "outline",
|
|
264
|
+
size: "sm",
|
|
265
|
+
onClick: () => setDecision("approve"),
|
|
266
|
+
className: decision === "approve" ? "bg-emerald-600 hover:bg-emerald-700" : "",
|
|
267
|
+
children: t("workflows.orderApproval.approveButton", "Approve")
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
/* @__PURE__ */ jsx(
|
|
271
|
+
Button,
|
|
272
|
+
{
|
|
273
|
+
variant: decision === "reject" ? "destructive" : "outline",
|
|
274
|
+
size: "sm",
|
|
275
|
+
onClick: () => setDecision("reject"),
|
|
276
|
+
children: t("workflows.orderApproval.rejectButton", "Reject")
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
] })
|
|
280
|
+
] }),
|
|
281
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
282
|
+
/* @__PURE__ */ jsxs("label", { className: "text-sm font-medium", children: [
|
|
283
|
+
t("workflows.orderApproval.commentsLabel", "Comments"),
|
|
284
|
+
" ",
|
|
285
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground font-normal", children: [
|
|
286
|
+
"(",
|
|
287
|
+
t("common.optional", "optional"),
|
|
288
|
+
")"
|
|
289
|
+
] })
|
|
290
|
+
] }),
|
|
291
|
+
/* @__PURE__ */ jsx(
|
|
292
|
+
Textarea,
|
|
293
|
+
{
|
|
294
|
+
value: comments,
|
|
295
|
+
onChange: (e) => setComments(e.target.value),
|
|
296
|
+
placeholder: t("workflows.orderApproval.commentsPlaceholder", "Add optional comments..."),
|
|
297
|
+
rows: 2
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
] }),
|
|
301
|
+
/* @__PURE__ */ jsxs(
|
|
302
|
+
Button,
|
|
303
|
+
{
|
|
304
|
+
onClick: handleCompleteTask,
|
|
305
|
+
disabled: !decision || isSubmitting,
|
|
306
|
+
variant: "default",
|
|
307
|
+
size: "sm",
|
|
308
|
+
className: "w-full",
|
|
309
|
+
children: [
|
|
310
|
+
isSubmitting && /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "mr-2" }),
|
|
311
|
+
t("workflows.orderApproval.submitDecision", "Submit Decision")
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
)
|
|
315
|
+
] }),
|
|
316
|
+
activeInstance && !pendingTask && activeInstance.status !== "COMPLETED" && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-blue-200 bg-blue-50 p-3", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-blue-800", children: t("workflows.orderApproval.processing", "Workflow is processing...") }) }),
|
|
317
|
+
activeInstance && activeInstance.status === "COMPLETED" && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-emerald-200 bg-emerald-50 p-3", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-emerald-800", children: t("workflows.orderApproval.completed", "Approval workflow completed.") }) })
|
|
318
|
+
] });
|
|
319
|
+
}
|
|
320
|
+
export {
|
|
321
|
+
OrderApprovalWidget as default
|
|
322
|
+
};
|
|
323
|
+
//# sourceMappingURL=widget.client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/workflows/widgets/injection/order-approval/widget.client.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { emitSalesDocumentDataRefresh } from '@open-mercato/core/modules/sales/lib/frontend/documentDataEvents'\n\ntype OrderRecord = {\n id: string\n orderNumber?: string\n status?: string\n statusEntryId?: string\n}\n\ntype WorkflowInstance = {\n id: string\n workflowId: string\n status: 'RUNNING' | 'PAUSED' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'WAITING_FOR_ACTIVITIES'\n currentStepId: string\n context: Record<string, any>\n}\n\ntype UserTask = {\n id: string\n taskName: string\n status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED'\n workflowInstanceId: string\n claimedBy: string | null\n}\n\ntype DictionaryEntry = {\n id: string\n value: string\n label: string\n color?: string\n icon?: string\n}\n\nconst WORKFLOW_ID = 'sales_order_approval_v1'\n\nexport default function OrderApprovalWidget({ data }: InjectionWidgetComponentProps<unknown, OrderRecord>) {\n const t = useT()\n const queryClient = useQueryClient()\n const orderId = data?.id\n\n const [decision, setDecision] = React.useState<'approve' | 'reject' | ''>('')\n const [comments, setComments] = React.useState('')\n const [error, setError] = React.useState<string | null>(null)\n\n // Track if we're waiting for workflow to process (for polling)\n const [isWaitingForProcessing, setIsWaitingForProcessing] = React.useState(false)\n\n // Fetch current order status directly (to detect when workflow updates it)\n // Use list endpoint with ID filter since single-item endpoint doesn't exist\n const { data: orderData } = useQuery({\n queryKey: ['order-status', orderId],\n queryFn: async () => {\n if (!orderId) return null\n const result = await apiCall<{ items: Array<{ id: string; status?: string | null }> }>(\n `/api/sales/orders?id=${orderId}&pageSize=1`\n )\n return result.ok && result.result?.items?.[0] ? result.result.items[0] : null\n },\n enabled: Boolean(orderId),\n staleTime: 5_000,\n // Poll when waiting for processing to detect status change\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n // Use fresh order status from query, fallback to prop data\n // The API returns status as a string value (e.g., \"approved\", \"pending_approval\")\n const currentOrderStatus = orderData?.status || data?.status\n\n // Fetch active workflow instances for this order\n const { data: instancesData, isLoading: instancesLoading } = useQuery({\n queryKey: ['workflow-instances', orderId],\n queryFn: async () => {\n if (!orderId) return { data: [] }\n const result = await apiCall<{ data: WorkflowInstance[] }>(\n `/api/workflows/instances?entityId=${orderId}&status=RUNNING,PAUSED,WAITING_FOR_ACTIVITIES`\n )\n return result.ok ? result.result : { data: [] }\n },\n enabled: Boolean(orderId),\n staleTime: 5_000,\n // Poll every 2 seconds when waiting for processing\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n const activeInstance = instancesData?.data?.find(\n (inst) => inst.workflowId === WORKFLOW_ID && !['COMPLETED', 'FAILED', 'CANCELLED'].includes(inst.status)\n )\n\n // Fetch pending user tasks for active instance\n const { data: tasksData, isLoading: tasksLoading } = useQuery({\n queryKey: ['workflow-tasks', activeInstance?.id],\n queryFn: async () => {\n if (!activeInstance?.id) return { data: [] }\n const result = await apiCall<{ data: UserTask[] }>(\n `/api/workflows/tasks?workflowInstanceId=${activeInstance.id}&status=PENDING,IN_PROGRESS`\n )\n return result.ok ? result.result : { data: [] }\n },\n enabled: Boolean(activeInstance?.id),\n staleTime: 5_000,\n // Poll every 2 seconds when waiting for processing\n refetchInterval: isWaitingForProcessing ? 2_000 : false,\n })\n\n const pendingTask = tasksData?.data?.[0]\n\n // Auto-detect when workflow is processing (active instance but no pending task)\n const isProcessing = activeInstance && !pendingTask && !['COMPLETED', 'FAILED', 'CANCELLED'].includes(activeInstance.status)\n\n // Track previous status to detect changes\n const prevStatusRef = React.useRef<string | undefined>(data?.status)\n\n // Update polling state when processing state changes\n React.useEffect(() => {\n if (isProcessing) {\n setIsWaitingForProcessing(true)\n } else if (!activeInstance || activeInstance.status === 'COMPLETED') {\n setIsWaitingForProcessing(false)\n }\n }, [isProcessing, activeInstance])\n\n // When order status changes, emit refresh event to update the page\n React.useEffect(() => {\n const newStatus = orderData?.status\n const oldStatus = prevStatusRef.current\n\n if (newStatus && oldStatus && newStatus !== oldStatus) {\n // Status changed - emit document refresh event to reload the page data\n if (orderId) {\n emitSalesDocumentDataRefresh({ documentId: orderId, kind: 'order' })\n }\n }\n\n prevStatusRef.current = newStatus || oldStatus\n }, [orderData?.status, orderId])\n\n // First fetch dictionaries to find the order_status dictionary ID\n const { data: dictionariesData } = useQuery({\n queryKey: ['dictionaries'],\n queryFn: async () => {\n const result = await apiCall<{ items: Array<{ id: string; key: string }> }>(\n '/api/dictionaries'\n )\n return result.ok ? result.result : { items: [] }\n },\n staleTime: 60_000,\n })\n\n const orderStatusDictionaryId = React.useMemo(() => {\n const dict = dictionariesData?.items?.find(d => d.key === 'sales.order_status')\n return dict?.id\n }, [dictionariesData])\n\n // Fetch order status dictionary entries using the dictionary ID\n const { data: statusEntriesData } = useQuery({\n queryKey: ['dictionary-entries', orderStatusDictionaryId],\n queryFn: async () => {\n if (!orderStatusDictionaryId) return { items: [] }\n const result = await apiCall<{ items: DictionaryEntry[] }>(\n `/api/dictionaries/${orderStatusDictionaryId}/entries`\n )\n return result.ok ? result.result : { items: [] }\n },\n enabled: Boolean(orderStatusDictionaryId),\n staleTime: 60_000,\n })\n\n const findStatusId = React.useCallback((code: string) => {\n const entries = statusEntriesData?.items || []\n // The API returns 'value' as the code/key for dictionary entries\n const entry = entries.find(\n (e) => e.value === code || e.label?.toLowerCase() === code.toLowerCase()\n )\n return entry?.id\n }, [statusEntriesData])\n\n // Start workflow mutation\n const startWorkflowMutation = useMutation({\n mutationFn: async () => {\n const pendingApprovalStatusId = findStatusId('pending_approval')\n const approvedStatusId = findStatusId('approved')\n const rejectedStatusId = findStatusId('rejected')\n\n if (!pendingApprovalStatusId || !approvedStatusId || !rejectedStatusId) {\n throw new Error(t('workflows.orderApproval.missingStatuses', 'Missing order status entries. Please ensure pending_approval, approved, and rejected statuses exist in the sales.order_status dictionary.'))\n }\n\n const result = await apiCall<{ data: WorkflowInstance }>('/api/workflows/instances', {\n method: 'POST',\n body: JSON.stringify({\n workflowId: WORKFLOW_ID,\n initialContext: {\n orderId,\n pendingApprovalStatusId,\n approvedStatusId,\n rejectedStatusId,\n },\n metadata: {\n entityType: 'SalesOrder',\n entityId: orderId,\n },\n }),\n })\n\n if (!result.ok) {\n const errorResult = result.result as { error?: string } | null\n throw new Error(errorResult?.error || t('workflows.orderApproval.startError', 'Failed to start approval workflow'))\n }\n\n return result.result\n },\n onSuccess: () => {\n setError(null)\n queryClient.invalidateQueries({ queryKey: ['workflow-instances', orderId] })\n },\n onError: (err: Error) => {\n setError(err.message)\n },\n })\n\n // Complete task mutation\n const completeTaskMutation = useMutation({\n mutationFn: async ({ taskId, formData }: { taskId: string; formData: { decision: string; comments?: string } }) => {\n const result = await apiCall(`/api/workflows/tasks/${taskId}/complete`, {\n method: 'POST',\n body: JSON.stringify({ formData }),\n })\n\n if (!result.ok) {\n const errorResult = result.result as { error?: string } | null\n throw new Error(errorResult?.error || t('workflows.orderApproval.completeError', 'Failed to complete approval task'))\n }\n\n return result.result\n },\n onSuccess: () => {\n setError(null)\n setDecision('')\n setComments('')\n // Start polling immediately after submitting decision\n setIsWaitingForProcessing(true)\n queryClient.invalidateQueries({ queryKey: ['workflow-instances', orderId] })\n queryClient.invalidateQueries({ queryKey: ['workflow-tasks', activeInstance?.id] })\n // Also refresh the order data\n queryClient.invalidateQueries({ queryKey: ['sales-order', orderId] })\n },\n onError: (err: Error) => {\n setError(err.message)\n },\n })\n\n const handleStartWorkflow = () => {\n // Start polling after starting workflow\n setIsWaitingForProcessing(true)\n startWorkflowMutation.mutate()\n }\n\n const handleCompleteTask = () => {\n if (!pendingTask || !decision) return\n completeTaskMutation.mutate({\n taskId: pendingTask.id,\n formData: { decision, comments },\n })\n }\n\n // Handle keyboard shortcuts (Cmd/Ctrl+Enter to submit)\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (pendingTask && decision && !isSubmitting) {\n handleCompleteTask()\n }\n }\n }\n\n const isLoading = instancesLoading || tasksLoading\n const isSubmitting = startWorkflowMutation.isPending || completeTaskMutation.isPending\n\n // Don't render if no orderId\n if (!orderId) return null\n\n // Only show widget when order status is pending_approval\n const orderStatus = currentOrderStatus?.toLowerCase()\n if (orderStatus !== 'pending_approval') {\n return null\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center p-4\">\n <Spinner size=\"sm\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</span>\n </div>\n )\n }\n\n // Show workflow status badge\n const getStatusBadge = () => {\n if (!activeInstance) return null\n\n const statusVariants: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n RUNNING: 'default',\n PAUSED: 'secondary',\n WAITING_FOR_ACTIVITIES: 'secondary',\n COMPLETED: 'default',\n FAILED: 'destructive',\n CANCELLED: 'outline',\n }\n\n return (\n <Badge variant={statusVariants[activeInstance.status] || 'outline'}>\n {t(`workflows.instances.statuses.${activeInstance.status}`, activeInstance.status)}\n </Badge>\n )\n }\n\n return (\n <div className=\"space-y-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <div className=\"text-sm font-semibold text-foreground\">\n {t('workflows.orderApproval.groupLabel', 'Order Approval')}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('workflows.orderApproval.groupDescription', 'Review and approve or reject this order')}\n </p>\n </div>\n {getStatusBadge()}\n </div>\n\n {error && (\n <div className=\"rounded-md border border-red-200 bg-red-50 p-3 text-xs text-red-800\">\n {error}\n </div>\n )}\n\n {/* No active workflow - show request approval button */}\n {!activeInstance && (\n <div className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {t('workflows.orderApproval.noWorkflowActive', 'No approval workflow is active for this order.')}\n </p>\n <Button\n onClick={handleStartWorkflow}\n disabled={isSubmitting}\n variant=\"default\"\n size=\"sm\"\n >\n {isSubmitting && <Spinner size=\"sm\" className=\"mr-2\" />}\n {t('workflows.orderApproval.requestApproval', 'Request Approval')}\n </Button>\n </div>\n )}\n\n {/* Active workflow with pending task - show approve/reject UI */}\n {activeInstance && pendingTask && (\n <div className=\"space-y-3\" onKeyDown={handleKeyDown}>\n <div className=\"rounded-md border border-amber-200 bg-amber-50 p-3\">\n <p className=\"text-sm font-medium text-amber-800\">\n {t('workflows.orderApproval.pendingTitle', 'Pending Approval')}\n </p>\n <p className=\"text-xs text-amber-700 mt-1\">\n {t('workflows.orderApproval.pendingDescription', 'This order requires approval before processing.')}\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('workflows.orderApproval.decisionLabel', 'Decision')}\n </label>\n <div className=\"flex gap-2\">\n <Button\n variant={decision === 'approve' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setDecision('approve')}\n className={decision === 'approve' ? 'bg-emerald-600 hover:bg-emerald-700' : ''}\n >\n {t('workflows.orderApproval.approveButton', 'Approve')}\n </Button>\n <Button\n variant={decision === 'reject' ? 'destructive' : 'outline'}\n size=\"sm\"\n onClick={() => setDecision('reject')}\n >\n {t('workflows.orderApproval.rejectButton', 'Reject')}\n </Button>\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('workflows.orderApproval.commentsLabel', 'Comments')} <span className=\"text-muted-foreground font-normal\">({t('common.optional', 'optional')})</span>\n </label>\n <Textarea\n value={comments}\n onChange={(e) => setComments(e.target.value)}\n placeholder={t('workflows.orderApproval.commentsPlaceholder', 'Add optional comments...')}\n rows={2}\n />\n </div>\n\n <Button\n onClick={handleCompleteTask}\n disabled={!decision || isSubmitting}\n variant=\"default\"\n size=\"sm\"\n className=\"w-full\"\n >\n {isSubmitting && <Spinner size=\"sm\" className=\"mr-2\" />}\n {t('workflows.orderApproval.submitDecision', 'Submit Decision')}\n </Button>\n </div>\n )}\n\n {/* Active workflow but no pending task (processing) */}\n {activeInstance && !pendingTask && activeInstance.status !== 'COMPLETED' && (\n <div className=\"rounded-md border border-blue-200 bg-blue-50 p-3\">\n <p className=\"text-sm text-blue-800\">\n {t('workflows.orderApproval.processing', 'Workflow is processing...')}\n </p>\n </div>\n )}\n\n {/* Completed workflow */}\n {activeInstance && activeInstance.status === 'COMPLETED' && (\n <div className=\"rounded-md border border-emerald-200 bg-emerald-50 p-3\">\n <p className=\"text-sm text-emerald-800\">\n {t('workflows.orderApproval.completed', 'Approval workflow completed.')}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import OrderApprovalWidget from "./widget.client.js";
|
|
2
|
+
const widget = {
|
|
3
|
+
metadata: {
|
|
4
|
+
id: "workflows.injection.order-approval",
|
|
5
|
+
title: "Order Approval",
|
|
6
|
+
description: "Approve or reject orders requiring authorization",
|
|
7
|
+
features: ["sales.orders.approve"],
|
|
8
|
+
priority: 100,
|
|
9
|
+
enabled: true
|
|
10
|
+
},
|
|
11
|
+
Widget: OrderApprovalWidget
|
|
12
|
+
};
|
|
13
|
+
var widget_default = widget;
|
|
14
|
+
export {
|
|
15
|
+
widget_default as default
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=widget.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/workflows/widgets/injection/order-approval/widget.ts"],
|
|
4
|
+
"sourcesContent": ["import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport OrderApprovalWidget from './widget.client'\n\nconst widget: InjectionWidgetModule<any, any> = {\n metadata: {\n id: 'workflows.injection.order-approval',\n title: 'Order Approval',\n description: 'Approve or reject orders requiring authorization',\n features: ['sales.orders.approve'],\n priority: 100,\n enabled: true,\n },\n Widget: OrderApprovalWidget,\n}\n\nexport default widget\n"],
|
|
5
|
+
"mappings": "AACA,OAAO,yBAAyB;AAEhC,MAAM,SAA0C;AAAA,EAC9C,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,sBAAsB;AAAA,IACjC,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AACV;AAEA,IAAO,iBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const injectionTable = {
|
|
2
|
+
// Inject the order approval widget into the sales order detail page
|
|
3
|
+
"sales.document.detail.order:details": [
|
|
4
|
+
{
|
|
5
|
+
widgetId: "workflows.injection.order-approval",
|
|
6
|
+
kind: "group",
|
|
7
|
+
column: 2,
|
|
8
|
+
groupLabel: "workflows.orderApproval.groupLabel",
|
|
9
|
+
groupDescription: "workflows.orderApproval.groupDescription",
|
|
10
|
+
priority: 200
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
var injection_table_default = injectionTable;
|
|
15
|
+
export {
|
|
16
|
+
injection_table_default as default,
|
|
17
|
+
injectionTable
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=injection-table.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/workflows/widgets/injection-table.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\n/**\n * Workflows module injection table\n * Maps injection spot IDs to widget IDs for automatic widget injection\n */\nexport const injectionTable: ModuleInjectionTable = {\n // Inject the order approval widget into the sales order detail page\n 'sales.document.detail.order:details': [\n {\n widgetId: 'workflows.injection.order-approval',\n kind: 'group',\n column: 2,\n groupLabel: 'workflows.orderApproval.groupLabel',\n groupDescription: 'workflows.orderApproval.groupDescription',\n priority: 200,\n },\n ],\n}\n\nexport default injectionTable\n"],
|
|
5
|
+
"mappings": "AAMO,MAAM,iBAAuC;AAAA;AAAA,EAElD,uCAAuC;AAAA,IACrC;AAAA,MACE,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const id = 'id'
|
|
2
|
+
export const name = 'name'
|
|
3
|
+
export const description = 'description'
|
|
4
|
+
export const workflow_definition_id = 'workflow_definition_id'
|
|
5
|
+
export const event_pattern = 'event_pattern'
|
|
6
|
+
export const config = 'config'
|
|
7
|
+
export const enabled = 'enabled'
|
|
8
|
+
export const priority = 'priority'
|
|
9
|
+
export const tenant_id = 'tenant_id'
|
|
10
|
+
export const organization_id = 'organization_id'
|
|
11
|
+
export const created_by = 'created_by'
|
|
12
|
+
export const updated_by = 'updated_by'
|
|
13
|
+
export const created_at = 'created_at'
|
|
14
|
+
export const updated_at = 'updated_at'
|
|
15
|
+
export const deleted_at = 'deleted_at'
|
|
@@ -155,6 +155,7 @@ export const E = {
|
|
|
155
155
|
"user_task": "workflows:user_task",
|
|
156
156
|
"workflow_definition": "workflows:workflow_definition",
|
|
157
157
|
"workflow_event": "workflows:workflow_event",
|
|
158
|
+
"workflow_event_trigger": "workflows:workflow_event_trigger",
|
|
158
159
|
"workflow_instance": "workflows:workflow_instance"
|
|
159
160
|
},
|
|
160
161
|
"currencies": {
|
|
@@ -117,6 +117,7 @@ import * as user_sidebar_preference from './entities/user_sidebar_preference/ind
|
|
|
117
117
|
import * as user_task from './entities/user_task/index'
|
|
118
118
|
import * as workflow_definition from './entities/workflow_definition/index'
|
|
119
119
|
import * as workflow_event from './entities/workflow_event/index'
|
|
120
|
+
import * as workflow_event_trigger from './entities/workflow_event_trigger/index'
|
|
120
121
|
import * as workflow_instance from './entities/workflow_instance/index'
|
|
121
122
|
|
|
122
123
|
export const entityFieldsRegistry: Record<string, Record<string, string>> = {
|
|
@@ -237,6 +238,7 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
|
|
|
237
238
|
user_task,
|
|
238
239
|
workflow_definition,
|
|
239
240
|
workflow_event,
|
|
241
|
+
workflow_event_trigger,
|
|
240
242
|
workflow_instance
|
|
241
243
|
}
|
|
242
244
|
|