@endora-commerce/mod-customer-accounts 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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/admin/api/customer-groups-client.d.ts +27 -0
  4. package/dist/admin/api/customer-groups-client.d.ts.map +1 -0
  5. package/dist/admin/api/customer-groups-client.js +17 -0
  6. package/dist/admin/api/customer-groups-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +33 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +38 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/CustomerGroupsPage.d.ts +18 -0
  12. package/dist/admin/pages/CustomerGroupsPage.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomerGroupsPage.js +108 -0
  14. package/dist/admin/pages/CustomerGroupsPage.js.map +1 -0
  15. package/dist/backend/entities/customer-account.entity.d.ts +111 -0
  16. package/dist/backend/entities/customer-account.entity.d.ts.map +1 -0
  17. package/dist/backend/entities/customer-account.entity.js +230 -0
  18. package/dist/backend/entities/customer-account.entity.js.map +1 -0
  19. package/dist/backend/entities/customer-group.entity.d.ts +29 -0
  20. package/dist/backend/entities/customer-group.entity.d.ts.map +1 -0
  21. package/dist/backend/entities/customer-group.entity.js +70 -0
  22. package/dist/backend/entities/customer-group.entity.js.map +1 -0
  23. package/dist/backend/entities/password-reset-token.entity.d.ts +16 -0
  24. package/dist/backend/entities/password-reset-token.entity.d.ts.map +1 -0
  25. package/dist/backend/entities/password-reset-token.entity.js +59 -0
  26. package/dist/backend/entities/password-reset-token.entity.js.map +1 -0
  27. package/dist/backend/index.d.ts +144 -0
  28. package/dist/backend/index.d.ts.map +1 -0
  29. package/dist/backend/index.js +407 -0
  30. package/dist/backend/index.js.map +1 -0
  31. package/dist/backend/routes.admin.d.ts +26 -0
  32. package/dist/backend/routes.admin.d.ts.map +1 -0
  33. package/dist/backend/routes.admin.js +35 -0
  34. package/dist/backend/routes.admin.js.map +1 -0
  35. package/dist/backend/services/audit-references.d.ts +22 -0
  36. package/dist/backend/services/audit-references.d.ts.map +1 -0
  37. package/dist/backend/services/audit-references.js +46 -0
  38. package/dist/backend/services/audit-references.js.map +1 -0
  39. package/dist/backend/services/customer-account-lifecycle-ports.d.ts +168 -0
  40. package/dist/backend/services/customer-account-lifecycle-ports.d.ts.map +1 -0
  41. package/dist/backend/services/customer-account-lifecycle-ports.js +420 -0
  42. package/dist/backend/services/customer-account-lifecycle-ports.js.map +1 -0
  43. package/dist/backend/services/customer-account-ports.d.ts +143 -0
  44. package/dist/backend/services/customer-account-ports.d.ts.map +1 -0
  45. package/dist/backend/services/customer-account-ports.js +422 -0
  46. package/dist/backend/services/customer-account-ports.js.map +1 -0
  47. package/dist/backend/services/customer-auth-service.d.ts +62 -0
  48. package/dist/backend/services/customer-auth-service.d.ts.map +1 -0
  49. package/dist/backend/services/customer-auth-service.js +105 -0
  50. package/dist/backend/services/customer-auth-service.js.map +1 -0
  51. package/dist/backend/services/customer-group-read-port.d.ts +24 -0
  52. package/dist/backend/services/customer-group-read-port.d.ts.map +1 -0
  53. package/dist/backend/services/customer-group-read-port.js +44 -0
  54. package/dist/backend/services/customer-group-read-port.js.map +1 -0
  55. package/dist/backend/services/customer-group-service.d.ts +21 -0
  56. package/dist/backend/services/customer-group-service.d.ts.map +1 -0
  57. package/dist/backend/services/customer-group-service.js +97 -0
  58. package/dist/backend/services/customer-group-service.js.map +1 -0
  59. package/dist/backend/services/customer-rollup-scope.d.ts +30 -0
  60. package/dist/backend/services/customer-rollup-scope.d.ts.map +1 -0
  61. package/dist/backend/services/customer-rollup-scope.js +42 -0
  62. package/dist/backend/services/customer-rollup-scope.js.map +1 -0
  63. package/dist/backend/services/password-reset-service.d.ts +15 -0
  64. package/dist/backend/services/password-reset-service.d.ts.map +1 -0
  65. package/dist/backend/services/password-reset-service.js +90 -0
  66. package/dist/backend/services/password-reset-service.js.map +1 -0
  67. package/dist/backend/services/role-service.d.ts +22 -0
  68. package/dist/backend/services/role-service.d.ts.map +1 -0
  69. package/dist/backend/services/role-service.js +83 -0
  70. package/dist/backend/services/role-service.js.map +1 -0
  71. package/dist/backend/services/two-factor-enrolments.d.ts +18 -0
  72. package/dist/backend/services/two-factor-enrolments.d.ts.map +1 -0
  73. package/dist/backend/services/two-factor-enrolments.js +2 -0
  74. package/dist/backend/services/two-factor-enrolments.js.map +1 -0
  75. package/dist/manifest.d.ts +169 -0
  76. package/dist/manifest.d.ts.map +1 -0
  77. package/dist/manifest.js +212 -0
  78. package/dist/manifest.js.map +1 -0
  79. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts +6 -0
  80. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts.map +1 -0
  81. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js +25 -0
  82. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js.map +1 -0
  83. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts +34 -0
  84. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts.map +1 -0
  85. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js +43 -0
  86. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js.map +1 -0
  87. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts +54 -0
  88. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts.map +1 -0
  89. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js +97 -0
  90. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js.map +1 -0
  91. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts +12 -0
  92. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts.map +1 -0
  93. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js +16 -0
  94. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js.map +1 -0
  95. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts +14 -0
  96. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts.map +1 -0
  97. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js +18 -0
  98. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js.map +1 -0
  99. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts +22 -0
  100. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts.map +1 -0
  101. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js +26 -0
  102. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js.map +1 -0
  103. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts +46 -0
  104. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts.map +1 -0
  105. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js +97 -0
  106. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js.map +1 -0
  107. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts +31 -0
  108. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts.map +1 -0
  109. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js +35 -0
  110. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js.map +1 -0
  111. package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts +58 -0
  112. package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts.map +1 -0
  113. package/dist/migrations/20260825T141659_customer_accounts_organization_required.js +133 -0
  114. package/dist/migrations/20260825T141659_customer_accounts_organization_required.js.map +1 -0
  115. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts +27 -0
  116. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts.map +1 -0
  117. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js +44 -0
  118. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js.map +1 -0
  119. package/dist/migrations/index.d.ts +36 -0
  120. package/dist/migrations/index.d.ts.map +1 -0
  121. package/dist/migrations/index.js +47 -0
  122. package/dist/migrations/index.js.map +1 -0
  123. package/docs/customer_accounts.md +38 -0
  124. package/i18n/en.json +30 -0
  125. package/i18n/pl.json +30 -0
  126. package/package.json +94 -0
  127. package/tailwind.css +14 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ <!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
2
+
3
+ # @endora-commerce/mod-customer-accounts
4
+
5
+ Customer (B2B) account records, including organization membership and role.
6
+
7
+ ## What this is
8
+
9
+ An **Endora Commerce module package**. Its module id is `customer_accounts` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
10
+
11
+ A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
12
+
13
+ ## Entry points
14
+
15
+ | Import | Contents |
16
+ | --- | --- |
17
+ | `@endora-commerce/mod-customer-accounts` | the module manifest — its id, version, dependencies, settings and activation |
18
+ | `@endora-commerce/mod-customer-accounts/admin` | the Admin UI contribution: the screens, navigation and palette actions it adds |
19
+ | `@endora-commerce/mod-customer-accounts/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
20
+ | `@endora-commerce/mod-customer-accounts/migrations` | the module’s own schema migrations, in the order the platform runs them |
21
+ | `@endora-commerce/mod-customer-accounts/tailwind.css` | the Tailwind source declaration for the admin code above |
22
+
23
+ ## Depends on
24
+
25
+ Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
26
+
27
+ **Endora packages**
28
+
29
+ - `@endora-commerce/admin-kit` — *optional*
30
+ - `@endora-commerce/contracts`
31
+ - `@endora-commerce/mod-organizations`
32
+ - `@endora-commerce/platform`
33
+
34
+ **Third-party**
35
+
36
+ - `@mikro-orm/core` ^6
37
+ - `@mikro-orm/migrations` ^6
38
+ - `@mikro-orm/postgresql` ^6
39
+ - `fastify` ^5
40
+ - `lucide-react` ^1 — *optional*
41
+ - `react` ^19 — *optional*
42
+ - `zod` ^4
43
+
44
+ ## What the tarball carries
45
+
46
+ - `dist/` — the compiled JavaScript and its type declarations
47
+ - `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
48
+ - `docs/` — the package’s own pages in the Endora Commerce documentation site
49
+ - `tailwind.css` — the Tailwind source declaration
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pnpm add @endora-commerce/mod-customer-accounts
55
+ ```
56
+
57
+ ## Licence
58
+
59
+ MIT — the text is in `LICENSE`, beside this file.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Typed admin client for customer groups (feature 076, D-79).
3
+ *
4
+ * The endpoints moved from `price_lists` to `customer_accounts` without
5
+ * changing path or payload, so this client is the same three calls the
6
+ * `CustomerGroupPicker` has always made — plus the two writes, which had no
7
+ * caller in the SPA at all until this screen.
8
+ */
9
+ export interface AdminCustomerGroup {
10
+ id: string;
11
+ code: string;
12
+ name: string;
13
+ description: string | null;
14
+ createdAt: string;
15
+ updatedAt: string;
16
+ }
17
+ export interface CustomerGroupUpsertBody {
18
+ code: string;
19
+ name: string;
20
+ description?: string | null;
21
+ }
22
+ export declare const customerGroupsClient: {
23
+ list(): Promise<AdminCustomerGroup[]>;
24
+ upsert(code: string, body: CustomerGroupUpsertBody): Promise<AdminCustomerGroup>;
25
+ remove(id: string): Promise<void>;
26
+ };
27
+ //# sourceMappingURL=customer-groups-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer-groups-client.d.ts","sourceRoot":"","sources":["../../../src/admin/api/customer-groups-client.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,oBAAoB;YACvB,OAAO,CAAC,kBAAkB,EAAE,CAAC;iBAKxB,MAAM,QAAQ,uBAAuB,GAAG,OAAO,CAAC,kBAAkB,CAAC;eAQrE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGlC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { apiClient } from '@endora-commerce/admin-kit/lib';
2
+ export const customerGroupsClient = {
3
+ list() {
4
+ return apiClient
5
+ .get('/api/v1/admin/customer-groups')
6
+ .then((r) => r.data);
7
+ },
8
+ upsert(code, body) {
9
+ return apiClient
10
+ .put(`/api/v1/admin/customer-groups/${encodeURIComponent(code)}`, body)
11
+ .then((r) => r.data);
12
+ },
13
+ remove(id) {
14
+ return apiClient.delete(`/api/v1/admin/customer-groups/${id}`);
15
+ },
16
+ };
17
+ //# sourceMappingURL=customer-groups-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer-groups-client.js","sourceRoot":"","sources":["../../../src/admin/api/customer-groups-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAyB3D,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI;QACF,OAAO,SAAS;aACb,GAAG,CAAiC,+BAA+B,CAAC;aACpE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,CAAC,IAAY,EAAE,IAA6B;QAChD,OAAO,SAAS;aACb,GAAG,CACF,iCAAiC,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAC3D,IAAI,CACL;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,CAAC,EAAU;QACf,OAAO,SAAS,CAAC,MAAM,CAAO,iCAAiC,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;CACF,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `customer_accounts`' admin surface — one route and one sidebar entry,
3
+ * declared by the module that owns them (feature 091, Phase 4, the plan's
4
+ * batch 7;
5
+ * `specs/091-module-owned-admin-surfaces/contracts/admin-contribution.md`).
6
+ *
7
+ * **The screen is customer *groups*, and the module that owns it is the one
8
+ * that owns the account** — feature 076's D-79, unchanged by the move. The
9
+ * route is `/customer-groups`, its API is `routes.admin.ts`' three handlers,
10
+ * and the codes are this module's own `customer_groups:*` pair.
11
+ *
12
+ * **This module declares `activation.nonDeactivatable`**, which changes what
13
+ * its off-state proof can drive and not whether it has one. A lock removes the
14
+ * **operator's** ability to make `isPresent` answer `false`; it removes nothing
15
+ * from the frontend's question, because the admin asks presence of every module
16
+ * whatever its manifest says and the platform axis — a deployment that never
17
+ * installs it — reaches the same gate. That is batch four's ruling
18
+ * (`plan.md` Ruling 2) and `audit_logs` is the worked example.
19
+ *
20
+ * **Its sidebar position does not change.** Every other row in *Customers* is
21
+ * still host-declared, so `composeNav` appends this one after all of them —
22
+ * which is where the hand-written table already had it, fifth of six, above
23
+ * `/credit-limits`. The declared weight (400, the hand-written position times a
24
+ * hundred, batch four's convention) is what will restore the order once that
25
+ * section's other rows convert; there is nothing to order it against today.
26
+ *
27
+ * **This entry exports data and nothing else** (R2), like every `./admin`
28
+ * layer, and each component is a dynamic-import factory (R6) so Vite has a
29
+ * split point whether or not anybody remembers to ask for one.
30
+ */
31
+ import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
32
+ export declare const contributions: AdminContributions;
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAenF,eAAO,MAAM,aAAa,EAAE,kBAyB3B,CAAC"}
@@ -0,0 +1,38 @@
1
+ /** The module's only admin route: the customer-group list and editor. */
2
+ const ROUTE_PATH = '/customer-groups';
3
+ /**
4
+ * The code the screen's entry route enforces —
5
+ * `requireAdmin('customer_groups:read')` on
6
+ * `GET /api/v1/admin/customer-groups` (`routes.admin.ts:37`). The two write
7
+ * handlers beside it take `customer_groups:write`, which is what the screen's
8
+ * own controls are gated on; the read code is what opens it, and it is the code
9
+ * the module's `open-customer-groups` palette action already declares.
10
+ */
11
+ const PERMISSION = 'customer_groups:read';
12
+ export const contributions = {
13
+ routes: [
14
+ {
15
+ path: ROUTE_PATH,
16
+ component: () => import('./pages/CustomerGroupsPage.js'),
17
+ requiredPermission: PERMISSION,
18
+ index: true,
19
+ },
20
+ ],
21
+ nav: [
22
+ {
23
+ to: ROUTE_PATH,
24
+ // Module-relative (R8), resolved in this module's own namespace out of
25
+ // `packages/modules/customer_accounts/i18n/`. It was
26
+ // `appShell.nav.customerGroups` in the shared `_i18n` bundle, one of the
27
+ // four shared files a module author had to edit.
28
+ labelKey: 'nav.customerGroups.label',
29
+ // The glyph `AppShell.tsx` rendered by hand, and already on
30
+ // `KnownIconNameSchema` — this module's own palette action names it.
31
+ icon: 'Users',
32
+ section: 'customers',
33
+ weight: 400,
34
+ requiredPermission: PERMISSION,
35
+ },
36
+ ],
37
+ };
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAgCA,yEAAyE;AACzE,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,+BAA+B,CAAC;YACxD,kBAAkB,EAAE,UAAU;YAC9B,KAAK,EAAE,IAAI;SACZ;KACF;IACD,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,qDAAqD;YACrD,yEAAyE;YACzE,iDAAiD;YACjD,QAAQ,EAAE,0BAA0B;YACpC,4DAA4D;YAC5D,qEAAqE;YACrE,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,UAAU;SAC/B;KACF;CACF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Customer groups — the management screen (feature 076, D-79).
4
+ *
5
+ * The `PUT` and `DELETE` endpoints existed with no caller in this repository:
6
+ * the SPA only ever listed groups, from three pickers. This screen is the
7
+ * missing half. It writes through the same two endpoints, which is why creating
8
+ * and editing are one form — the endpoint is an upsert keyed by `code`, so the
9
+ * code is fixed once the group exists.
10
+ */
11
+ export declare function CustomerGroupsPage(): ReactNode;
12
+ /**
13
+ * The default export the route declaration's dynamic-import factory takes
14
+ * (`contracts/admin-contribution.md` R6). The named export is kept because the
15
+ * screen is also the subject of this module's own admin tests.
16
+ */
17
+ export default CustomerGroupsPage;
18
+ //# sourceMappingURL=CustomerGroupsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomerGroupsPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/CustomerGroupsPage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAUzF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,CA0K9C;AA4ED;;;;GAIG;AACH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { Pencil, Trash2 } from 'lucide-react';
4
+ import { ApiError, useAuth } from '@endora-commerce/admin-kit/lib';
5
+ import { Alert, AlertDescription, Button, Card, CardContent, CardHeader, CardTitle, Input, Label, PageHeader, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@endora-commerce/admin-kit/ui';
6
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
7
+ import { customerGroupsClient, } from '../api/customer-groups-client.js';
8
+ /**
9
+ * Customer groups — the management screen (feature 076, D-79).
10
+ *
11
+ * The `PUT` and `DELETE` endpoints existed with no caller in this repository:
12
+ * the SPA only ever listed groups, from three pickers. This screen is the
13
+ * missing half. It writes through the same two endpoints, which is why creating
14
+ * and editing are one form — the endpoint is an upsert keyed by `code`, so the
15
+ * code is fixed once the group exists.
16
+ */
17
+ export function CustomerGroupsPage() {
18
+ const t = useTranslation('customer_accounts');
19
+ const { hasPermission } = useAuth();
20
+ const canWrite = hasPermission('customer_groups:write');
21
+ const [rows, setRows] = useState([]);
22
+ const [loading, setLoading] = useState(true);
23
+ const [error, setError] = useState(null);
24
+ const [info, setInfo] = useState(null);
25
+ // `null` = the blank "new group" form; a row = an edit seeded from it. The
26
+ // nonce remounts the blank form after a save so it does not keep stale input.
27
+ const [editing, setEditing] = useState(null);
28
+ const [formNonce, setFormNonce] = useState(0);
29
+ const refresh = useCallback(async () => {
30
+ setLoading(true);
31
+ setError(null);
32
+ try {
33
+ setRows(await customerGroupsClient.list());
34
+ }
35
+ catch (err) {
36
+ setError(err instanceof ApiError ? err.envelope.error.message : t('groups.error.load'));
37
+ }
38
+ finally {
39
+ setLoading(false);
40
+ }
41
+ }, [t]);
42
+ useEffect(() => {
43
+ void refresh();
44
+ }, [refresh]);
45
+ const resetForm = useCallback(() => {
46
+ setEditing(null);
47
+ setFormNonce((n) => n + 1);
48
+ }, []);
49
+ const handleSubmit = useCallback(async (value) => {
50
+ setError(null);
51
+ try {
52
+ await customerGroupsClient.upsert(value.code, {
53
+ code: value.code,
54
+ name: value.name,
55
+ description: value.description.trim() === '' ? null : value.description,
56
+ });
57
+ setInfo(t('groups.message.saved', { code: value.code }));
58
+ resetForm();
59
+ await refresh();
60
+ }
61
+ catch (err) {
62
+ setError(err instanceof ApiError ? err.envelope.error.message : t('groups.error.save'));
63
+ }
64
+ }, [refresh, resetForm, t]);
65
+ const handleDelete = useCallback(async (row) => {
66
+ if (!confirm(t('groups.action.delete.confirm', { code: row.code })))
67
+ return;
68
+ setError(null);
69
+ try {
70
+ await customerGroupsClient.remove(row.id);
71
+ if (editing?.id === row.id)
72
+ resetForm();
73
+ setInfo(t('groups.message.deleted'));
74
+ await refresh();
75
+ }
76
+ catch (err) {
77
+ setError(err instanceof ApiError ? err.envelope.error.message : t('groups.error.delete'));
78
+ }
79
+ }, [editing, refresh, resetForm, t]);
80
+ const startEdit = useCallback((row) => {
81
+ setError(null);
82
+ setInfo(null);
83
+ setEditing(row);
84
+ if (typeof window !== 'undefined')
85
+ window.scrollTo({ top: 0, behavior: 'smooth' });
86
+ }, []);
87
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: t('groups.page.title'), description: t('groups.page.description') }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, info ? (_jsx(Alert, { variant: "success", className: "mb-4", children: _jsx(AlertDescription, { children: info }) })) : null, canWrite ? (_jsxs(Card, { className: "mb-4", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: editing
88
+ ? t('groups.form.editTitle', { code: editing.code })
89
+ : t('groups.form.newTitle') }) }), _jsx(CardContent, { children: _jsx(GroupForm, { initial: editing, onSubmit: handleSubmit, ...(editing ? { onCancel: resetForm } : {}) }, editing ? `edit-${editing.id}` : `new-${formNonce}`) })] })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('groups.loading') })) : rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('groups.empty') })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t('groups.column.code') }), _jsx(TableHead, { children: t('groups.column.name') }), _jsx(TableHead, { children: t('groups.column.description') }), _jsx(TableHead, {})] }) }), _jsx(TableBody, { children: rows.map((row) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx("code", { className: "font-mono text-xs", children: row.code }) }), _jsx(TableCell, { children: row.name }), _jsx(TableCell, { className: "text-muted-foreground", children: row.description ?? '—' }), _jsx(TableCell, { children: canWrite ? (_jsxs("div", { className: "flex justify-end gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", type: "button", onClick: () => startEdit(row), children: [_jsx(Pencil, {}), t('groups.action.edit')] }), _jsxs(Button, { variant: "destructive", size: "sm", type: "button", onClick: () => void handleDelete(row), children: [_jsx(Trash2, {}), t('groups.action.delete')] })] })) : null })] }, row.id))) })] })) }) })] }));
90
+ }
91
+ function GroupForm({ initial, onSubmit, onCancel, }) {
92
+ const t = useTranslation('customer_accounts');
93
+ const isEdit = Boolean(initial);
94
+ const [code, setCode] = useState(initial?.code ?? '');
95
+ const [name, setName] = useState(initial?.name ?? '');
96
+ const [description, setDescription] = useState(initial?.description ?? '');
97
+ return (_jsxs("form", { className: "space-y-4", onSubmit: (e) => {
98
+ e.preventDefault();
99
+ void onSubmit({ code, name, description });
100
+ }, children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "cgcode", children: t('groups.field.code') }), _jsx(Input, { id: "cgcode", value: code, onChange: (e) => setCode(e.target.value), required: true, disabled: isEdit, "aria-describedby": "cgcode-help" }), _jsx("p", { id: "cgcode-help", className: "text-xs text-muted-foreground", children: t('groups.field.code.help') })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "cgname", children: t('groups.field.name') }), _jsx(Input, { id: "cgname", value: name, onChange: (e) => setName(e.target.value), required: true })] }), _jsxs("div", { className: "space-y-2 md:col-span-2", children: [_jsx(Label, { htmlFor: "cgdesc", children: t('groups.field.description') }), _jsx(Input, { id: "cgdesc", value: description, onChange: (e) => setDescription(e.target.value) })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "submit", children: t('groups.action.save') }), onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, children: t('groups.action.cancel') })) : null] })] }));
101
+ }
102
+ /**
103
+ * The default export the route declaration's dynamic-import factory takes
104
+ * (`contracts/admin-contribution.md` R6). The named export is kept because the
105
+ * screen is also the subject of this module's own admin tests.
106
+ */
107
+ export default CustomerGroupsPage;
108
+ //# sourceMappingURL=CustomerGroupsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomerGroupsPage.js","sourceRoot":"","sources":["../../../src/admin/pages/CustomerGroupsPage.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * CustomerAccount — a user inside an Organization. One email ⇒ at most one
4
+ * CustomerAccount installation-wide (FR-041).
5
+ *
6
+ * `passwordHash` is an argon2id-derived key (see services/password-hasher.ts
7
+ * in the auth module).
8
+ */
9
+ export declare class CustomerAccount {
10
+ [OptionalProps]?: 'customFieldValues' | 'id' | 'createdAt' | 'updatedAt' | 'role' | 'emailVerifiedAt' | 'passwordSetAt' | 'twoFactorConfirmedAt' | 'lastLoginAt' | 'deletedAt' | 'customerGroupId' | 'subtreeRollupEnabled' | 'blockedAt' | 'blockReason' | 'blockSource' | 'blockedByAdminUserId' | 'blockedByCustomerAccountId' | 'deletionRequestedByAdminUserId' | 'anonymizedAt';
11
+ id: string;
12
+ /**
13
+ * The tenant that scopes this account, and the platform's single unit of
14
+ * tenancy (Principle XI).
15
+ *
16
+ * **Not nullable, since D-178.** Feature 026 US2 relaxed it so the platform
17
+ * could serve "no-org / guest-style" accounts falling back to platform
18
+ * defaults for prices, credit limit, allowed payment and delivery methods and
19
+ * warehouse visibility. Feature 051 replaced that design: an individual (B2C)
20
+ * customer is backed by a single-member **personal** organisation, so the
21
+ * guard always has a concrete tenant and there is no "no-organization"
22
+ * scoping path. The column stayed nullable for another year and three write
23
+ * paths went on producing NULLs — self-registration's two-flush window,
24
+ * federated sign-in (which provisioned no organisation at all), and an admin
25
+ * "un-assign" button. All three are closed, and the column is `NOT NULL`,
26
+ * which is the only refusal available: MikroORM applies its tenant filter to
27
+ * `SELECT` / `UPDATE` / `DELETE` and not to `INSERT`, so nothing in the guard
28
+ * can stop a tenant-less row being written.
29
+ *
30
+ * The account and its organisation are created in **one transaction** — see
31
+ * `CustomerAccountLifecycleWriteService.createStandalone` and the
32
+ * federated-sign-in `autoCreate` in this module's `backend.ts`.
33
+ */
34
+ organizationId: string;
35
+ email: string;
36
+ passwordHash: string;
37
+ /**
38
+ * Issue #222 — when a password somebody supplied was last set on this
39
+ * account. `null` means "no such password is on record", which covers both
40
+ * the account federated sign-in auto-created (its hash is a random pair of
41
+ * UUIDs nobody was told) and every row written before this column existed.
42
+ *
43
+ * It exists because `passwordHash` is NOT NULL for *every* account and
44
+ * therefore answers a different question from the one that matters on a
45
+ * security surface: whether the holder has a credential they can actually
46
+ * use. `SocialLinkService` is the first reader — it refuses to sever an
47
+ * account's last federated identity while this is null.
48
+ *
49
+ * Stamped by every write of a caller-supplied password (registration, the
50
+ * member write port, a redeemed reset token, a self-service change) and
51
+ * deliberately **not** by the generated hash. The anonymization sweep clears
52
+ * it back to null with the hash it scrubs.
53
+ */
54
+ passwordSetAt?: Date | null;
55
+ firstName: string;
56
+ lastName: string;
57
+ role: 'organization_admin' | 'regular_user';
58
+ emailVerifiedAt?: Date | null;
59
+ /**
60
+ * Never written, and since 2026-08-28 never **read** either.
61
+ *
62
+ * Its only non-null writer was the superseded customer 2FA path, deleted with
63
+ * `two_factor_secret`, so the published `twoFactorEnabled` derived from it
64
+ * was a provably constant `false` — including on `GET /api/v1/me/customer`,
65
+ * which told a buyer their own account was unprotected while it was not.
66
+ * `CustomerAccountRecord.twoFactorEnabled` is now the live `mfa` enrolment,
67
+ * read through `mfaEnrolmentStatePort`, and nothing in the tree reads this
68
+ * column.
69
+ *
70
+ * It is dead schema kept for one more step: dropping it is a migration on
71
+ * this module's table and is deliberately not folded into the repair of the
72
+ * five derivation sites. Do not read it — the honest answer is `mfa`'s.
73
+ */
74
+ twoFactorConfirmedAt?: Date | null;
75
+ lastLoginAt?: Date | null;
76
+ createdAt: Date;
77
+ updatedAt: Date;
78
+ customFieldValues: Record<string, unknown>;
79
+ deletedAt?: Date | null;
80
+ /**
81
+ * Direct customer→customer-group membership (FR-022). Independent of the
82
+ * Organization's group: the effective pricing group is
83
+ * `customerGroupId ?? organization.customerGroupId`. FK to `customer_groups`
84
+ * (owned by price_lists), ON DELETE SET NULL.
85
+ */
86
+ customerGroupId?: string | null;
87
+ /**
88
+ * Feature 056 (T032) — customer-side roll-up capability. When `true`, this
89
+ * login sees/acts across its organization's **subtree** (the tenant scope is
90
+ * widened from single-org to `allowed-set(subtreeIds(theirOrg))` server-side,
91
+ * Principle XI). Default `false` ⇒ node-only (flat behavior). Set only by a
92
+ * platform admin on the org member surface; mirrors the admin/sales-rep
93
+ * `organizations:rollup` capability for a buyer account.
94
+ */
95
+ subtreeRollupEnabled: boolean;
96
+ /** Non-null ⇒ the account is blocked and login is denied (FR-012/FR-016). */
97
+ blockedAt?: Date | null;
98
+ blockReason?: string | null;
99
+ /** Who applied the block — drives unblock authority (FR-013/FR-015). */
100
+ blockSource?: 'staff' | 'org_owner' | null;
101
+ blockedByAdminUserId?: string | null;
102
+ blockedByCustomerAccountId?: string | null;
103
+ /** Admin who soft-deleted the account (paired with `deletedAt`, FR-039). */
104
+ deletionRequestedByAdminUserId?: string | null;
105
+ /**
106
+ * Non-null ⇒ PII has been irreversibly scrubbed by the anonymization sweep
107
+ * after the retention window; restore is no longer possible (FR-040).
108
+ */
109
+ anonymizedAt?: Date | null;
110
+ }
111
+ //# sourceMappingURL=customer-account.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer-account.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/customer-account.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAgC,MAAM,iBAAiB,CAAC;AAI7F;;;;;;GAMG;AACH,qBAEa,eAAe;IAC1B,CAAC,aAAa,CAAC,CAAC,EACZ,mBAAmB,GACnB,IAAI,GACJ,WAAW,GACX,WAAW,GACX,MAAM,GACN,iBAAiB,GACjB,eAAe,GACf,sBAAsB,GACtB,aAAa,GACb,WAAW,GACX,iBAAiB,GACjB,sBAAsB,GACtB,WAAW,GACX,aAAa,GACb,aAAa,GACb,sBAAsB,GACtB,4BAA4B,GAC5B,gCAAgC,GAChC,cAAc,CAAC;IAGnB,EAAE,EAAE,MAAM,CAAgB;IAE1B;;;;;;;;;;;;;;;;;;;;;OAqBG;IAGH,cAAc,EAAG,MAAM,CAAC;IAIxB,KAAK,EAAG,MAAM,CAAC;IAGf,YAAY,EAAG,MAAM,CAAC;IAEtB;;;;;;;;;;;;;;;;OAgBG;IAEH,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAG5B,SAAS,EAAG,MAAM,CAAC;IAGnB,QAAQ,EAAG,MAAM,CAAC;IAIlB,IAAI,EAAE,oBAAoB,GAAG,cAAc,CAAkB;IAG7D,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAE9B;;;;;;;;;;;;;;OAcG;IAEH,oBAAoB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGnC,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAG1B,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;IAI7B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAGhD,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAIxB;;;;;OAKG;IAGH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;;;;OAOG;IAEH,oBAAoB,EAAE,OAAO,CAAS;IAEtC,6EAA6E;IAG7E,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B,wEAAwE;IAExE,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAAC;IAG3C,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrC,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3C,4EAA4E;IAE5E,8BAA8B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/C;;;OAGG;IAEH,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC5B"}