@endora-commerce/mod-quote-requests 0.100.0

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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/admin/index.d.ts +26 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +56 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/RfqCreatePage.d.ts +9 -0
  8. package/dist/admin/pages/RfqCreatePage.d.ts.map +1 -0
  9. package/dist/admin/pages/RfqCreatePage.js +163 -0
  10. package/dist/admin/pages/RfqCreatePage.js.map +1 -0
  11. package/dist/admin/pages/RfqDetail.d.ts +9 -0
  12. package/dist/admin/pages/RfqDetail.d.ts.map +1 -0
  13. package/dist/admin/pages/RfqDetail.js +399 -0
  14. package/dist/admin/pages/RfqDetail.js.map +1 -0
  15. package/dist/admin/pages/RfqList.d.ts +9 -0
  16. package/dist/admin/pages/RfqList.d.ts.map +1 -0
  17. package/dist/admin/pages/RfqList.js +214 -0
  18. package/dist/admin/pages/RfqList.js.map +1 -0
  19. package/dist/admin/validity.d.ts +52 -0
  20. package/dist/admin/validity.d.ts.map +1 -0
  21. package/dist/admin/validity.js +61 -0
  22. package/dist/admin/validity.js.map +1 -0
  23. package/dist/backend/entities/quote-request-event.entity.d.ts +24 -0
  24. package/dist/backend/entities/quote-request-event.entity.d.ts.map +1 -0
  25. package/dist/backend/entities/quote-request-event.entity.js +67 -0
  26. package/dist/backend/entities/quote-request-event.entity.js.map +1 -0
  27. package/dist/backend/entities/quote-request-item.entity.d.ts +34 -0
  28. package/dist/backend/entities/quote-request-item.entity.d.ts.map +1 -0
  29. package/dist/backend/entities/quote-request-item.entity.js +119 -0
  30. package/dist/backend/entities/quote-request-item.entity.js.map +1 -0
  31. package/dist/backend/entities/quote-request-notification-event.entity.d.ts +23 -0
  32. package/dist/backend/entities/quote-request-notification-event.entity.d.ts.map +1 -0
  33. package/dist/backend/entities/quote-request-notification-event.entity.js +72 -0
  34. package/dist/backend/entities/quote-request-notification-event.entity.js.map +1 -0
  35. package/dist/backend/entities/quote-request-revision.entity.d.ts +35 -0
  36. package/dist/backend/entities/quote-request-revision.entity.d.ts.map +1 -0
  37. package/dist/backend/entities/quote-request-revision.entity.js +67 -0
  38. package/dist/backend/entities/quote-request-revision.entity.js.map +1 -0
  39. package/dist/backend/entities/quote-request.entity.d.ts +90 -0
  40. package/dist/backend/entities/quote-request.entity.d.ts.map +1 -0
  41. package/dist/backend/entities/quote-request.entity.js +191 -0
  42. package/dist/backend/entities/quote-request.entity.js.map +1 -0
  43. package/dist/backend/index.d.ts +120 -0
  44. package/dist/backend/index.d.ts.map +1 -0
  45. package/dist/backend/index.js +361 -0
  46. package/dist/backend/index.js.map +1 -0
  47. package/dist/backend/plugin.d.ts +95 -0
  48. package/dist/backend/plugin.d.ts.map +1 -0
  49. package/dist/backend/plugin.js +152 -0
  50. package/dist/backend/plugin.js.map +1 -0
  51. package/dist/backend/routes.admin.d.ts +13 -0
  52. package/dist/backend/routes.admin.d.ts.map +1 -0
  53. package/dist/backend/routes.admin.js +82 -0
  54. package/dist/backend/routes.admin.js.map +1 -0
  55. package/dist/backend/routes.customer.d.ts +18 -0
  56. package/dist/backend/routes.customer.d.ts.map +1 -0
  57. package/dist/backend/routes.customer.js +98 -0
  58. package/dist/backend/routes.customer.js.map +1 -0
  59. package/dist/backend/routes.sales-reps.d.ts +12 -0
  60. package/dist/backend/routes.sales-reps.d.ts.map +1 -0
  61. package/dist/backend/routes.sales-reps.js +57 -0
  62. package/dist/backend/routes.sales-reps.js.map +1 -0
  63. package/dist/backend/services/order-completion-reactor.d.ts +32 -0
  64. package/dist/backend/services/order-completion-reactor.d.ts.map +1 -0
  65. package/dist/backend/services/order-completion-reactor.js +57 -0
  66. package/dist/backend/services/order-completion-reactor.js.map +1 -0
  67. package/dist/backend/services/quote-request-business-id-generator.d.ts +46 -0
  68. package/dist/backend/services/quote-request-business-id-generator.d.ts.map +1 -0
  69. package/dist/backend/services/quote-request-business-id-generator.js +69 -0
  70. package/dist/backend/services/quote-request-business-id-generator.js.map +1 -0
  71. package/dist/backend/services/quote-request-read-port.d.ts +36 -0
  72. package/dist/backend/services/quote-request-read-port.d.ts.map +1 -0
  73. package/dist/backend/services/quote-request-read-port.js +96 -0
  74. package/dist/backend/services/quote-request-read-port.js.map +1 -0
  75. package/dist/backend/services/raised-on-channel.d.ts +23 -0
  76. package/dist/backend/services/raised-on-channel.d.ts.map +1 -0
  77. package/dist/backend/services/raised-on-channel.js +26 -0
  78. package/dist/backend/services/raised-on-channel.js.map +1 -0
  79. package/dist/backend/services/rfq-admin-service.d.ts +75 -0
  80. package/dist/backend/services/rfq-admin-service.d.ts.map +1 -0
  81. package/dist/backend/services/rfq-admin-service.js +487 -0
  82. package/dist/backend/services/rfq-admin-service.js.map +1 -0
  83. package/dist/backend/services/rfq-event-service.d.ts +32 -0
  84. package/dist/backend/services/rfq-event-service.d.ts.map +1 -0
  85. package/dist/backend/services/rfq-event-service.js +28 -0
  86. package/dist/backend/services/rfq-event-service.js.map +1 -0
  87. package/dist/backend/services/rfq-expiry-worker.d.ts +50 -0
  88. package/dist/backend/services/rfq-expiry-worker.d.ts.map +1 -0
  89. package/dist/backend/services/rfq-expiry-worker.js +70 -0
  90. package/dist/backend/services/rfq-expiry-worker.js.map +1 -0
  91. package/dist/backend/services/rfq-notification-service.d.ts +36 -0
  92. package/dist/backend/services/rfq-notification-service.d.ts.map +1 -0
  93. package/dist/backend/services/rfq-notification-service.js +52 -0
  94. package/dist/backend/services/rfq-notification-service.js.map +1 -0
  95. package/dist/backend/services/rfq-revision-service.d.ts +70 -0
  96. package/dist/backend/services/rfq-revision-service.d.ts.map +1 -0
  97. package/dist/backend/services/rfq-revision-service.js +120 -0
  98. package/dist/backend/services/rfq-revision-service.js.map +1 -0
  99. package/dist/backend/services/rfq-service.d.ts +140 -0
  100. package/dist/backend/services/rfq-service.d.ts.map +1 -0
  101. package/dist/backend/services/rfq-service.js +786 -0
  102. package/dist/backend/services/rfq-service.js.map +1 -0
  103. package/dist/backend/services/sales-channel-attributions.d.ts +38 -0
  104. package/dist/backend/services/sales-channel-attributions.d.ts.map +1 -0
  105. package/dist/backend/services/sales-channel-attributions.js +48 -0
  106. package/dist/backend/services/sales-channel-attributions.js.map +1 -0
  107. package/dist/manifest.d.ts +203 -0
  108. package/dist/manifest.d.ts.map +1 -0
  109. package/dist/manifest.js +277 -0
  110. package/dist/manifest.js.map +1 -0
  111. package/dist/migrations/20260424T190112_quote_requests_init.d.ts +19 -0
  112. package/dist/migrations/20260424T190112_quote_requests_init.d.ts.map +1 -0
  113. package/dist/migrations/20260424T190112_quote_requests_init.js +65 -0
  114. package/dist/migrations/20260424T190112_quote_requests_init.js.map +1 -0
  115. package/dist/migrations/20260503T141344_quote_requests_workflow.d.ts +37 -0
  116. package/dist/migrations/20260503T141344_quote_requests_workflow.d.ts.map +1 -0
  117. package/dist/migrations/20260503T141344_quote_requests_workflow.js +251 -0
  118. package/dist/migrations/20260503T141344_quote_requests_workflow.js.map +1 -0
  119. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.d.ts +10 -0
  120. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.d.ts.map +1 -0
  121. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.js +16 -0
  122. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.js.map +1 -0
  123. package/dist/migrations/20260611T140417_quote_requests_business_id.d.ts +24 -0
  124. package/dist/migrations/20260611T140417_quote_requests_business_id.d.ts.map +1 -0
  125. package/dist/migrations/20260611T140417_quote_requests_business_id.js +34 -0
  126. package/dist/migrations/20260611T140417_quote_requests_business_id.js.map +1 -0
  127. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.d.ts +21 -0
  128. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.d.ts.map +1 -0
  129. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.js +29 -0
  130. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.js.map +1 -0
  131. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.d.ts +12 -0
  132. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.d.ts.map +1 -0
  133. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.js +16 -0
  134. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.js.map +1 -0
  135. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts +34 -0
  136. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts.map +1 -0
  137. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js +57 -0
  138. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js.map +1 -0
  139. package/dist/migrations/index.d.ts +53 -0
  140. package/dist/migrations/index.d.ts.map +1 -0
  141. package/dist/migrations/index.js +61 -0
  142. package/dist/migrations/index.js.map +1 -0
  143. package/dist/migrations/status-mapping.d.ts +18 -0
  144. package/dist/migrations/status-mapping.d.ts.map +1 -0
  145. package/dist/migrations/status-mapping.js +28 -0
  146. package/dist/migrations/status-mapping.js.map +1 -0
  147. package/docs/quote_requests.md +168 -0
  148. package/i18n/en.json +14 -0
  149. package/i18n/pl.json +14 -0
  150. package/package.json +99 -0
  151. package/tailwind.css +14 -0
@@ -0,0 +1,34 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * `quote_requests.sales_channel_id` — the channel a quote request was raised
4
+ * on (feature 005, FR-012).
5
+ *
6
+ * The column, its foreign key and its index were added by the platform's
7
+ * frozen `Migration20260430T170044CoreSalesChannelsPromote` until
8
+ * `specs/120-migration-closure-bridge-ownership/` Phase 3. Under D-226 a
9
+ * migration may name a table only if its own module creates it, a module in
10
+ * its transitive `dependencies` closure creates it, or the platform creates
11
+ * it — and the platform declares no dependencies, so it could never name
12
+ * `quote_requests` at all. An instance that omits this module had a frozen
13
+ * corpus altering a table nothing builds.
14
+ *
15
+ * Here the closure holds in both directions the SQL runs: `quote_requests` is
16
+ * this module's own table, and `sales_channels` is the kernel's, created
17
+ * inside the frozen prefix that every above-watermark migration runs after.
18
+ * `sales_channels` is in this module's `dependencies` besides.
19
+ *
20
+ * **Written idempotently**, because a database that has already applied the
21
+ * frozen migration carries all three objects. The storage keys on the class
22
+ * name and holds no checksum, so the reduced frozen body is not re-offered
23
+ * there; this migration is the no-op it reads as, and on a fresh database it
24
+ * is the change. The constraint add is guarded by a `pg_constraint` probe
25
+ * because PostgreSQL has no `add constraint if not exists`.
26
+ *
27
+ * The column stays NULLABLE, as it was: the backfill and the NOT NULL flip
28
+ * are a separate step (feature 005 T060) and are not this migration's.
29
+ */
30
+ export declare class Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution extends Migration {
31
+ up(): Promise<void>;
32
+ down(): Promise<void>;
33
+ }
34
+ //# sourceMappingURL=20260912T125614_quote_requests_quote_request_channel_attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260912T125614_quote_requests_quote_request_channel_attribution.d.ts","sourceRoot":"","sources":["../../src/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,mEAAoE,SAAQ,SAAS;IACjF,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAOrC"}
@@ -0,0 +1,57 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * `quote_requests.sales_channel_id` — the channel a quote request was raised
4
+ * on (feature 005, FR-012).
5
+ *
6
+ * The column, its foreign key and its index were added by the platform's
7
+ * frozen `Migration20260430T170044CoreSalesChannelsPromote` until
8
+ * `specs/120-migration-closure-bridge-ownership/` Phase 3. Under D-226 a
9
+ * migration may name a table only if its own module creates it, a module in
10
+ * its transitive `dependencies` closure creates it, or the platform creates
11
+ * it — and the platform declares no dependencies, so it could never name
12
+ * `quote_requests` at all. An instance that omits this module had a frozen
13
+ * corpus altering a table nothing builds.
14
+ *
15
+ * Here the closure holds in both directions the SQL runs: `quote_requests` is
16
+ * this module's own table, and `sales_channels` is the kernel's, created
17
+ * inside the frozen prefix that every above-watermark migration runs after.
18
+ * `sales_channels` is in this module's `dependencies` besides.
19
+ *
20
+ * **Written idempotently**, because a database that has already applied the
21
+ * frozen migration carries all three objects. The storage keys on the class
22
+ * name and holds no checksum, so the reduced frozen body is not re-offered
23
+ * there; this migration is the no-op it reads as, and on a fresh database it
24
+ * is the change. The constraint add is guarded by a `pg_constraint` probe
25
+ * because PostgreSQL has no `add constraint if not exists`.
26
+ *
27
+ * The column stays NULLABLE, as it was: the backfill and the NOT NULL flip
28
+ * are a separate step (feature 005 T060) and are not this migration's.
29
+ */
30
+ export class Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution extends Migration {
31
+ async up() {
32
+ this.addSql('alter table "quote_requests" add column if not exists "sales_channel_id" uuid null;');
33
+ this.addSql(`
34
+ do $$
35
+ begin
36
+ if not exists (
37
+ select 1 from pg_constraint
38
+ where "conname" = 'quote_requests_sales_channel_fk'
39
+ and "conrelid" = '"quote_requests"'::regclass
40
+ ) then
41
+ alter table "quote_requests"
42
+ add constraint "quote_requests_sales_channel_fk"
43
+ foreign key ("sales_channel_id") references "sales_channels" ("id")
44
+ on delete restrict;
45
+ end if;
46
+ end $$;
47
+ `);
48
+ this.addSql('create index if not exists "quote_requests_sales_channel_id_index" ' +
49
+ 'on "quote_requests" ("sales_channel_id");');
50
+ }
51
+ async down() {
52
+ this.addSql('drop index if exists "quote_requests_sales_channel_id_index";');
53
+ this.addSql('alter table "quote_requests" drop constraint if exists "quote_requests_sales_channel_fk";');
54
+ this.addSql('alter table "quote_requests" drop column if exists "sales_channel_id";');
55
+ }
56
+ }
57
+ //# sourceMappingURL=20260912T125614_quote_requests_quote_request_channel_attribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260912T125614_quote_requests_quote_request_channel_attribution.js","sourceRoot":"","sources":["../../src/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,mEAAoE,SAAQ,SAAS;IACvF,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,qFAAqF,CACtF,CAAC;QACF,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;KAcX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,qEAAqE;YACnE,2CAA2C,CAC9C,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,+DAA+D,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,CACT,2FAA2F,CAC5F,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,wEAAwE,CAAC,CAAC;IACxF,CAAC;CACF"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent — *"the `./migrations` export of
8
+ * @endora-commerce/mod-quote-requests exports no 'migrations' array"*. `blog`
9
+ * published only `export *` here until D-168, so an installed `blog` did not
10
+ * merely lose its schema, it stopped the platform from booting; the defect was
11
+ * invisible because a workspace member is not an installed package and the
12
+ * committed host registry names each class directly.
13
+ *
14
+ * **Six classes, and the order in this array is not the order they run in.**
15
+ * They are listed here in ascending timestamp, which is the order
16
+ * `backend/src/db/migration-order.ts` computes *within* a module — and within a
17
+ * module only. Where this module's block sits relative to every other module's
18
+ * is decided by the manifest `dependencies` graph, not by a timestamp and not by
19
+ * this file (feature 081). Two of these six share nothing but their day:
20
+ * `…T140415_…_qr_item_packaging` and `…T140417_…_business_id` are two minutes
21
+ * apart and both depend on the `…T141344_…_workflow` rewrite above them, which
22
+ * is exactly what "a timestamp orders a module's own migrations" buys.
23
+ *
24
+ * The **named** exports stay, and the asymmetry with `./backend` — which
25
+ * publishes an array and no named class (D-168) — is deliberate.
26
+ * `db/migrations-registry.generated.ts` imports each class by name from this
27
+ * specifier and hands it to `migration('quote_requests', …)`, and a migration
28
+ * class name is contract in a way an entity class name is not:
29
+ * `mikro_orm_migrations` persists it, so it is a string every already-migrated
30
+ * database holds. It also carries none of the hazard D-168 removes — no module
31
+ * has a reason to name another module's migration, and doing so buys nothing an
32
+ * entity import buys.
33
+ *
34
+ * A class that is in neither the array nor the barrel is a migration that does
35
+ * not run: `migration:pending` reports nothing pending and the first symptom is
36
+ * a query against a table nobody created. The composer refuses a migration file
37
+ * no declared subpath covers for exactly that reason, but it cannot see whether
38
+ * the barrel behind the subpath actually carries the class — that is this
39
+ * file's job.
40
+ *
41
+ * `status-mapping.ts` sits in this directory and is deliberately **not** here:
42
+ * it is a §4 helper, not a migration (see the note in `generate-composer.ts`).
43
+ */
44
+ import { Migration20260424T190112QuoteRequestsInit } from './20260424T190112_quote_requests_init.js';
45
+ import { Migration20260503T141344QuoteRequestsWorkflow } from './20260503T141344_quote_requests_workflow.js';
46
+ import { Migration20260611T140415QuoteRequestsQrItemPackaging } from './20260611T140415_quote_requests_qr_item_packaging.js';
47
+ import { Migration20260611T140417QuoteRequestsBusinessId } from './20260611T140417_quote_requests_business_id.js';
48
+ import { Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting } from './20260617T095510_quote_requests_backfill_admin_created_awaiting.js';
49
+ import { Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues } from './20260718T200342_quote_requests_quote_request_custom_field_values.js';
50
+ import { Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution } from './20260912T125614_quote_requests_quote_request_channel_attribution.js';
51
+ export declare const migrations: (typeof Migration20260424T190112QuoteRequestsInit)[];
52
+ export { Migration20260424T190112QuoteRequestsInit, Migration20260503T141344QuoteRequestsWorkflow, Migration20260611T140415QuoteRequestsQrItemPackaging, Migration20260611T140417QuoteRequestsBusinessId, Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting, Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues, Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution, };
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,EAAE,6CAA6C,EAAE,MAAM,8CAA8C,CAAC;AAC7G,OAAO,EAAE,oDAAoD,EAAE,MAAM,uDAAuD,CAAC;AAC7H,OAAO,EAAE,+CAA+C,EAAE,MAAM,iDAAiD,CAAC;AAClH,OAAO,EAAE,iEAAiE,EAAE,MAAM,qEAAqE,CAAC;AACxJ,OAAO,EAAE,kEAAkE,EAAE,MAAM,uEAAuE,CAAC;AAC3J,OAAO,EAAE,mEAAmE,EAAE,MAAM,uEAAuE,CAAC;AAE5J,eAAO,MAAM,UAAU,sDAQtB,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,6CAA6C,EAC7C,oDAAoD,EACpD,+CAA+C,EAC/C,iEAAiE,EACjE,kEAAkE,EAClE,mEAAmE,GACpE,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent — *"the `./migrations` export of
8
+ * @endora-commerce/mod-quote-requests exports no 'migrations' array"*. `blog`
9
+ * published only `export *` here until D-168, so an installed `blog` did not
10
+ * merely lose its schema, it stopped the platform from booting; the defect was
11
+ * invisible because a workspace member is not an installed package and the
12
+ * committed host registry names each class directly.
13
+ *
14
+ * **Six classes, and the order in this array is not the order they run in.**
15
+ * They are listed here in ascending timestamp, which is the order
16
+ * `backend/src/db/migration-order.ts` computes *within* a module — and within a
17
+ * module only. Where this module's block sits relative to every other module's
18
+ * is decided by the manifest `dependencies` graph, not by a timestamp and not by
19
+ * this file (feature 081). Two of these six share nothing but their day:
20
+ * `…T140415_…_qr_item_packaging` and `…T140417_…_business_id` are two minutes
21
+ * apart and both depend on the `…T141344_…_workflow` rewrite above them, which
22
+ * is exactly what "a timestamp orders a module's own migrations" buys.
23
+ *
24
+ * The **named** exports stay, and the asymmetry with `./backend` — which
25
+ * publishes an array and no named class (D-168) — is deliberate.
26
+ * `db/migrations-registry.generated.ts` imports each class by name from this
27
+ * specifier and hands it to `migration('quote_requests', …)`, and a migration
28
+ * class name is contract in a way an entity class name is not:
29
+ * `mikro_orm_migrations` persists it, so it is a string every already-migrated
30
+ * database holds. It also carries none of the hazard D-168 removes — no module
31
+ * has a reason to name another module's migration, and doing so buys nothing an
32
+ * entity import buys.
33
+ *
34
+ * A class that is in neither the array nor the barrel is a migration that does
35
+ * not run: `migration:pending` reports nothing pending and the first symptom is
36
+ * a query against a table nobody created. The composer refuses a migration file
37
+ * no declared subpath covers for exactly that reason, but it cannot see whether
38
+ * the barrel behind the subpath actually carries the class — that is this
39
+ * file's job.
40
+ *
41
+ * `status-mapping.ts` sits in this directory and is deliberately **not** here:
42
+ * it is a §4 helper, not a migration (see the note in `generate-composer.ts`).
43
+ */
44
+ import { Migration20260424T190112QuoteRequestsInit } from './20260424T190112_quote_requests_init.js';
45
+ import { Migration20260503T141344QuoteRequestsWorkflow } from './20260503T141344_quote_requests_workflow.js';
46
+ import { Migration20260611T140415QuoteRequestsQrItemPackaging } from './20260611T140415_quote_requests_qr_item_packaging.js';
47
+ import { Migration20260611T140417QuoteRequestsBusinessId } from './20260611T140417_quote_requests_business_id.js';
48
+ import { Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting } from './20260617T095510_quote_requests_backfill_admin_created_awaiting.js';
49
+ import { Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues } from './20260718T200342_quote_requests_quote_request_custom_field_values.js';
50
+ import { Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution } from './20260912T125614_quote_requests_quote_request_channel_attribution.js';
51
+ export const migrations = [
52
+ Migration20260424T190112QuoteRequestsInit,
53
+ Migration20260503T141344QuoteRequestsWorkflow,
54
+ Migration20260611T140415QuoteRequestsQrItemPackaging,
55
+ Migration20260611T140417QuoteRequestsBusinessId,
56
+ Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting,
57
+ Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues,
58
+ Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution,
59
+ ];
60
+ export { Migration20260424T190112QuoteRequestsInit, Migration20260503T141344QuoteRequestsWorkflow, Migration20260611T140415QuoteRequestsQrItemPackaging, Migration20260611T140417QuoteRequestsBusinessId, Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting, Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues, Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution, };
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,EAAE,6CAA6C,EAAE,MAAM,8CAA8C,CAAC;AAC7G,OAAO,EAAE,oDAAoD,EAAE,MAAM,uDAAuD,CAAC;AAC7H,OAAO,EAAE,+CAA+C,EAAE,MAAM,iDAAiD,CAAC;AAClH,OAAO,EAAE,iEAAiE,EAAE,MAAM,qEAAqE,CAAC;AACxJ,OAAO,EAAE,kEAAkE,EAAE,MAAM,uEAAuE,CAAC;AAC3J,OAAO,EAAE,mEAAmE,EAAE,MAAM,uEAAuE,CAAC;AAE5J,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,yCAAyC;IACzC,6CAA6C;IAC7C,oDAAoD;IACpD,+CAA+C;IAC/C,iEAAiE;IACjE,kEAAkE;IAClE,mEAAmE;CACpE,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,6CAA6C,EAC7C,oDAAoD,EACpD,+CAA+C,EAC/C,iEAAiE,EACjE,kEAAkE,EAClE,mEAAmE,GACpE,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Legacy → new status mapping for the Quote Requests workflow rewrite
3
+ * (feature 008). The mapping itself is pure so we can unit-test it
4
+ * without bringing up Postgres. The migration consumes this exact
5
+ * function in its data step.
6
+ *
7
+ * Spec reference: research.md §R1, data-model.md §8.
8
+ */
9
+ export type LegacyStatus = 'draft' | 'new' | 'under_review' | 'quoted' | 'accepted' | 'rejected' | 'expired';
10
+ export type NewStatus = 'Created from admin' | 'Pending' | 'Canceled' | 'Approved' | 'Completed' | 'Expired';
11
+ export interface MappingResult {
12
+ /** Target status. `null` means the row should be deleted (legacy `draft`). */
13
+ status: NewStatus | null;
14
+ /** Whether to set `awaiting_customer_revision_acceptance = true`. */
15
+ awaitingCustomerRevisionAcceptance: boolean;
16
+ }
17
+ export declare function mapLegacyStatus(legacy: LegacyStatus): MappingResult;
18
+ //# sourceMappingURL=status-mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-mapping.d.ts","sourceRoot":"","sources":["../../src/migrations/status-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,KAAK,GACL,cAAc,GACd,QAAQ,GACR,UAAU,GACV,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,MAAM,SAAS,GACjB,oBAAoB,GACpB,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,qEAAqE;IACrE,kCAAkC,EAAE,OAAO,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAkBnE"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Legacy → new status mapping for the Quote Requests workflow rewrite
3
+ * (feature 008). The mapping itself is pure so we can unit-test it
4
+ * without bringing up Postgres. The migration consumes this exact
5
+ * function in its data step.
6
+ *
7
+ * Spec reference: research.md §R1, data-model.md §8.
8
+ */
9
+ export function mapLegacyStatus(legacy) {
10
+ switch (legacy) {
11
+ case 'draft':
12
+ return { status: null, awaitingCustomerRevisionAcceptance: false };
13
+ case 'new':
14
+ case 'under_review':
15
+ return { status: 'Pending', awaitingCustomerRevisionAcceptance: false };
16
+ case 'quoted':
17
+ // Sales rep had issued a counter-quote; under the new model that is
18
+ // the "modified, awaiting customer accept/reject" state.
19
+ return { status: 'Pending', awaitingCustomerRevisionAcceptance: true };
20
+ case 'accepted':
21
+ return { status: 'Approved', awaitingCustomerRevisionAcceptance: false };
22
+ case 'rejected':
23
+ return { status: 'Canceled', awaitingCustomerRevisionAcceptance: false };
24
+ case 'expired':
25
+ return { status: 'Expired', awaitingCustomerRevisionAcceptance: false };
26
+ }
27
+ }
28
+ //# sourceMappingURL=status-mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-mapping.js","sourceRoot":"","sources":["../../src/migrations/status-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0BH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QACrE,KAAK,KAAK,CAAC;QACX,KAAK,cAAc;YACjB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QAC1E,KAAK,QAAQ;YACX,oEAAoE;YACpE,yDAAyD;YACzD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kCAAkC,EAAE,IAAI,EAAE,CAAC;QACzE,KAAK,UAAU;YACb,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QAC3E,KAAK,UAAU;YACb,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QAC3E,KAAK,SAAS;YACZ,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;IAC5E,CAAC;AACH,CAAC"}
@@ -0,0 +1,168 @@
1
+ ---
2
+ title: quote_requests
3
+ description: RFQ lifecycle (draft → quote → accept/reject)
4
+ ---
5
+
6
+ # `quote_requests`
7
+
8
+ The Quote Requests module implements the B2B negotiation loop.
9
+ A customer (or sales representative on the
10
+ customer's behalf) drafts a Quote Request, the other side reviews it,
11
+ either side may modify the request and require explicit re-acceptance,
12
+ and an approved Quote Request can be turned into an order through the
13
+ standard checkout. Every transition is captured in an append-only
14
+ event log so the customer-facing detail page and the admin detail page
15
+ both render the same chronological history.
16
+
17
+ ## Statuses
18
+
19
+ Six values, replacing the foundation-era set:
20
+
21
+ - `Created from admin` — drafted by a sales rep / admin, awaiting customer acceptance.
22
+ - `Pending` — submitted by the customer, awaiting internal-side response.
23
+ - `Approved` — green-lit by the responsible party.
24
+ - `Completed` — an order has been placed from this Quote Request.
25
+ - `Canceled` — rejected by either party (with optional reason).
26
+ - `Expired` — auto-flipped by the expiry worker when the configured threshold elapses.
27
+
28
+ `Canceled`, `Completed`, and `Expired` are terminal.
29
+
30
+ ## Public surface
31
+
32
+ Customer endpoints require a customer session; admin endpoints are
33
+ gated by `rfqs:handle`. PATCH-shaped endpoints accept `If-Match`
34
+ versions for optimistic concurrency, and customer accept/reject
35
+ revision additionally pin `expectedRevisionNumber`.
36
+
37
+ | Verb + Path | Audience | Purpose |
38
+ | --- | --- | --- |
39
+ | `GET /api/v1/quote-requests` | customer | List visible Quote Requests (org-admin role expands to whole-org visibility). |
40
+ | `GET /api/v1/quote-requests/:id` | customer | Detail with optional `comparisonAgainstLastSeen` block while awaiting acceptance. |
41
+ | `POST /api/v1/quote-requests` | customer | Create + submit in one call. |
42
+ | `PATCH /api/v1/quote-requests/:id` | customer | Edit a Pending RFQ that the internal side has not yet touched. |
43
+ | `POST /api/v1/quote-requests/:id/accept-revision` | customer | Accept the latest revision (also covers `Created from admin`). |
44
+ | `POST /api/v1/quote-requests/:id/reject-revision` | customer | Reject the latest revision with optional reason. |
45
+ | `POST /api/v1/quote-requests/:id/resubmit` | customer | Clone an old Quote Request into a new Pending one at the customer's current price list. |
46
+ | `GET /api/v1/admin/quote-requests` | admin | List, scoped by sales-rep assignment + filterable by status / organization. |
47
+ | `GET /api/v1/admin/quote-requests/:id` | admin | Detail with full actor identity in the event log. |
48
+ | `POST /api/v1/admin/quote-requests` | admin | Create on customer's behalf → status `Created from admin`. |
49
+ | `PATCH /api/v1/admin/quote-requests/:id` | admin | Modify a Pending or Created from admin RFQ → triggers customer re-acceptance. |
50
+ | `POST /api/v1/admin/quote-requests/:id/approve` | admin | Approve a Pending RFQ. |
51
+ | `POST /api/v1/admin/quote-requests/:id/cancel` | admin | Cancel with optional reason. |
52
+ | `POST /api/v1/admin/quote-requests/:id/assign` | admin | Set `assignedAdminUserId` (informational). |
53
+ | `GET /api/v1/admin/sales-reps/:adminUserId/organizations` | admin | Reverse view — organizations a rep is responsible for, with the count of open quote requests in each. |
54
+ | `GET /api/v1/storefront/settings/quote-requests` | public | Returns the two storefront visibility flags. |
55
+
56
+ The three endpoints that assign a sales representative *to* an organization —
57
+ `GET`, `POST` and `DELETE` under
58
+ `/api/v1/admin/organizations/:id/sales-reps` — belong to the **organizations**
59
+ module and are gated by `organizations:assign-sales-rep`, not by `rfqs:handle`.
60
+ They used to be registered here, and the split is not cosmetic: assigning a
61
+ representative qualifies an organization, so it must keep working when quote
62
+ requests is switched off, and it cannot be gated by a permission code declared
63
+ by a module that can disappear. The one endpoint left above is the one that
64
+ reads a quote request, and it is gated `rfqs:handle` precisely so that it
65
+ disappears with this module.
66
+
67
+ ## Visibility model
68
+
69
+ A sales representative is a platform administrator with the
70
+ `sales_representative` role. The
71
+ `SalesRepAssignmentService.canSeeOrganization(adminUserId, organizationId)`
72
+ predicate centralises the visibility rule:
73
+
74
+ 1. `platform_admin` role → sees every organization.
75
+ 2. Otherwise the admin sees an organization iff a row in
76
+ `organization_sales_rep_assignments` ties them together, OR the
77
+ organization has zero rows in that table (the "unassigned-org
78
+ fallback" — visible to every sales rep).
79
+
80
+ A customer with the `org_admin` role on their own organization sees
81
+ every Quote Request in the organization, not just their own.
82
+
83
+ ## Settings
84
+
85
+ Three settings drive the module — all live under the `quote_requests`
86
+ group and are configured through the existing settings module.
87
+
88
+ | Code | Type | Default | Effect |
89
+ | --- | --- | --- | --- |
90
+ | `quote_requests.expiry_days` | integer | `0` | Auto-expire Pending / Created from admin RFQs after N days. `0` disables. |
91
+ | `quote_requests.show_add_to_quote_on_card` | boolean | `true` | Toggle the "Add to quote" button on storefront product cards. |
92
+ | `quote_requests.show_add_to_quote_on_pdp` | boolean | `true` | Toggle the "Add to quote" button on product detail pages. |
93
+
94
+ ## Background jobs
95
+
96
+ `RfqExpiryWorker.sweep()` runs every 30 minutes via the foundation
97
+ BullMQ scheduler. It reads `quote_requests.expiry_days` from the
98
+ settings module's resolved snapshot; if that value is 0 the sweep is
99
+ a no-op. Otherwise it transitions every Pending and Created from
100
+ admin row whose `updated_at < now() - INTERVAL <expiryDays> days` to
101
+ `Expired`, writes one `expired` event per row, and fans out
102
+ notifications to both parties.
103
+
104
+ ## Data model
105
+
106
+ Three tables on top of the foundation `quote_requests` and
107
+ `quote_request_items`:
108
+
109
+ - `quote_request_revisions` — full snapshot per modify event.
110
+ - `quote_request_events` — append-only history (one row per state
111
+ transition or modification, with discriminated-union payload).
112
+ - `quote_request_notification_events` — one row per recipient ×
113
+ channel; unique on `(quote_request_id, source_event_id, recipient*,
114
+ channel)` so retries are idempotent.
115
+
116
+ `organization_sales_rep_assignments` — the m:n relation between
117
+ organizations and admin users that the visibility model above reads —
118
+ is **not** one of them: it is owned by the `organizations` module,
119
+ which qualifies the organization with it, and this module reaches it
120
+ through that module's `organizationSalesRepScopePort`.
121
+
122
+ The canonical `quote_requests` row carries the current state plus
123
+ `current_revision_number`, `last_customer_seen_revision_number`, and
124
+ `awaiting_customer_revision_acceptance`. The customer's "what changed
125
+ since I last visited" diff is computed on read by comparing the
126
+ revision identified by `last_customer_seen_revision_number` against
127
+ the revision identified by `current_revision_number`.
128
+
129
+ ### `sales_channel_id` is nullable, and stays nullable
130
+
131
+ Every request records the sales channel it was raised on, taken from
132
+ the resolved request channel. The column is nullable and will remain
133
+ so.
134
+
135
+ It was added nullable on purpose: the deploy that shipped it must not
136
+ depend on the boot-time default-channel reconciler having already run,
137
+ which is the ordinary phased shape — add nullable, start writing,
138
+ backfill, flip to `NOT NULL`. The middle step is the one that cannot
139
+ be taken here. Nothing wrote the column between the migration that
140
+ added it and the change that started populating it, so every request
141
+ raised in that window carries `null`, and no record anywhere says
142
+ which channel it came from. Projecting the system-default channel over
143
+ that gap would not recover an attribution, it would invent one — and
144
+ the sales-channel delete guard would then start refusing deletions on
145
+ evidence the platform made up.
146
+
147
+ A deployment that genuinely needs the column non-nullable therefore
148
+ deletes the null tail, or takes a per-row answer from a source that
149
+ knows one. There is no backfill script, and there deliberately never
150
+ will be one.
151
+
152
+ ## Notifications
153
+
154
+ Every state transition fans out through `RfqNotificationService` to
155
+ the appropriate recipients (customer for admin-side actions, sales
156
+ reps + platform admins for customer-side actions, both parties on
157
+ expiry). Email and in-account channels both fire. The unique
158
+ constraint on `quote_request_notification_events` guarantees once-only
159
+ delivery per (transition, recipient, channel).
160
+
161
+ ## Conversion to order
162
+
163
+ When an order is created with a populated `source_quote_request_id`,
164
+ an event subscriber inside the module flips the originating Quote
165
+ Request to `Completed`, populates `converted_order_id`, and fires the
166
+ `completed` notification. The cart-creation step that locks RFQ
167
+ agreed prices into a checkout cart is delivered through the existing
168
+ cart and checkout flows.
package/i18n/en.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "actions.openInbox.label": "Open RFQ inbox",
3
+ "actions.openInbox.description": "Review pending quote requests",
4
+ "errors.RFQ_NOT_DRAFT": "Rfq Not Draft.",
5
+ "errors.RFQ_EMPTY": "Rfq Empty.",
6
+ "errors.RFQ_NOT_QUOTED": "Rfq Not Quoted.",
7
+ "errors.RFQ_NOT_ACCEPTED": "Rfq Not Accepted.",
8
+ "errors.RFQ_NOT_NEW": "Rfq Not New.",
9
+ "errors.RFQ_ALREADY_CLAIMED": "Rfq Already Claimed.",
10
+ "errors.RFQ_EXPIRED": "This quote is no longer valid — its validity period has ended. Ask your sales representative for a new quote, or submit the request again.",
11
+ "errors.QUOTE_INCOMPLETE": "Quote Incomplete.",
12
+ "errors.QUOTE_VALIDITY_ENDED": "The accepted quote is no longer valid — its validity period has ended. Ask your sales representative for a new quote before ordering.",
13
+ "nav.quoteRequests.label": "Quote requests"
14
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "actions.openInbox.label": "Otwórz skrzynkę zapytań",
3
+ "actions.openInbox.description": "Przejrzyj oczekujące zapytania ofertowe",
4
+ "errors.RFQ_NOT_DRAFT": "Błąd: rfq not draft.",
5
+ "errors.RFQ_EMPTY": "Błąd: rfq empty.",
6
+ "errors.RFQ_NOT_QUOTED": "Błąd: rfq not quoted.",
7
+ "errors.RFQ_NOT_ACCEPTED": "Błąd: rfq not accepted.",
8
+ "errors.RFQ_NOT_NEW": "Błąd: rfq not new.",
9
+ "errors.RFQ_ALREADY_CLAIMED": "Błąd: rfq already claimed.",
10
+ "errors.RFQ_EXPIRED": "Ta oferta straciła ważność — termin jej ważności upłynął. Poproś opiekuna handlowego o nową ofertę lub złóż zapytanie ponownie.",
11
+ "errors.QUOTE_INCOMPLETE": "Błąd: quote incomplete.",
12
+ "errors.QUOTE_VALIDITY_ENDED": "Zaakceptowana oferta straciła ważność — termin jej ważności upłynął. Przed złożeniem zamówienia poproś opiekuna handlowego o nową ofertę.",
13
+ "nav.quoteRequests.label": "Zapytania ofertowe"
14
+ }
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@endora-commerce/mod-quote-requests",
3
+ "version": "0.100.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "Customer-initiated RFQ workflow with admin pricing, approvals, and expiry.",
7
+ "license": "MIT",
8
+ "endora": {
9
+ "type": "module",
10
+ "id": "quote_requests"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/endora-commerce/endora-commerce.git",
15
+ "directory": "packages/modules/quote_requests"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/manifest.d.ts",
23
+ "default": "./dist/manifest.js"
24
+ },
25
+ "./backend": {
26
+ "types": "./dist/backend/index.d.ts",
27
+ "default": "./dist/backend/index.js"
28
+ },
29
+ "./migrations": {
30
+ "types": "./dist/migrations/index.d.ts",
31
+ "default": "./dist/migrations/index.js"
32
+ },
33
+ "./admin": {
34
+ "types": "./dist/admin/index.d.ts",
35
+ "default": "./dist/admin/index.js"
36
+ },
37
+ "./tailwind.css": "./tailwind.css",
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "i18n",
43
+ "docs",
44
+ "tailwind.css"
45
+ ],
46
+ "engines": {
47
+ "node": ">=22.18.0"
48
+ },
49
+ "peerDependencies": {
50
+ "@mikro-orm/core": "^6",
51
+ "@mikro-orm/migrations": "^6",
52
+ "@mikro-orm/postgresql": "^6",
53
+ "fastify": "^5",
54
+ "lucide-react": "^1",
55
+ "react": "^19",
56
+ "react-router-dom": "^7",
57
+ "zod": "^4",
58
+ "@endora-commerce/admin-kit": "0.100.0",
59
+ "@endora-commerce/contracts": "0.100.0",
60
+ "@endora-commerce/platform": "0.100.0"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "@endora-commerce/admin-kit": {
64
+ "optional": true
65
+ },
66
+ "lucide-react": {
67
+ "optional": true
68
+ },
69
+ "react": {
70
+ "optional": true
71
+ },
72
+ "react-router-dom": {
73
+ "optional": true
74
+ }
75
+ },
76
+ "devDependencies": {
77
+ "@mikro-orm/core": "^6.6.13",
78
+ "@mikro-orm/migrations": "^6.6.13",
79
+ "@mikro-orm/postgresql": "^6.6.13",
80
+ "@types/node": "^22.9.0",
81
+ "@types/react": "^19.2.14",
82
+ "fastify": "^5.12.5",
83
+ "lucide-react": "^1.11.0",
84
+ "react": "^19.2.5",
85
+ "react-router-dom": "^7.18.2",
86
+ "typescript": "^5.9.3",
87
+ "vitest": "^4.1.11",
88
+ "zod": "^4.2.0",
89
+ "@endora-commerce/admin-kit": "0.100.0",
90
+ "@endora-commerce/platform": "0.100.0",
91
+ "@endora-commerce/contracts": "0.100.0"
92
+ },
93
+ "scripts": {
94
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.json",
95
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.ui.json --noEmit",
96
+ "lint": "eslint src",
97
+ "test": "vitest run"
98
+ }
99
+ }
package/tailwind.css ADDED
@@ -0,0 +1,14 @@
1
+ /* @endora-commerce/mod-quote-requests — AUTO-GENERATED by `pnpm --filter backend run manifests:generate`.
2
+ *
3
+ * The `@source` directives this package asks its host to scan
4
+ * (`specs/110-instance-repository/contracts/admin-stylesheet-composition.md` R1).
5
+ * They resolve relative to **this file**, so they hold wherever the package is
6
+ * installed — a workspace link here, `node_modules` in a client's instance.
7
+ *
8
+ * The `dist` line is what a published tarball ships and is what an instance
9
+ * scans; the `src` line is inert there and is what keeps `pnpm --filter admin
10
+ * run dev` reading source in this repository. Do not edit: run
11
+ * `pnpm --filter backend run manifests:generate`.
12
+ */
13
+ @source "./dist/admin";
14
+ @source "./src/admin";