@endora-commerce/mod-customers 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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -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 +68 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/CustomerDetail.d.ts +9 -0
  8. package/dist/admin/pages/CustomerDetail.d.ts.map +1 -0
  9. package/dist/admin/pages/CustomerDetail.js +72 -0
  10. package/dist/admin/pages/CustomerDetail.js.map +1 -0
  11. package/dist/admin/pages/CustomersList.d.ts +9 -0
  12. package/dist/admin/pages/CustomersList.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomersList.js +115 -0
  14. package/dist/admin/pages/CustomersList.js.map +1 -0
  15. package/dist/admin/pages/OnlineCustomers.d.ts +9 -0
  16. package/dist/admin/pages/OnlineCustomers.d.ts.map +1 -0
  17. package/dist/admin/pages/OnlineCustomers.js +77 -0
  18. package/dist/admin/pages/OnlineCustomers.js.map +1 -0
  19. package/dist/admin/panels/HistoryPanels.d.ts +11 -0
  20. package/dist/admin/panels/HistoryPanels.d.ts.map +1 -0
  21. package/dist/admin/panels/HistoryPanels.js +64 -0
  22. package/dist/admin/panels/HistoryPanels.js.map +1 -0
  23. package/dist/admin/panels/ManagementPanels.d.ts +14 -0
  24. package/dist/admin/panels/ManagementPanels.d.ts.map +1 -0
  25. package/dist/admin/panels/ManagementPanels.js +93 -0
  26. package/dist/admin/panels/ManagementPanels.js.map +1 -0
  27. package/dist/backend/entities/customer-address.entity.d.ts +25 -0
  28. package/dist/backend/entities/customer-address.entity.d.ts.map +1 -0
  29. package/dist/backend/entities/customer-address.entity.js +95 -0
  30. package/dist/backend/entities/customer-address.entity.js.map +1 -0
  31. package/dist/backend/index.d.ts +91 -0
  32. package/dist/backend/index.d.ts.map +1 -0
  33. package/dist/backend/index.js +201 -0
  34. package/dist/backend/index.js.map +1 -0
  35. package/dist/backend/plugin.d.ts +168 -0
  36. package/dist/backend/plugin.d.ts.map +1 -0
  37. package/dist/backend/plugin.js +83 -0
  38. package/dist/backend/plugin.js.map +1 -0
  39. package/dist/backend/routes.admin.d.ts +70 -0
  40. package/dist/backend/routes.admin.d.ts.map +1 -0
  41. package/dist/backend/routes.admin.js +313 -0
  42. package/dist/backend/routes.admin.js.map +1 -0
  43. package/dist/backend/routes.register.d.ts +12 -0
  44. package/dist/backend/routes.register.d.ts.map +1 -0
  45. package/dist/backend/routes.register.js +40 -0
  46. package/dist/backend/routes.register.js.map +1 -0
  47. package/dist/backend/routes.self.d.ts +46 -0
  48. package/dist/backend/routes.self.d.ts.map +1 -0
  49. package/dist/backend/routes.self.js +145 -0
  50. package/dist/backend/routes.self.js.map +1 -0
  51. package/dist/backend/serializers.d.ts +13 -0
  52. package/dist/backend/serializers.d.ts.map +1 -0
  53. package/dist/backend/serializers.js +39 -0
  54. package/dist/backend/serializers.js.map +1 -0
  55. package/dist/backend/services/customer-address-read-port.d.ts +29 -0
  56. package/dist/backend/services/customer-address-read-port.d.ts.map +1 -0
  57. package/dist/backend/services/customer-address-read-port.js +53 -0
  58. package/dist/backend/services/customer-address-read-port.js.map +1 -0
  59. package/dist/backend/services/customer-address-service.d.ts +94 -0
  60. package/dist/backend/services/customer-address-service.d.ts.map +1 -0
  61. package/dist/backend/services/customer-address-service.js +176 -0
  62. package/dist/backend/services/customer-address-service.js.map +1 -0
  63. package/dist/backend/services/customer-admin-query-service.d.ts +57 -0
  64. package/dist/backend/services/customer-admin-query-service.d.ts.map +1 -0
  65. package/dist/backend/services/customer-admin-query-service.js +97 -0
  66. package/dist/backend/services/customer-admin-query-service.js.map +1 -0
  67. package/dist/backend/services/customer-authority-service.d.ts +46 -0
  68. package/dist/backend/services/customer-authority-service.d.ts.map +1 -0
  69. package/dist/backend/services/customer-authority-service.js +42 -0
  70. package/dist/backend/services/customer-authority-service.js.map +1 -0
  71. package/dist/backend/services/customer-defaults-service.d.ts +48 -0
  72. package/dist/backend/services/customer-defaults-service.d.ts.map +1 -0
  73. package/dist/backend/services/customer-defaults-service.js +71 -0
  74. package/dist/backend/services/customer-defaults-service.js.map +1 -0
  75. package/dist/backend/services/customer-deletion-service.d.ts +39 -0
  76. package/dist/backend/services/customer-deletion-service.d.ts.map +1 -0
  77. package/dist/backend/services/customer-deletion-service.js +94 -0
  78. package/dist/backend/services/customer-deletion-service.js.map +1 -0
  79. package/dist/backend/services/customer-moderation-service.d.ts +58 -0
  80. package/dist/backend/services/customer-moderation-service.d.ts.map +1 -0
  81. package/dist/backend/services/customer-moderation-service.js +70 -0
  82. package/dist/backend/services/customer-moderation-service.js.map +1 -0
  83. package/dist/backend/services/customer-org-assignment-service.d.ts +52 -0
  84. package/dist/backend/services/customer-org-assignment-service.d.ts.map +1 -0
  85. package/dist/backend/services/customer-org-assignment-service.js +106 -0
  86. package/dist/backend/services/customer-org-assignment-service.js.map +1 -0
  87. package/dist/backend/services/customer-presence-service.d.ts +44 -0
  88. package/dist/backend/services/customer-presence-service.d.ts.map +1 -0
  89. package/dist/backend/services/customer-presence-service.js +47 -0
  90. package/dist/backend/services/customer-presence-service.js.map +1 -0
  91. package/dist/backend/services/customer-registration-service.d.ts +56 -0
  92. package/dist/backend/services/customer-registration-service.d.ts.map +1 -0
  93. package/dist/backend/services/customer-registration-service.js +43 -0
  94. package/dist/backend/services/customer-registration-service.js.map +1 -0
  95. package/dist/backend/workers/anonymization-sweep-worker.d.ts +15 -0
  96. package/dist/backend/workers/anonymization-sweep-worker.d.ts.map +1 -0
  97. package/dist/backend/workers/anonymization-sweep-worker.js +20 -0
  98. package/dist/backend/workers/anonymization-sweep-worker.js.map +1 -0
  99. package/dist/manifest.d.ts +181 -0
  100. package/dist/manifest.d.ts.map +1 -0
  101. package/dist/manifest.js +192 -0
  102. package/dist/manifest.js.map +1 -0
  103. package/dist/migrations/20260611T140404_customers_customer_addresses_init.d.ts +14 -0
  104. package/dist/migrations/20260611T140404_customers_customer_addresses_init.d.ts.map +1 -0
  105. package/dist/migrations/20260611T140404_customers_customer_addresses_init.js +40 -0
  106. package/dist/migrations/20260611T140404_customers_customer_addresses_init.js.map +1 -0
  107. package/dist/migrations/index.d.ts +27 -0
  108. package/dist/migrations/index.d.ts.map +1 -0
  109. package/dist/migrations/index.js +29 -0
  110. package/dist/migrations/index.js.map +1 -0
  111. package/docs/customers.md +108 -0
  112. package/i18n/en.json +94 -0
  113. package/i18n/pl.json +94 -0
  114. package/package.json +101 -0
  115. 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,60 @@
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-customers
4
+
5
+ Customer lifecycle business logic: registration, self-service, blocking, impersonation, groups, deletion, and presence.
6
+
7
+ ## What this is
8
+
9
+ An **Endora Commerce module package**. Its module id is `customers` — 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-customers` | the module manifest — its id, version, dependencies, settings and activation |
18
+ | `@endora-commerce/mod-customers/admin` | the Admin UI contribution: the screens, navigation and palette actions it adds |
19
+ | `@endora-commerce/mod-customers/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
20
+ | `@endora-commerce/mod-customers/migrations` | the module’s own schema migrations, in the order the platform runs them |
21
+ | `@endora-commerce/mod-customers/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/platform`
32
+
33
+ **Third-party**
34
+
35
+ - `@fastify/cookie` ^11
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
+ - `react-router-dom` ^7 — *optional*
43
+ - `zod` ^4
44
+
45
+ ## What the tarball carries
46
+
47
+ - `dist/` — the compiled JavaScript and its type declarations
48
+ - `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
49
+ - `docs/` — the package’s own pages in the Endora Commerce documentation site
50
+ - `tailwind.css` — the Tailwind source declaration
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pnpm add @endora-commerce/mod-customers
56
+ ```
57
+
58
+ ## Licence
59
+
60
+ MIT — the text is in `LICENSE`, beside this file.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `customers`' admin surface — three routes and two sidebar entries (feature
3
+ * 091, Phase 4 batch 14).
4
+ *
5
+ * **This module is a zone *host*, and that is what puts it in batch 14 rather
6
+ * than in 13.** `CustomerDetail.tsx` renders `customer.detail.after`, the
7
+ * member P7b published for it, and `quick_order` contributes its default
8
+ * preferences panel there. The publication is what freed this directory to
9
+ * move: `check:admin-registrations` counts routes and nav entries and a zone is
10
+ * neither, so a host may move whether or not its contributor has — the mirror
11
+ * of the property batch 13's row rests on.
12
+ *
13
+ * **What it drains: nothing, and the zero is measured rather than assumed.**
14
+ * Before anything moved on this branch, this module owned no
15
+ * `backend/scripts/ledgers/cross-module-imports/` shard, was named as a target
16
+ * by none, had no key in `admin-surface.ts` (which is empty) and none in
17
+ * `foreign-module-ids.ts`. `check:module-boundary` reads
18
+ * `cross-module reaches=7 ledger-size=7 shards=4` on both sides of the move,
19
+ * and that agreement is the measurement rather than a silence.
20
+ *
21
+ * **This entry exports data and nothing else** (R2); every component is a
22
+ * dynamic-import factory (R6).
23
+ */
24
+ import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
25
+ export declare const contributions: AdminContributions;
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAiBnF,eAAO,MAAM,aAAa,EAAE,kBAqD3B,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The one code that opens every screen below.
3
+ *
4
+ * `packages/modules/customers/src/backend/routes.admin.ts` gates the roster,
5
+ * the presence view and one customer's detail with `customers:read`; the block,
6
+ * unblock, impersonate, delete and restore controls each enforce
7
+ * `customers:manage` on the API and gate themselves on the screen through
8
+ * `useAuth().hasPermission`. The read code is what opens the screen, which is
9
+ * issue #232's rule and the code both hand-written sidebar rows carried.
10
+ */
11
+ const READ_PERMISSION = 'customers:read';
12
+ /** The module's landing route: the customer roster. */
13
+ const ROUTE_PATH = '/customers';
14
+ export const contributions = {
15
+ routes: [
16
+ {
17
+ path: ROUTE_PATH,
18
+ component: () => import('./pages/CustomersList.js'),
19
+ requiredPermission: READ_PERMISSION,
20
+ index: true,
21
+ },
22
+ {
23
+ // Declared before `/customers/:id`, and `App.tsx` declared it before it
24
+ // too. `<Routes>` ranks by specificity rather than by declaration order,
25
+ // so the static segment wins whatever the order; the ordering is kept
26
+ // anyway, so a reader does not have to know that to see why the detail
27
+ // route is safe here.
28
+ path: `${ROUTE_PATH}/online`,
29
+ component: () => import('./pages/OnlineCustomers.js'),
30
+ requiredPermission: READ_PERMISSION,
31
+ },
32
+ {
33
+ path: `${ROUTE_PATH}/:id`,
34
+ component: () => import('./pages/CustomerDetail.js'),
35
+ requiredPermission: READ_PERMISSION,
36
+ },
37
+ ],
38
+ nav: [
39
+ {
40
+ to: ROUTE_PATH,
41
+ // Module-relative (R8), resolved in this module's own namespace out of
42
+ // `packages/modules/customers/i18n/`. It was `appShell.nav.customers` in
43
+ // the shared `_i18n` bundle, one of the four shared files a module author
44
+ // had to edit.
45
+ labelKey: 'nav.customers.label',
46
+ // The glyph `AppShell.tsx` rendered by hand, and already on
47
+ // `KnownIconNameSchema` — this module's own palette actions name it.
48
+ icon: 'Users',
49
+ section: 'customers',
50
+ // First and second in *Customers*, which is where the hand-written table
51
+ // put them. `customer_accounts` and `credit_limits` declare 400 and
52
+ // `comparisons` 600, so 100 and 200 keep the order an operator already
53
+ // had; `/organizations` at 300 sits between them and those, exactly as it
54
+ // did while all three were the host's.
55
+ weight: 100,
56
+ requiredPermission: READ_PERMISSION,
57
+ },
58
+ {
59
+ to: `${ROUTE_PATH}/online`,
60
+ labelKey: 'nav.customersOnline.label',
61
+ icon: 'Users',
62
+ section: 'customers',
63
+ weight: 200,
64
+ requiredPermission: READ_PERMISSION,
65
+ },
66
+ ],
67
+ };
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAyBA;;;;;;;;;GASG;AACH,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEzC,uDAAuD;AACvD,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAAC;YACnD,kBAAkB,EAAE,eAAe;YACnC,KAAK,EAAE,IAAI;SACZ;QACD;YACE,wEAAwE;YACxE,yEAAyE;YACzE,sEAAsE;YACtE,uEAAuE;YACvE,sBAAsB;YACtB,IAAI,EAAE,GAAG,UAAU,SAAS;YAC5B,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,4BAA4B,CAAC;YACrD,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,IAAI,EAAE,GAAG,UAAU,MAAM;YACzB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC;YACpD,kBAAkB,EAAE,eAAe;SACpC;KACF;IACD,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,yEAAyE;YACzE,0EAA0E;YAC1E,eAAe;YACf,QAAQ,EAAE,qBAAqB;YAC/B,4DAA4D;YAC5D,qEAAqE;YACrE,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,WAAW;YACpB,yEAAyE;YACzE,oEAAoE;YACpE,uEAAuE;YACvE,0EAA0E;YAC1E,uCAAuC;YACvC,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,EAAE,EAAE,GAAG,UAAU,SAAS;YAC1B,QAAQ,EAAE,2BAA2B;YACrC,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,eAAe;SACpC;KACF;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function CustomerDetail(): ReactNode;
3
+ /**
4
+ * The registry loads a route component through a dynamic-import factory and
5
+ * reads its default export (feature 091, FR-013). The named export stays: it is
6
+ * the spelling this module's own code and its tests use.
7
+ */
8
+ export default CustomerDetail;
9
+ //# sourceMappingURL=CustomerDetail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomerDetail.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/CustomerDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAgCzE,wBAAgB,cAAc,IAAI,SAAS,CA0M1C;AAED;;;;GAIG;AACH,eAAe,cAAc,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { useParams } from 'react-router-dom';
4
+ import { ApiError, apiClient, formatDateTime } from '@endora-commerce/admin-kit/lib';
5
+ import { Alert, AlertDescription, Button, Badge, Card, CardContent, CardHeader, CardTitle, PageHeader } from '@endora-commerce/admin-kit/ui';
6
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
7
+ import { OrdersPanel, QuoteRequestsPanel, CartsPanel } from '../panels/HistoryPanels.js';
8
+ import { OrganizationAssignmentPanel, CustomerGroupPanel, AddressesPanel, } from '../panels/ManagementPanels.js';
9
+ import { CustomFieldValuesPanel } from '@endora-commerce/admin-kit/components';
10
+ import { AdminZone } from '@endora-commerce/admin-kit/zones';
11
+ export function CustomerDetail() {
12
+ const { id = '' } = useParams();
13
+ const t = useTranslation('customers');
14
+ const [c, setC] = useState(null);
15
+ const [error, setError] = useState(null);
16
+ const [busy, setBusy] = useState(false);
17
+ const refresh = useCallback(async () => {
18
+ setError(null);
19
+ try {
20
+ const res = await apiClient.get(`/api/v1/admin/customers/${id}`);
21
+ setC(res.data);
22
+ }
23
+ catch (err) {
24
+ setError(err instanceof ApiError ? err.envelope.error.message : t('detail.error'));
25
+ }
26
+ }, [id, t]);
27
+ useEffect(() => {
28
+ void refresh();
29
+ }, [refresh]);
30
+ const act = useCallback(async (run) => {
31
+ setBusy(true);
32
+ setError(null);
33
+ try {
34
+ await run();
35
+ await refresh();
36
+ }
37
+ catch (err) {
38
+ setError(err instanceof ApiError ? err.envelope.error.message : t('detail.actionError'));
39
+ }
40
+ finally {
41
+ setBusy(false);
42
+ }
43
+ }, [refresh, t]);
44
+ if (!c) {
45
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: t('detail.title') }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: t('detail.loading') }))] }));
46
+ }
47
+ const info = [
48
+ [t('detail.field.email'), c.email],
49
+ [t('detail.field.created'), formatDateTime(c.createdAt)],
50
+ [t('detail.field.group'), c.customerGroupName ?? t('list.none')],
51
+ [t('detail.field.organization'), c.organizationName ?? t('list.none')],
52
+ [
53
+ t('detail.field.status'),
54
+ c.deleted ? (_jsx(Badge, { variant: "outline", children: t('status.deleted') })) : c.blocked ? (_jsx(Badge, { variant: "destructive", children: t('status.blocked') })) : (_jsx(Badge, { children: t('status.active') })),
55
+ ],
56
+ [t('detail.field.lastLogin'), c.lastLoginAt ? formatDateTime(c.lastLoginAt) : t('list.none')],
57
+ ];
58
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: `${c.firstName} ${c.lastName}`, description: c.email }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, _jsxs(Card, { className: "mb-4", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('detail.info.title') }) }), _jsx(CardContent, { children: _jsx("dl", { className: "grid gap-3 sm:grid-cols-2", children: info.map(([label, value]) => (_jsxs("div", { className: "space-y-1", children: [_jsx("dt", { className: "text-xs text-muted-foreground", children: label }), _jsx("dd", { className: "text-sm", children: value })] }, label))) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('detail.actions.title') }) }), _jsxs(CardContent, { className: "flex flex-wrap gap-3", children: [c.blocked ? (_jsx(Button, { variant: "outline", disabled: busy, onClick: () => void act(() => apiClient.post(`/api/v1/admin/customers/${id}/unblock`, {})), children: t('detail.action.unblock') })) : (_jsx(Button, { variant: "destructive", disabled: busy || c.deleted, onClick: () => void act(() => apiClient.post(`/api/v1/admin/customers/${id}/block`, {})), children: t('detail.action.block') })), _jsx(Button, { variant: "outline", disabled: busy || c.deleted, onClick: () => void act(() => apiClient.post(`/api/v1/admin/customers/${id}/password-reset`, {})), children: t('detail.action.passwordReset') }), _jsx(Button, { variant: "outline", disabled: busy || c.deleted, onClick: () => void act(() => apiClient.post(`/api/v1/admin/customers/${id}/mfa/reset`, {})), children: t('detail.action.resetMfa') }), _jsx(Button, { variant: "outline", disabled: busy || c.deleted, onClick: () => void act(async () => {
59
+ const res = await apiClient.post(`/api/v1/admin/customers/${id}/impersonate`, {});
60
+ return res;
61
+ }), children: t('detail.action.impersonate') }), c.deleted ? (_jsx(Button, { variant: "outline", disabled: busy, onClick: () => void act(() => apiClient.post(`/api/v1/admin/customers/${id}/restore`, {})), children: t('detail.action.restore') })) : (_jsx(Button, { variant: "destructive", disabled: busy, onClick: () => void act(() => apiClient.delete(`/api/v1/admin/customers/${id}`)), children: t('detail.action.delete') }))] })] }), _jsx(OrganizationAssignmentPanel, { customerId: id, organizationId: c.organizationId, onChanged: () => void refresh() }), _jsx(CustomerGroupPanel, { customerId: id, customerGroupId: c.customerGroupId, onChanged: () => void refresh() }), _jsx(AddressesPanel, { customerId: id, onChanged: () => void refresh() }), _jsx(AdminZone, { name: "customer.detail.after", props: { customerId: id } }), _jsx(CustomFieldValuesPanel, { entityType: "customer", values: c.customFieldValues ?? {}, save: async (values) => {
62
+ await apiClient.patch(`/api/v1/admin/customers/${id}/custom-fields`, values);
63
+ await refresh();
64
+ } }), _jsx(OrdersPanel, { customerId: id }), _jsx(QuoteRequestsPanel, { customerId: id }), _jsx(CartsPanel, { customerId: id })] }));
65
+ }
66
+ /**
67
+ * The registry loads a route component through a dynamic-import factory and
68
+ * reads its default export (feature 091, FR-013). The named export stays: it is
69
+ * the spelling this module's own code and its tests use.
70
+ */
71
+ export default CustomerDetail;
72
+ //# sourceMappingURL=CustomerDetail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomerDetail.js","sourceRoot":"","sources":["../../../src/admin/pages/CustomerDetail.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function CustomersList(): ReactNode;
3
+ /**
4
+ * The registry loads a route component through a dynamic-import factory and
5
+ * reads its default export (feature 091, FR-013). The named export stays: it is
6
+ * the spelling this module's own code and its tests use.
7
+ */
8
+ export default CustomersList;
9
+ //# sourceMappingURL=CustomersList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomersList.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/CustomersList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAyBzE,wBAAgB,aAAa,IAAI,SAAS,CAoMzC;AAED;;;;GAIG;AACH,eAAe,aAAa,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { Link } from 'react-router-dom';
4
+ import { ArrowRight } from 'lucide-react';
5
+ import { ApiError, apiClient, formatDateTime, useAuth } from '@endora-commerce/admin-kit/lib';
6
+ import { Alert, AlertDescription, Button, Badge, Card, CardContent, Input, Label, PageHeader, Select } from '@endora-commerce/admin-kit/ui';
7
+ import { ResponsiveTable } from '@endora-commerce/admin-kit/components';
8
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
9
+ const STATUSES = ['active', 'blocked', 'deleted'];
10
+ export function CustomersList() {
11
+ const t = useTranslation('customers');
12
+ const { hasPermission } = useAuth();
13
+ const [rows, setRows] = useState([]);
14
+ const [loading, setLoading] = useState(true);
15
+ const [error, setError] = useState(null);
16
+ const [status, setStatus] = useState('');
17
+ const [q, setQ] = useState('');
18
+ const [organizationId, setOrganizationId] = useState('');
19
+ const [orgs, setOrgs] = useState([]);
20
+ const [resetMsg, setResetMsg] = useState(null);
21
+ const [resetting, setResetting] = useState(false);
22
+ const refresh = useCallback(async () => {
23
+ setLoading(true);
24
+ setError(null);
25
+ try {
26
+ const params = new URLSearchParams();
27
+ if (status)
28
+ params.set('status', status);
29
+ if (q)
30
+ params.set('q', q);
31
+ if (organizationId)
32
+ params.set('organizationId', organizationId);
33
+ const path = '/api/v1/admin/customers' + (params.toString() ? `?${params.toString()}` : '');
34
+ const res = await apiClient.get(path);
35
+ setRows(res.data);
36
+ }
37
+ catch (err) {
38
+ setError(err instanceof ApiError ? err.envelope.error.message : t('list.error'));
39
+ }
40
+ finally {
41
+ setLoading(false);
42
+ }
43
+ }, [status, q, organizationId, t]);
44
+ useEffect(() => {
45
+ void refresh();
46
+ }, [refresh]);
47
+ // Load organizations once to populate the filter (feature 042 US6).
48
+ useEffect(() => {
49
+ void (async () => {
50
+ try {
51
+ const res = await apiClient.get('/api/v1/admin/organizations');
52
+ setOrgs(res.data);
53
+ }
54
+ catch {
55
+ // Filter just won't populate — non-fatal.
56
+ }
57
+ })();
58
+ }, []);
59
+ // Feature 042 US6 — reset 2FA for every account in the selected organization.
60
+ const resetOrgMfa = useCallback(async () => {
61
+ if (!organizationId)
62
+ return;
63
+ setResetting(true);
64
+ setResetMsg(null);
65
+ try {
66
+ const res = await apiClient.post('/api/v1/admin/mfa/reset-bulk', { organizationId });
67
+ setResetMsg(`${t('list.action.resetOrgMfaDone')}: ${res.data.affected}/${res.data.requested}`);
68
+ }
69
+ catch (err) {
70
+ setResetMsg(err instanceof ApiError ? err.envelope.error.message : t('list.error'));
71
+ }
72
+ finally {
73
+ setResetting(false);
74
+ }
75
+ }, [organizationId, t]);
76
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: t('list.title'), description: t('list.description') }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, _jsx(Card, { className: "mb-4", children: _jsxs(CardContent, { className: "grid gap-4 pt-6 md:grid-cols-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "cstatus", children: t('list.field.status') }), _jsxs(Select, { id: "cstatus", value: status, onChange: (e) => setStatus(e.target.value), children: [_jsx("option", { value: "", children: t('list.filter.all') }), STATUSES.map((s) => (_jsx("option", { value: s, children: t(`status.${s}`) }, s)))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "corg", children: t('list.field.organization') }), _jsxs(Select, { id: "corg", value: organizationId, onChange: (e) => setOrganizationId(e.target.value), children: [_jsx("option", { value: "", children: t('list.filter.allOrgs') }), orgs.map((o) => (_jsx("option", { value: o.id, children: o.name }, o.id)))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "cq", children: t('list.field.search') }), _jsx(Input, { id: "cq", value: q, onChange: (e) => setQ(e.target.value) })] })] }) }), organizationId && hasPermission('mfa:reset') ? (_jsx(Card, { className: "mb-4", children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 pt-6", children: [_jsx(Button, { variant: "outline", disabled: resetting, onClick: () => void resetOrgMfa(), children: t('list.action.resetOrgMfa') }), resetMsg ? _jsx("span", { className: "text-sm text-muted-foreground", children: resetMsg }) : null] }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('list.loading') })) : rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('list.empty') })) : (_jsx(ResponsiveTable, { data: rows, keyExtractor: (c) => c.id, columns: [
77
+ {
78
+ id: 'name',
79
+ header: t('list.column.customer'),
80
+ primary: true,
81
+ render: (c) => (_jsxs("span", { className: "font-medium", children: [c.firstName, " ", c.lastName] })),
82
+ meta: (c) => (_jsx("span", { className: "font-mono text-xs text-muted-foreground", children: c.email })),
83
+ },
84
+ {
85
+ id: 'status',
86
+ header: t('list.column.status'),
87
+ render: (c) => c.deleted ? (_jsx(Badge, { variant: "outline", children: t('status.deleted') })) : c.blocked ? (_jsx(Badge, { variant: "destructive", children: t('status.blocked') })) : (_jsx(Badge, { children: t('status.active') })),
88
+ },
89
+ {
90
+ id: 'organization',
91
+ header: t('list.column.organization'),
92
+ hideOnMobile: true,
93
+ render: (c) => c.organizationName ?? t('list.none'),
94
+ },
95
+ {
96
+ id: 'group',
97
+ header: t('list.column.group'),
98
+ hideOnMobile: true,
99
+ render: (c) => c.customerGroupName ?? t('list.none'),
100
+ },
101
+ {
102
+ id: 'created',
103
+ header: t('list.column.created'),
104
+ hideOnMobile: true,
105
+ render: (c) => formatDateTime(c.createdAt),
106
+ },
107
+ ], renderActions: (c) => (_jsx(Button, { asChild: true, variant: "outline", size: "sm", className: "min-h-11", children: _jsxs(Link, { to: `/customers/${c.id}`, children: [t('list.action.open'), _jsx(ArrowRight, {})] }) })) })) }) })] }));
108
+ }
109
+ /**
110
+ * The registry loads a route component through a dynamic-import factory and
111
+ * reads its default export (feature 091, FR-013). The named export stays: it is
112
+ * the spelling this module's own code and its tests use.
113
+ */
114
+ export default CustomersList;
115
+ //# sourceMappingURL=CustomersList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomersList.js","sourceRoot":"","sources":["../../../src/admin/pages/CustomersList.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function OnlineCustomers(): ReactNode;
3
+ /**
4
+ * The registry loads a route component through a dynamic-import factory and
5
+ * reads its default export (feature 091, FR-013). The named export stays: it is
6
+ * the spelling this module's own code and its tests use.
7
+ */
8
+ export default OnlineCustomers;
9
+ //# sourceMappingURL=OnlineCustomers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OnlineCustomers.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/OnlineCustomers.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAkBjF,wBAAgB,eAAe,IAAI,SAAS,CA6I3C;AAED;;;;GAIG;AACH,eAAe,eAAe,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Link } from 'react-router-dom';
4
+ import { ArrowRight, RefreshCw } from 'lucide-react';
5
+ import { ApiError, apiClient, formatDateTime } from '@endora-commerce/admin-kit/lib';
6
+ import { Alert, AlertDescription, Button, Card, CardContent, Checkbox, Input, Label, PageHeader } from '@endora-commerce/admin-kit/ui';
7
+ import { ResponsiveTable } from '@endora-commerce/admin-kit/components';
8
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
9
+ const MIN_INTERVAL_SEC = 2;
10
+ export function OnlineCustomers() {
11
+ const t = useTranslation('customers');
12
+ const [rows, setRows] = useState([]);
13
+ const [loading, setLoading] = useState(true);
14
+ const [error, setError] = useState(null);
15
+ const [lastRefreshed, setLastRefreshed] = useState(null);
16
+ // Auto-refresh: opt-in, with a configurable interval (seconds). The presence
17
+ // list is derived from `Session.lastSeenAt`, so a buyer who logs in after the
18
+ // page loaded only appears on the next fetch — hence an explicit Refresh plus
19
+ // optional polling.
20
+ const [autoRefresh, setAutoRefresh] = useState(false);
21
+ const [intervalSec, setIntervalSec] = useState(15);
22
+ const refresh = useCallback(async () => {
23
+ setLoading(true);
24
+ setError(null);
25
+ try {
26
+ const res = await apiClient.get('/api/v1/admin/customers/online');
27
+ setRows(res.data);
28
+ setLastRefreshed(new Date());
29
+ }
30
+ catch (err) {
31
+ setError(err instanceof ApiError ? err.envelope.error.message : t('online.error'));
32
+ }
33
+ finally {
34
+ setLoading(false);
35
+ }
36
+ }, [t]);
37
+ useEffect(() => {
38
+ void refresh();
39
+ }, [refresh]);
40
+ // Keep the latest `refresh` in a ref so the polling effect does not resubscribe
41
+ // (and reset the timer) on every render.
42
+ const refreshRef = useRef(refresh);
43
+ useEffect(() => {
44
+ refreshRef.current = refresh;
45
+ }, [refresh]);
46
+ useEffect(() => {
47
+ if (!autoRefresh)
48
+ return;
49
+ const ms = Math.max(MIN_INTERVAL_SEC, intervalSec) * 1000;
50
+ const handle = setInterval(() => {
51
+ void refreshRef.current();
52
+ }, ms);
53
+ return () => clearInterval(handle);
54
+ }, [autoRefresh, intervalSec]);
55
+ const controls = (_jsxs("div", { className: "flex flex-wrap items-end gap-4", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { checked: autoRefresh, onChange: (e) => setAutoRefresh(e.target.checked) }), t('online.autoRefresh')] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "online-interval", className: "text-xs text-muted-foreground", children: t('online.intervalLabel') }), _jsx(Input, { id: "online-interval", type: "number", min: MIN_INTERVAL_SEC, step: 1, value: intervalSec, disabled: !autoRefresh, onChange: (e) => setIntervalSec(Math.max(MIN_INTERVAL_SEC, Number(e.target.value) || MIN_INTERVAL_SEC)), className: "w-24" })] }), _jsxs(Button, { variant: "outline", onClick: () => void refresh(), disabled: loading, children: [_jsx(RefreshCw, { className: loading ? 'animate-spin' : undefined }), t('online.refresh')] })] }));
56
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: t('online.title'), description: t('online.description'), actions: controls }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, lastRefreshed ? (_jsx("p", { className: "mb-2 text-xs text-muted-foreground", children: t('online.refreshedAt', { time: lastRefreshed.toLocaleTimeString() }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: loading && rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('online.loading') })) : rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('online.empty') })) : (_jsx(ResponsiveTable, { data: rows, keyExtractor: (c) => c.id, columns: [
57
+ {
58
+ id: 'name',
59
+ header: t('list.column.customer'),
60
+ primary: true,
61
+ render: (c) => (_jsxs("span", { className: "font-medium", children: [c.firstName, " ", c.lastName] })),
62
+ meta: (c) => _jsx("span", { className: "font-mono text-xs text-muted-foreground", children: c.email }),
63
+ },
64
+ {
65
+ id: 'lastSeen',
66
+ header: t('online.column.lastSeen'),
67
+ render: (c) => formatDateTime(c.lastSeenAt),
68
+ },
69
+ ], renderActions: (c) => (_jsx(Button, { asChild: true, variant: "outline", size: "sm", className: "min-h-11", children: _jsxs(Link, { to: `/customers/${c.id}`, children: [t('list.action.open'), _jsx(ArrowRight, {})] }) })) })) }) })] }));
70
+ }
71
+ /**
72
+ * The registry loads a route component through a dynamic-import factory and
73
+ * reads its default export (feature 091, FR-013). The named export stays: it is
74
+ * the spelling this module's own code and its tests use.
75
+ */
76
+ export default OnlineCustomers;
77
+ //# sourceMappingURL=OnlineCustomers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OnlineCustomers.js","sourceRoot":"","sources":["../../../src/admin/pages/OnlineCustomers.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AACvI,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAUjE,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IACtC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAmB,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAc,IAAI,CAAC,CAAC;IAEtE,6EAA6E;IAC7E,8EAA8E;IAC9E,8EAA8E;IAC9E,oBAAoB;IACpB,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACpD,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAA6B,gCAAgC,CAAC,CAAC;YAC9F,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,gBAAgB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;QACrF,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAER,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,OAAO,EAAE,CAAC;IACjB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,gFAAgF;IAChF,yCAAyC;IACzC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC/B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,WAAW;YAAE,OAAO;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;QAC1D,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;YAC9B,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;QAC5B,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IAE/B,MAAM,QAAQ,GAAG,CACf,eAAK,SAAS,EAAC,gCAAgC,aAC7C,iBAAO,SAAS,EAAC,iCAAiC,aAChD,KAAC,QAAQ,IACP,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GACjD,EACD,CAAC,CAAC,oBAAoB,CAAC,IAClB,EACR,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,KAAK,IAAC,OAAO,EAAC,iBAAiB,EAAC,SAAS,EAAC,+BAA+B,YACvE,CAAC,CAAC,sBAAsB,CAAC,GACpB,EACR,KAAC,KAAK,IACJ,EAAE,EAAC,iBAAiB,EACpB,IAAI,EAAC,QAAQ,EACb,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,CAAC,WAAW,EACtB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,CAAC,EACvG,SAAS,EAAC,MAAM,GAChB,IACE,EACN,MAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,aACxE,KAAC,SAAS,IAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,GAAI,EAC7D,CAAC,CAAC,gBAAgB,CAAC,IACb,IACL,CACP,CAAC;IAEF,OAAO,CACL,8BACE,KAAC,UAAU,IACT,KAAK,EAAE,CAAC,CAAC,cAAc,CAAC,EACxB,WAAW,EAAE,CAAC,CAAC,oBAAoB,CAAC,EACpC,OAAO,EAAE,QAAQ,GACjB,EAED,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,KAAK,IAAC,OAAO,EAAC,aAAa,EAAC,SAAS,EAAC,MAAM,YAC3C,KAAC,gBAAgB,cAAE,KAAK,GAAoB,GACtC,CACT,CAAC,CAAC,CAAC,IAAI,EAEP,aAAa,CAAC,CAAC,CAAC,CACf,YAAG,SAAS,EAAC,oCAAoC,YAC9C,CAAC,CAAC,oBAAoB,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,kBAAkB,EAAE,EAAE,CAAC,GACpE,CACL,CAAC,CAAC,CAAC,IAAI,EAER,KAAC,IAAI,cACH,KAAC,WAAW,IAAC,SAAS,EAAC,MAAM,YAC1B,OAAO,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAC9B,YAAG,SAAS,EAAC,+BAA+B,YAAE,CAAC,CAAC,gBAAgB,CAAC,GAAK,CACvE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACtB,YAAG,SAAS,EAAC,+BAA+B,YAAE,CAAC,CAAC,cAAc,CAAC,GAAK,CACrE,CAAC,CAAC,CAAC,CACF,KAAC,eAAe,IACd,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EACzB,OAAO,EAAE;4BACP;gCACE,EAAE,EAAE,MAAM;gCACV,MAAM,EAAE,CAAC,CAAC,sBAAsB,CAAC;gCACjC,OAAO,EAAE,IAAI;gCACb,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CACb,gBAAM,SAAS,EAAC,aAAa,aAC1B,CAAC,CAAC,SAAS,OAAG,CAAC,CAAC,QAAQ,IACpB,CACR;gCACD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAM,SAAS,EAAC,yCAAyC,YAAE,CAAC,CAAC,KAAK,GAAQ;6BACxF;4BACD;gCACE,EAAE,EAAE,UAAU;gCACd,MAAM,EAAE,CAAC,CAAC,wBAAwB,CAAC;gCACnC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC;6BAC5C;yBACF,EACD,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CACpB,KAAC,MAAM,IAAC,OAAO,QAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,UAAU,YAC9D,MAAC,IAAI,IAAC,EAAE,EAAE,cAAc,CAAC,CAAC,EAAE,EAAE,aAC3B,CAAC,CAAC,kBAAkB,CAAC,EACtB,KAAC,UAAU,KAAG,IACT,GACA,CACV,GACD,CACH,GACW,GACT,IACN,CACJ,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,eAAe,eAAe,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function OrdersPanel({ customerId }: {
3
+ customerId: string;
4
+ }): ReactNode;
5
+ export declare function QuoteRequestsPanel({ customerId }: {
6
+ customerId: string;
7
+ }): ReactNode;
8
+ export declare function CartsPanel({ customerId }: {
9
+ customerId: string;
10
+ }): ReactNode;
11
+ //# sourceMappingURL=HistoryPanels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HistoryPanels.d.ts","sourceRoot":"","sources":["../../../src/admin/panels/HistoryPanels.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA2CzE,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAwC7E;AAUD,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoCpF;AAkBD,wBAAgB,UAAU,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CA2D5E"}