@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.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/admin/api/customer-groups-client.d.ts +27 -0
- package/dist/admin/api/customer-groups-client.d.ts.map +1 -0
- package/dist/admin/api/customer-groups-client.js +17 -0
- package/dist/admin/api/customer-groups-client.js.map +1 -0
- package/dist/admin/index.d.ts +33 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +38 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomerGroupsPage.d.ts +18 -0
- package/dist/admin/pages/CustomerGroupsPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomerGroupsPage.js +108 -0
- package/dist/admin/pages/CustomerGroupsPage.js.map +1 -0
- package/dist/backend/entities/customer-account.entity.d.ts +111 -0
- package/dist/backend/entities/customer-account.entity.d.ts.map +1 -0
- package/dist/backend/entities/customer-account.entity.js +230 -0
- package/dist/backend/entities/customer-account.entity.js.map +1 -0
- package/dist/backend/entities/customer-group.entity.d.ts +29 -0
- package/dist/backend/entities/customer-group.entity.d.ts.map +1 -0
- package/dist/backend/entities/customer-group.entity.js +70 -0
- package/dist/backend/entities/customer-group.entity.js.map +1 -0
- package/dist/backend/entities/password-reset-token.entity.d.ts +16 -0
- package/dist/backend/entities/password-reset-token.entity.d.ts.map +1 -0
- package/dist/backend/entities/password-reset-token.entity.js +59 -0
- package/dist/backend/entities/password-reset-token.entity.js.map +1 -0
- package/dist/backend/index.d.ts +144 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +407 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +26 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +35 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/services/audit-references.d.ts +22 -0
- package/dist/backend/services/audit-references.d.ts.map +1 -0
- package/dist/backend/services/audit-references.js +46 -0
- package/dist/backend/services/audit-references.js.map +1 -0
- package/dist/backend/services/customer-account-lifecycle-ports.d.ts +168 -0
- package/dist/backend/services/customer-account-lifecycle-ports.d.ts.map +1 -0
- package/dist/backend/services/customer-account-lifecycle-ports.js +420 -0
- package/dist/backend/services/customer-account-lifecycle-ports.js.map +1 -0
- package/dist/backend/services/customer-account-ports.d.ts +143 -0
- package/dist/backend/services/customer-account-ports.d.ts.map +1 -0
- package/dist/backend/services/customer-account-ports.js +422 -0
- package/dist/backend/services/customer-account-ports.js.map +1 -0
- package/dist/backend/services/customer-auth-service.d.ts +62 -0
- package/dist/backend/services/customer-auth-service.d.ts.map +1 -0
- package/dist/backend/services/customer-auth-service.js +105 -0
- package/dist/backend/services/customer-auth-service.js.map +1 -0
- package/dist/backend/services/customer-group-read-port.d.ts +24 -0
- package/dist/backend/services/customer-group-read-port.d.ts.map +1 -0
- package/dist/backend/services/customer-group-read-port.js +44 -0
- package/dist/backend/services/customer-group-read-port.js.map +1 -0
- package/dist/backend/services/customer-group-service.d.ts +21 -0
- package/dist/backend/services/customer-group-service.d.ts.map +1 -0
- package/dist/backend/services/customer-group-service.js +97 -0
- package/dist/backend/services/customer-group-service.js.map +1 -0
- package/dist/backend/services/customer-rollup-scope.d.ts +30 -0
- package/dist/backend/services/customer-rollup-scope.d.ts.map +1 -0
- package/dist/backend/services/customer-rollup-scope.js +42 -0
- package/dist/backend/services/customer-rollup-scope.js.map +1 -0
- package/dist/backend/services/password-reset-service.d.ts +15 -0
- package/dist/backend/services/password-reset-service.d.ts.map +1 -0
- package/dist/backend/services/password-reset-service.js +90 -0
- package/dist/backend/services/password-reset-service.js.map +1 -0
- package/dist/backend/services/role-service.d.ts +22 -0
- package/dist/backend/services/role-service.d.ts.map +1 -0
- package/dist/backend/services/role-service.js +83 -0
- package/dist/backend/services/role-service.js.map +1 -0
- package/dist/backend/services/two-factor-enrolments.d.ts +18 -0
- package/dist/backend/services/two-factor-enrolments.d.ts.map +1 -0
- package/dist/backend/services/two-factor-enrolments.js +2 -0
- package/dist/backend/services/two-factor-enrolments.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +212 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts +6 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts.map +1 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js +25 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js.map +1 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts +34 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts.map +1 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js +43 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js.map +1 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts +54 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts.map +1 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js +97 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js.map +1 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts +12 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts.map +1 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js +16 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js.map +1 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts +14 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts.map +1 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js +18 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js.map +1 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts +22 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts.map +1 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js +26 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js.map +1 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts +46 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts.map +1 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js +97 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js.map +1 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts +31 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts.map +1 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js +35 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js.map +1 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts +58 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts.map +1 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.js +133 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.js.map +1 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts +27 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts.map +1 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js +44 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js.map +1 -0
- package/dist/migrations/index.d.ts +36 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +47 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/customer_accounts.md +38 -0
- package/i18n/en.json +30 -0
- package/i18n/pl.json +30 -0
- package/package.json +94 -0
- 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"}
|