@open-mercato/core 0.10.1-develop.7833.1.d6cbf0e3ab → 0.10.1-develop.7841.1.af87ac614d
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/cart/api/current/lines/bulk/route.js +1 -1
- package/dist/modules/cart/api/current/lines/bulk/route.js.map +2 -2
- package/dist/modules/cart/api/current/lines/route.js +1 -1
- package/dist/modules/cart/api/current/lines/route.js.map +2 -2
- package/dist/modules/cart/commands/lines.js +5 -10
- package/dist/modules/cart/commands/lines.js.map +2 -2
- package/dist/modules/cart/lib/currentCartRoute.js +8 -0
- package/dist/modules/cart/lib/currentCartRoute.js.map +3 -3
- package/dist/modules/cart/lib/lineMutationLock.js +52 -0
- package/dist/modules/cart/lib/lineMutationLock.js.map +7 -0
- package/dist/modules/customer_accounts/api/admin/roles.js +25 -2
- package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users.js +24 -7
- package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +77 -15
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +12 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
- package/dist/modules/customer_accounts/data/validators.js +2 -1
- package/dist/modules/customer_accounts/data/validators.js.map +2 -2
- package/dist/modules/customer_accounts/lib/adminOrganizationScope.js +46 -0
- package/dist/modules/customer_accounts/lib/adminOrganizationScope.js.map +7 -0
- package/dist/modules/customers/api/customer-dictionary-entries/route.js +86 -0
- package/dist/modules/customers/api/customer-dictionary-entries/route.js.map +7 -0
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +29 -7
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/cli.js +28 -110
- package/dist/modules/customers/cli.js.map +3 -3
- package/dist/modules/customers/components/DictionarySettings.js +1 -1
- package/dist/modules/customers/components/DictionarySettings.js.map +1 -1
- package/dist/modules/customers/components/detail/ManageTagsDialog.js +1 -1
- package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/TaskDialog.js +14 -1
- package/dist/modules/customers/components/detail/TaskDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/TaskForm.js +1 -16
- package/dist/modules/customers/components/detail/TaskForm.js.map +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js +38 -10
- package/dist/modules/customers/components/detail/TasksSection.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/useCustomerDictionary.js +12 -5
- package/dist/modules/customers/components/detail/hooks/useCustomerDictionary.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/useInteractions.js +3 -13
- package/dist/modules/customers/components/detail/hooks/useInteractions.js.map +2 -2
- package/dist/modules/customers/lib/dictionaryDefaults.js +134 -0
- package/dist/modules/customers/lib/dictionaryDefaults.js.map +7 -0
- package/dist/modules/customers/lib/dictionaryLabels.js +59 -0
- package/dist/modules/customers/lib/dictionaryLabels.js.map +7 -0
- package/dist/modules/customers/lib/interactionCompatibility.js +1 -0
- package/dist/modules/customers/lib/interactionCompatibility.js.map +2 -2
- package/dist/modules/customers/translations.js +9 -0
- package/dist/modules/customers/translations.js.map +7 -0
- package/dist/modules/eudr/index.js +13 -0
- package/dist/modules/eudr/index.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +3 -3
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +75 -21
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/staff/api/timesheets/access-requests/route.js +11 -5
- package/dist/modules/staff/api/timesheets/access-requests/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +19 -12
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +21 -12
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +14 -9
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js +31 -10
- package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/projectAccessScope.js +64 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectAccessScope.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectAccessScopeHeaders.js +13 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectAccessScopeHeaders.js.map +7 -0
- package/dist/modules/sync_excel/index.js +11 -1
- package/dist/modules/sync_excel/index.js.map +2 -2
- package/dist/modules/translations/di.js +2 -0
- package/dist/modules/translations/di.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/cart/api/current/lines/bulk/route.ts +1 -1
- package/src/modules/cart/api/current/lines/route.ts +1 -1
- package/src/modules/cart/commands/lines.ts +5 -10
- package/src/modules/cart/i18n/de.json +1 -0
- package/src/modules/cart/i18n/en.json +1 -0
- package/src/modules/cart/i18n/es.json +1 -0
- package/src/modules/cart/i18n/ko.json +1 -0
- package/src/modules/cart/i18n/pl.json +1 -0
- package/src/modules/cart/lib/currentCartRoute.ts +8 -0
- package/src/modules/cart/lib/lineMutationLock.ts +59 -0
- package/src/modules/customer_accounts/api/admin/roles.ts +26 -2
- package/src/modules/customer_accounts/api/admin/users.ts +26 -7
- package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +96 -13
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +9 -1
- package/src/modules/customer_accounts/data/validators.ts +1 -0
- package/src/modules/customer_accounts/i18n/de.json +10 -0
- package/src/modules/customer_accounts/i18n/en.json +10 -0
- package/src/modules/customer_accounts/i18n/es.json +10 -0
- package/src/modules/customer_accounts/i18n/ko.json +10 -0
- package/src/modules/customer_accounts/i18n/pl.json +10 -0
- package/src/modules/customer_accounts/lib/adminOrganizationScope.ts +71 -0
- package/src/modules/customers/api/customer-dictionary-entries/route.ts +82 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +29 -6
- package/src/modules/customers/cli.ts +23 -124
- package/src/modules/customers/components/DictionarySettings.tsx +1 -1
- package/src/modules/customers/components/detail/ManageTagsDialog.tsx +1 -1
- package/src/modules/customers/components/detail/TaskDialog.tsx +15 -1
- package/src/modules/customers/components/detail/TaskForm.tsx +1 -17
- package/src/modules/customers/components/detail/TasksSection.tsx +44 -14
- package/src/modules/customers/components/detail/hooks/useCustomerDictionary.ts +14 -8
- package/src/modules/customers/components/detail/hooks/useInteractions.ts +3 -4
- package/src/modules/customers/i18n/de.json +81 -0
- package/src/modules/customers/i18n/en.json +81 -0
- package/src/modules/customers/i18n/es.json +81 -0
- package/src/modules/customers/i18n/ko.json +81 -0
- package/src/modules/customers/i18n/pl.json +81 -0
- package/src/modules/customers/lib/dictionaryDefaults.ts +137 -0
- package/src/modules/customers/lib/dictionaryLabels.ts +73 -0
- package/src/modules/customers/lib/interactionCompatibility.ts +1 -0
- package/src/modules/customers/translations.ts +5 -0
- package/src/modules/eudr/index.ts +13 -0
- package/src/modules/sales/commands/documents.ts +3 -3
- package/src/modules/sales/components/documents/ItemsSection.tsx +82 -23
- package/src/modules/sales/components/documents/lineItemUtils.ts +2 -2
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/ko.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
- package/src/modules/staff/api/timesheets/access-requests/route.ts +13 -14
- package/src/modules/staff/api/timesheets/time-projects/route.ts +22 -13
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +25 -14
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +14 -13
- package/src/modules/staff/lib/time-tracking-ui/NoProjectAccess.tsx +30 -8
- package/src/modules/staff/lib/time-tracking-ui/projectAccessScope.ts +102 -0
- package/src/modules/staff/lib/time-tracking-ui/projectAccessScopeHeaders.ts +15 -0
- package/src/modules/sync_excel/index.ts +10 -0
- package/src/modules/translations/di.ts +2 -0
package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js
CHANGED
|
@@ -6,6 +6,7 @@ import Link from "next/link";
|
|
|
6
6
|
import { useRouter } from "next/navigation";
|
|
7
7
|
import { Globe, Settings } from "lucide-react";
|
|
8
8
|
import { DataTable } from "@open-mercato/ui/backend/DataTable";
|
|
9
|
+
import { ErrorMessage, LoadingMessage } from "@open-mercato/ui/backend/detail";
|
|
9
10
|
import { RowActions } from "@open-mercato/ui/backend/RowActions";
|
|
10
11
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
11
12
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
@@ -19,6 +20,7 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
|
19
20
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
20
21
|
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
|
|
21
22
|
import { ListEmptyState } from "@open-mercato/ui/backend/filters/ListEmptyState";
|
|
23
|
+
import { OrganizationSelect } from "@open-mercato/core/modules/directory/components/OrganizationSelect";
|
|
22
24
|
import { buildPortalRootUrl, buildPortalUrlPattern } from "../../../lib/portalUrl.js";
|
|
23
25
|
import { useDemoPortalAccounts } from "../useDemoPortalAccounts.js";
|
|
24
26
|
function formatDate(value, fallback) {
|
|
@@ -27,35 +29,64 @@ function formatDate(value, fallback) {
|
|
|
27
29
|
if (Number.isNaN(date.getTime())) return fallback;
|
|
28
30
|
return date.toLocaleDateString();
|
|
29
31
|
}
|
|
30
|
-
async function fetchRoleFilterOptions() {
|
|
32
|
+
async function fetchRoleFilterOptions(organizationId) {
|
|
31
33
|
try {
|
|
34
|
+
const params = new URLSearchParams({ pageSize: "100" });
|
|
35
|
+
if (organizationId) params.set("organizationId", organizationId);
|
|
32
36
|
const call = await apiCall(
|
|
33
|
-
|
|
37
|
+
`/api/customer_accounts/admin/roles?${params.toString()}`
|
|
34
38
|
);
|
|
35
|
-
if (!call.ok) return
|
|
36
|
-
const items =
|
|
39
|
+
if (!call.ok || !Array.isArray(call.result?.items)) return null;
|
|
40
|
+
const items = call.result.items;
|
|
37
41
|
return items.filter((item) => typeof item?.id === "string" && typeof item?.name === "string").map((item) => ({ value: item.id, label: item.name, id: item.id }));
|
|
38
42
|
} catch {
|
|
39
|
-
return
|
|
43
|
+
return null;
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
function CreateUserDialog({
|
|
43
47
|
open,
|
|
44
48
|
onOpenChange,
|
|
45
|
-
roleOptions,
|
|
46
49
|
onCreated,
|
|
47
|
-
onRunMutation
|
|
50
|
+
onRunMutation,
|
|
51
|
+
activeOrganizationId
|
|
48
52
|
}) {
|
|
49
53
|
const t = useT();
|
|
50
54
|
const [email, setEmail] = React.useState("");
|
|
51
55
|
const [displayName, setDisplayName] = React.useState("");
|
|
52
56
|
const [password, setPassword] = React.useState("");
|
|
57
|
+
const [organizationId, setOrganizationId] = React.useState(null);
|
|
58
|
+
const [organizationRoles, setOrganizationRoles] = React.useState(null);
|
|
59
|
+
const [roleReloadToken, setRoleReloadToken] = React.useState(0);
|
|
53
60
|
const [selectedRoleIds, setSelectedRoleIds] = React.useState([]);
|
|
54
61
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
55
62
|
const resetForm = React.useCallback(() => {
|
|
56
63
|
setEmail("");
|
|
57
64
|
setDisplayName("");
|
|
58
65
|
setPassword("");
|
|
66
|
+
setOrganizationId(null);
|
|
67
|
+
setSelectedRoleIds([]);
|
|
68
|
+
}, []);
|
|
69
|
+
const requireOrganization = !activeOrganizationId;
|
|
70
|
+
const roleOrganizationId = organizationId ?? activeOrganizationId;
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
if (!open || !roleOrganizationId) {
|
|
73
|
+
setOrganizationRoles(null);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
let cancelled = false;
|
|
77
|
+
setOrganizationRoles({ organizationId: roleOrganizationId, status: "loading" });
|
|
78
|
+
fetchRoleFilterOptions(roleOrganizationId).then((opts) => {
|
|
79
|
+
if (cancelled) return;
|
|
80
|
+
setOrganizationRoles(opts === null ? { organizationId: roleOrganizationId, status: "error" } : { organizationId: roleOrganizationId, status: "loaded", options: opts });
|
|
81
|
+
});
|
|
82
|
+
return () => {
|
|
83
|
+
cancelled = true;
|
|
84
|
+
};
|
|
85
|
+
}, [open, roleOrganizationId, roleReloadToken]);
|
|
86
|
+
const currentOrganizationRoles = organizationRoles?.organizationId === roleOrganizationId ? organizationRoles : null;
|
|
87
|
+
const availableRoleOptions = currentOrganizationRoles?.status === "loaded" ? currentOrganizationRoles.options : [];
|
|
88
|
+
const handleOrganizationChange = React.useCallback((next) => {
|
|
89
|
+
setOrganizationId(next ?? null);
|
|
59
90
|
setSelectedRoleIds([]);
|
|
60
91
|
}, []);
|
|
61
92
|
const handleSubmit = React.useCallback(async (event) => {
|
|
@@ -64,6 +95,10 @@ function CreateUserDialog({
|
|
|
64
95
|
flash(t("customer_accounts.admin.createUser.error.required", "Email, name, and password are required"), "error");
|
|
65
96
|
return;
|
|
66
97
|
}
|
|
98
|
+
if (requireOrganization && !organizationId) {
|
|
99
|
+
flash(t("customer_accounts.admin.createUser.error.organizationRequired", "Select an organization for the new user"), "error");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
67
102
|
setIsSubmitting(true);
|
|
68
103
|
try {
|
|
69
104
|
await onRunMutation(async () => {
|
|
@@ -76,7 +111,8 @@ function CreateUserDialog({
|
|
|
76
111
|
email: email.trim(),
|
|
77
112
|
displayName: displayName.trim(),
|
|
78
113
|
password,
|
|
79
|
-
roleIds: selectedRoleIds.length > 0 ? selectedRoleIds : void 0
|
|
114
|
+
roleIds: selectedRoleIds.length > 0 ? selectedRoleIds : void 0,
|
|
115
|
+
organizationId: organizationId ?? void 0
|
|
80
116
|
})
|
|
81
117
|
}
|
|
82
118
|
);
|
|
@@ -95,7 +131,7 @@ function CreateUserDialog({
|
|
|
95
131
|
} finally {
|
|
96
132
|
setIsSubmitting(false);
|
|
97
133
|
}
|
|
98
|
-
}, [displayName, email, onCreated, onOpenChange, onRunMutation, password, resetForm, selectedRoleIds, t]);
|
|
134
|
+
}, [displayName, email, onCreated, onOpenChange, onRunMutation, organizationId, password, requireOrganization, resetForm, selectedRoleIds, t]);
|
|
99
135
|
const handleKeyDown = React.useCallback((event) => {
|
|
100
136
|
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
101
137
|
event.preventDefault();
|
|
@@ -153,9 +189,31 @@ function CreateUserDialog({
|
|
|
153
189
|
}
|
|
154
190
|
)
|
|
155
191
|
] }),
|
|
156
|
-
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
193
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "create-organization", children: t("customer_accounts.admin.createUser.fields.organization", "Organization") }),
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
OrganizationSelect,
|
|
196
|
+
{
|
|
197
|
+
id: "create-organization",
|
|
198
|
+
value: organizationId,
|
|
199
|
+
onChange: handleOrganizationChange,
|
|
200
|
+
required: requireOrganization,
|
|
201
|
+
includeEmptyOption: true,
|
|
202
|
+
emptyOptionLabel: requireOrganization ? t("customer_accounts.admin.createUser.fields.organizationPlaceholder", "Select an organization") : t("customer_accounts.admin.createUser.fields.organizationCurrent", "Current organization"),
|
|
203
|
+
className: "w-full h-9 rounded border px-2 text-sm"
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
] }),
|
|
207
|
+
roleOrganizationId && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
157
208
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("customer_accounts.admin.createUser.fields.roles", "Roles") }),
|
|
158
|
-
|
|
209
|
+
currentOrganizationRoles?.status === "error" ? /* @__PURE__ */ jsx(
|
|
210
|
+
ErrorMessage,
|
|
211
|
+
{
|
|
212
|
+
label: t("customer_accounts.admin.createUser.error.loadRoles", "Could not load roles for this organization"),
|
|
213
|
+
action: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setRoleReloadToken((token) => token + 1), children: t("customer_accounts.admin.createUser.actions.retryRoles", "Retry") })
|
|
214
|
+
}
|
|
215
|
+
) : currentOrganizationRoles?.status !== "loaded" ? /* @__PURE__ */ jsx(LoadingMessage, { label: t("customer_accounts.admin.createUser.loadingRoles", "Loading roles...") }) : availableRoleOptions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("customer_accounts.admin.createUser.noRoles", "No roles are available for this organization") }) : null,
|
|
216
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: availableRoleOptions.map((role) => {
|
|
159
217
|
const isSelected = selectedRoleIds.includes(role.id);
|
|
160
218
|
return /* @__PURE__ */ jsx(
|
|
161
219
|
Button,
|
|
@@ -184,7 +242,11 @@ function CreateUserDialog({
|
|
|
184
242
|
] })
|
|
185
243
|
] }) });
|
|
186
244
|
}
|
|
187
|
-
function PortalUsersPageClient({
|
|
245
|
+
function PortalUsersPageClient({
|
|
246
|
+
portalOrigin,
|
|
247
|
+
portalOrgSlug = null,
|
|
248
|
+
activeOrganizationId = null
|
|
249
|
+
}) {
|
|
188
250
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
189
251
|
const t = useT();
|
|
190
252
|
const router = useRouter();
|
|
@@ -217,7 +279,7 @@ function PortalUsersPageClient({ portalOrigin, portalOrgSlug = null }) {
|
|
|
217
279
|
React.useEffect(() => {
|
|
218
280
|
let cancelled = false;
|
|
219
281
|
fetchRoleFilterOptions().then((opts) => {
|
|
220
|
-
if (!cancelled) setRoleOptions(opts);
|
|
282
|
+
if (!cancelled) setRoleOptions(opts ?? []);
|
|
221
283
|
});
|
|
222
284
|
return () => {
|
|
223
285
|
cancelled = true;
|
|
@@ -526,9 +588,9 @@ function PortalUsersPageClient({ portalOrigin, portalOrgSlug = null }) {
|
|
|
526
588
|
{
|
|
527
589
|
open: createDialogOpen,
|
|
528
590
|
onOpenChange: setCreateDialogOpen,
|
|
529
|
-
roleOptions,
|
|
530
591
|
onCreated: () => setReloadToken((token) => token + 1),
|
|
531
|
-
onRunMutation: runMutationWithContext
|
|
592
|
+
onRunMutation: runMutationWithContext,
|
|
593
|
+
activeOrganizationId
|
|
532
594
|
}
|
|
533
595
|
),
|
|
534
596
|
ConfirmDialogElement
|
package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Globe, Settings } from 'lucide-react'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { buildPortalRootUrl, buildPortalUrlPattern } from '../../../lib/portalUrl'\nimport { useDemoPortalAccounts } from '../useDemoPortalAccounts'\n\ntype UserRow = {\n id: string\n displayName: string\n email: string\n emailVerified: boolean\n isActive: boolean\n lastLoginAt: string | null\n roles: Array<{ id: string; name: string; slug: string }>\n createdAt: string\n updatedAt?: string | null\n personEntityId: string | null\n customerEntityId: string | null\n organizationId: string | null\n organizationName: string | null\n}\n\ntype UsersResponse = {\n items?: UserRow[]\n total?: number\n totalPages?: number\n totalIsCapped?: boolean\n}\n\nfunction formatDate(value: string | null | undefined, fallback: string): string {\n if (!value) return fallback\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallback\n return date.toLocaleDateString()\n}\n\nasync function fetchRoleFilterOptions(): Promise<Array<{ value: string; label: string; id: string }>> {\n try {\n const call = await apiCall<{ items?: Array<{ id: string; name: string }> }>(\n '/api/customer_accounts/admin/roles?pageSize=100',\n )\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items : []\n return items\n .filter((item) => typeof item?.id === 'string' && typeof item?.name === 'string')\n .map((item) => ({ value: item.id, label: item.name, id: item.id }))\n } catch {\n return []\n }\n}\n\nfunction CreateUserDialog({\n open,\n onOpenChange,\n roleOptions,\n onCreated,\n onRunMutation,\n}: {\n open: boolean\n onOpenChange: (next: boolean) => void\n roleOptions: Array<{ id: string; label: string }>\n onCreated: () => void\n onRunMutation: <T>(operation: () => Promise<T>) => Promise<T>\n}) {\n const t = useT()\n const [email, setEmail] = React.useState('')\n const [displayName, setDisplayName] = React.useState('')\n const [password, setPassword] = React.useState('')\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const resetForm = React.useCallback(() => {\n setEmail('')\n setDisplayName('')\n setPassword('')\n setSelectedRoleIds([])\n }, [])\n\n const handleSubmit = React.useCallback(async (event: React.FormEvent) => {\n event.preventDefault()\n if (!email.trim() || !displayName.trim() || !password.trim()) {\n flash(t('customer_accounts.admin.createUser.error.required', 'Email, name, and password are required'), 'error')\n return\n }\n setIsSubmitting(true)\n try {\n await onRunMutation(async () => {\n const call = await apiCall<{ ok: boolean; error?: string }>(\n '/api/customer_accounts/admin/users',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: email.trim(),\n displayName: displayName.trim(),\n password,\n roleIds: selectedRoleIds.length > 0 ? selectedRoleIds : undefined,\n }),\n },\n )\n if (!call.ok) {\n flash(call.result?.error || t('customer_accounts.admin.createUser.error.save', 'Failed to create user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.createUser.flash.created', 'User created'), 'success')\n resetForm()\n onOpenChange(false)\n onCreated()\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.createUser.error.save', 'Failed to create user')\n flash(message, 'error')\n } finally {\n setIsSubmitting(false)\n }\n }, [displayName, email, onCreated, onOpenChange, onRunMutation, password, resetForm, selectedRoleIds, t])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n const form = (event.target as HTMLElement).closest('form')\n if (form) form.requestSubmit()\n }\n }, [])\n\n return (\n <Dialog open={open} onOpenChange={(next) => { if (!next) resetForm(); onOpenChange(next) }}>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t('customer_accounts.admin.createUser.title', 'Create Customer User')}</DialogTitle>\n </DialogHeader>\n <form onSubmit={(event) => { void handleSubmit(event) }} onKeyDown={handleKeyDown} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-email\">\n {t('customer_accounts.admin.createUser.fields.email', 'Email')}\n </label>\n <EmailInput\n id=\"create-email\"\n required\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.emailPlaceholder', 'user@example.com')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-name\">\n {t('customer_accounts.admin.createUser.fields.displayName', 'Display Name')}\n </label>\n <Input\n id=\"create-name\"\n type=\"text\"\n required\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.displayNamePlaceholder', 'John Doe')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-password\">\n {t('customer_accounts.admin.createUser.fields.password', 'Password')}\n </label>\n <PasswordInput\n id=\"create-password\"\n required\n minLength={8}\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.passwordPlaceholder', 'Min. 8 characters')}\n autoComplete=\"new-password\"\n />\n </div>\n {roleOptions.length > 0 && (\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium\">{t('customer_accounts.admin.createUser.fields.roles', 'Roles')}</p>\n <div className=\"flex flex-wrap gap-2\">\n {roleOptions.map((role) => {\n const isSelected = selectedRoleIds.includes(role.id)\n return (\n <Button\n key={role.id}\n type=\"button\"\n size=\"2xs\"\n variant={isSelected ? 'secondary' : 'outline'}\n aria-pressed={isSelected}\n onClick={() => setSelectedRoleIds((prev) =>\n prev.includes(role.id) ? prev.filter((rid) => rid !== role.id) : [...prev, role.id],\n )}\n className=\"rounded-full\"\n >\n {role.label}\n </Button>\n )\n })}\n </div>\n </div>\n )}\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { resetForm(); onOpenChange(false) }}>\n {t('customer_accounts.admin.createUser.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={isSubmitting}>\n {isSubmitting\n ? t('customer_accounts.admin.createUser.actions.creating', 'Creating...')\n : t('customer_accounts.admin.createUser.actions.create', 'Create User')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type PortalUsersPageClientProps = {\n portalOrigin: string\n portalOrgSlug?: string | null\n}\n\nexport function PortalUsersPageClient({ portalOrigin, portalOrgSlug = null }: PortalUsersPageClientProps) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<UserRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [roleOptions, setRoleOptions] = React.useState<Array<{ value: string; label: string; id: string }>>([])\n const [createDialogOpen, setCreateDialogOpen] = React.useState(false)\n const { accounts: demoAccounts } = useDemoPortalAccounts()\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n entityType: string\n entityId?: string\n }>({\n contextId: 'customer_accounts:users-list',\n })\n\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { entityType: 'customer_accounts:user' },\n })\n },\n [runMutation],\n )\n\n React.useEffect(() => {\n let cancelled = false\n fetchRoleFilterOptions().then((opts) => {\n if (!cancelled) setRoleOptions(opts)\n })\n return () => { cancelled = true }\n }, [])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('customer_accounts.admin.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'active', label: t('customer_accounts.admin.filters.active', 'Active') },\n { value: 'inactive', label: t('customer_accounts.admin.filters.inactive', 'Inactive') },\n ],\n },\n {\n id: 'roleId',\n label: t('customer_accounts.admin.filters.role', 'Role'),\n type: 'select',\n options: roleOptions,\n },\n ], [roleOptions, t])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) params.set('status', status)\n const roleId = filterValues.roleId\n if (typeof roleId === 'string' && roleId.trim()) params.set('roleId', roleId)\n return params.toString()\n }, [filterValues, page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: UsersResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<UsersResponse>(\n `/api/customer_accounts/admin/users?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n setTotalIsCapped(payload?.totalIsCapped === true)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleToggleActive = React.useCallback(async (user: UserRow) => {\n const nextActive = !user.isActive\n const actionLabel = nextActive\n ? t('customer_accounts.admin.actions.activate', 'Activate')\n : t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.toggleActive', '{{action}} user \"{{name}}\"?', {\n action: actionLabel,\n name: user.displayName || user.email,\n }),\n variant: nextActive ? 'default' : 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: nextActive }),\n },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.toggleActive', 'Failed to update user status'), 'error')\n return\n }\n flash(\n nextActive\n ? t('customer_accounts.admin.flash.activated', 'User activated')\n : t('customer_accounts.admin.flash.deactivated', 'User deactivated'),\n 'success',\n )\n setReloadToken((token) => token + 1)\n }, { userId: user.id, isActive: nextActive })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.toggleActive', 'Failed to update user status')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleDelete = React.useCallback(async (user: UserRow) => {\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.delete', 'Delete user \"{{name}}\"?', {\n name: user.displayName || user.email,\n }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n { method: 'DELETE' },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.delete', 'Failed to delete user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.flash.deleted', 'User deleted'), 'success')\n setReloadToken((token) => token + 1)\n }, { id: user.id })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.delete', 'Failed to delete user')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const columns = React.useMemo<ColumnDef<UserRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">-</span>\n return [\n {\n accessorKey: 'displayName',\n header: t('customer_accounts.admin.columns.displayName', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/users/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.displayName || row.original.email}\n </Link>\n ),\n },\n {\n accessorKey: 'email',\n header: t('customer_accounts.admin.columns.email', 'Email'),\n },\n {\n accessorKey: 'organizationName',\n header: t('customer_accounts.admin.columns.organization', 'Organization'),\n cell: ({ row }) => {\n const label = row.original.organizationName || row.original.organizationId\n if (!label) return noValue\n return <span className=\"text-sm\">{label}</span>\n },\n },\n {\n accessorKey: 'emailVerified',\n header: t('customer_accounts.admin.columns.emailVerified', 'Verified'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.emailVerified\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}>\n {row.original.emailVerified\n ? t('customer_accounts.admin.verified', 'Yes')\n : t('customer_accounts.admin.unverified', 'No')}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('customer_accounts.admin.columns.status', 'Status'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.isActive\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}>\n {row.original.isActive\n ? t('customer_accounts.admin.active', 'Active')\n : t('customer_accounts.admin.inactive', 'Inactive')}\n </span>\n ),\n },\n {\n accessorKey: 'lastLoginAt',\n header: t('customer_accounts.admin.columns.lastLogin', 'Last Login'),\n cell: ({ row }) => formatDate(row.original.lastLoginAt, '-') || noValue,\n },\n {\n accessorKey: 'roles',\n header: t('customer_accounts.admin.columns.roles', 'Roles'),\n cell: ({ row }) => {\n const roles = row.original.roles\n if (!roles || !roles.length) return noValue\n return <span className=\"text-sm\">{roles.map((r) => r.name).join(', ')}</span>\n },\n },\n {\n accessorKey: 'createdAt',\n header: t('customer_accounts.admin.columns.createdAt', 'Created'),\n cell: ({ row }) => formatDate(row.original.createdAt, '-'),\n },\n ]\n }, [t])\n\n return (\n <>\n <div className=\"rounded-lg border border-status-info-border bg-status-info-bg p-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h3 className=\"text-sm font-medium text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.title', 'Customer Portal')}\n </h3>\n <p className=\"mt-1 text-sm text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.description', 'Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.')}\n </p>\n <p className=\"mt-1.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.url', 'Portal URL: {url}', {\n url: buildPortalUrlPattern(portalOrigin),\n })}\n </p>\n {demoAccounts.length > 0 ? (\n <p className=\"mt-0.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.credentials', 'Seeded demo credentials: {email} / {password}', {\n email: demoAccounts[0].email,\n password: demoAccounts[0].password,\n })}\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 flex-col gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <Link href=\"/backend/customer_accounts/settings\">\n <Settings className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.openConfiguration', 'Open Configuration')}\n </Link>\n </Button>\n {portalOrgSlug ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <a href={buildPortalRootUrl(portalOrigin, portalOrgSlug)} target=\"_blank\" rel=\"noopener noreferrer\">\n <Globe className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.open', 'Open Portal')}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n <DataTable<UserRow>\n stickyActionsColumn\n title={t('customer_accounts.admin.title', 'Users')}\n titleHeadingLevel={1}\n actions={(\n <Button onClick={() => setCreateDialogOpen(true)}>\n {t('customer_accounts.admin.actions.createUser', 'Create User')}\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.searchPlaceholder', 'Search by name or email...')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n perspective={{ tableId: extensionPoints.hosts.usersTable.tableId }}\n emptyState={(\n <ListEmptyState\n entityName={t('customer_accounts.admin.title', 'Users')}\n onCreate={() => setCreateDialogOpen(true)}\n createLabel={t('customer_accounts.admin.actions.createUser', 'Create User')}\n />\n )}\n onRowClick={(row) => router.push(`/backend/customer_accounts/users/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'view',\n label: t('customer_accounts.admin.actions.view', 'View'),\n onSelect: () => { router.push(`/backend/customer_accounts/users/${row.id}`) },\n },\n {\n id: 'toggle-active',\n label: row.isActive\n ? t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n : t('customer_accounts.admin.actions.activate', 'Activate'),\n onSelect: () => { void handleToggleActive(row) },\n },\n {\n id: 'delete',\n label: t('customer_accounts.admin.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n },\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage }}\n isLoading={isLoading}\n />\n <CreateUserDialog\n open={createDialogOpen}\n onOpenChange={setCreateDialogOpen}\n roleOptions={roleOptions}\n onCreated={() => setReloadToken((token) => token + 1)}\n onRunMutation={runMutationWithContext}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Globe, Settings } from 'lucide-react'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { buildPortalRootUrl, buildPortalUrlPattern } from '../../../lib/portalUrl'\nimport { useDemoPortalAccounts } from '../useDemoPortalAccounts'\n\ntype UserRow = {\n id: string\n displayName: string\n email: string\n emailVerified: boolean\n isActive: boolean\n lastLoginAt: string | null\n roles: Array<{ id: string; name: string; slug: string }>\n createdAt: string\n updatedAt?: string | null\n personEntityId: string | null\n customerEntityId: string | null\n organizationId: string | null\n organizationName: string | null\n}\n\ntype UsersResponse = {\n items?: UserRow[]\n total?: number\n totalPages?: number\n totalIsCapped?: boolean\n}\n\nfunction formatDate(value: string | null | undefined, fallback: string): string {\n if (!value) return fallback\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallback\n return date.toLocaleDateString()\n}\n\ntype RoleOption = { value: string; label: string; id: string }\n\ntype OrganizationRolesState =\n | { organizationId: string; status: 'loading' | 'error' }\n | { organizationId: string; status: 'loaded'; options: RoleOption[] }\n\nasync function fetchRoleFilterOptions(organizationId?: string | null): Promise<RoleOption[] | null> {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (organizationId) params.set('organizationId', organizationId)\n const call = await apiCall<{ items?: Array<{ id: string; name: string }> }>(\n `/api/customer_accounts/admin/roles?${params.toString()}`,\n )\n if (!call.ok || !Array.isArray(call.result?.items)) return null\n const items = call.result.items\n return items\n .filter((item) => typeof item?.id === 'string' && typeof item?.name === 'string')\n .map((item) => ({ value: item.id, label: item.name, id: item.id }))\n } catch {\n return null\n }\n}\n\nfunction CreateUserDialog({\n open,\n onOpenChange,\n onCreated,\n onRunMutation,\n activeOrganizationId,\n}: {\n open: boolean\n onOpenChange: (next: boolean) => void\n onCreated: () => void\n onRunMutation: <T>(operation: () => Promise<T>) => Promise<T>\n activeOrganizationId: string | null\n}) {\n const t = useT()\n const [email, setEmail] = React.useState('')\n const [displayName, setDisplayName] = React.useState('')\n const [password, setPassword] = React.useState('')\n const [organizationId, setOrganizationId] = React.useState<string | null>(null)\n const [organizationRoles, setOrganizationRoles] = React.useState<OrganizationRolesState | null>(null)\n const [roleReloadToken, setRoleReloadToken] = React.useState(0)\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const resetForm = React.useCallback(() => {\n setEmail('')\n setDisplayName('')\n setPassword('')\n setOrganizationId(null)\n setSelectedRoleIds([])\n }, [])\n\n const requireOrganization = !activeOrganizationId\n const roleOrganizationId = organizationId ?? activeOrganizationId\n\n React.useEffect(() => {\n if (!open || !roleOrganizationId) {\n setOrganizationRoles(null)\n return\n }\n let cancelled = false\n setOrganizationRoles({ organizationId: roleOrganizationId, status: 'loading' })\n fetchRoleFilterOptions(roleOrganizationId).then((opts) => {\n if (cancelled) return\n setOrganizationRoles(opts === null\n ? { organizationId: roleOrganizationId, status: 'error' }\n : { organizationId: roleOrganizationId, status: 'loaded', options: opts })\n })\n return () => { cancelled = true }\n }, [open, roleOrganizationId, roleReloadToken])\n\n const currentOrganizationRoles = organizationRoles?.organizationId === roleOrganizationId ? organizationRoles : null\n const availableRoleOptions = currentOrganizationRoles?.status === 'loaded' ? currentOrganizationRoles.options : []\n\n const handleOrganizationChange = React.useCallback((next: string | null) => {\n setOrganizationId(next ?? null)\n setSelectedRoleIds([])\n }, [])\n\n const handleSubmit = React.useCallback(async (event: React.FormEvent) => {\n event.preventDefault()\n if (!email.trim() || !displayName.trim() || !password.trim()) {\n flash(t('customer_accounts.admin.createUser.error.required', 'Email, name, and password are required'), 'error')\n return\n }\n if (requireOrganization && !organizationId) {\n flash(t('customer_accounts.admin.createUser.error.organizationRequired', 'Select an organization for the new user'), 'error')\n return\n }\n setIsSubmitting(true)\n try {\n await onRunMutation(async () => {\n const call = await apiCall<{ ok: boolean; error?: string }>(\n '/api/customer_accounts/admin/users',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: email.trim(),\n displayName: displayName.trim(),\n password,\n roleIds: selectedRoleIds.length > 0 ? selectedRoleIds : undefined,\n organizationId: organizationId ?? undefined,\n }),\n },\n )\n if (!call.ok) {\n flash(call.result?.error || t('customer_accounts.admin.createUser.error.save', 'Failed to create user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.createUser.flash.created', 'User created'), 'success')\n resetForm()\n onOpenChange(false)\n onCreated()\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.createUser.error.save', 'Failed to create user')\n flash(message, 'error')\n } finally {\n setIsSubmitting(false)\n }\n }, [displayName, email, onCreated, onOpenChange, onRunMutation, organizationId, password, requireOrganization, resetForm, selectedRoleIds, t])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n const form = (event.target as HTMLElement).closest('form')\n if (form) form.requestSubmit()\n }\n }, [])\n\n return (\n <Dialog open={open} onOpenChange={(next) => { if (!next) resetForm(); onOpenChange(next) }}>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t('customer_accounts.admin.createUser.title', 'Create Customer User')}</DialogTitle>\n </DialogHeader>\n <form onSubmit={(event) => { void handleSubmit(event) }} onKeyDown={handleKeyDown} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-email\">\n {t('customer_accounts.admin.createUser.fields.email', 'Email')}\n </label>\n <EmailInput\n id=\"create-email\"\n required\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.emailPlaceholder', 'user@example.com')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-name\">\n {t('customer_accounts.admin.createUser.fields.displayName', 'Display Name')}\n </label>\n <Input\n id=\"create-name\"\n type=\"text\"\n required\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.displayNamePlaceholder', 'John Doe')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-password\">\n {t('customer_accounts.admin.createUser.fields.password', 'Password')}\n </label>\n <PasswordInput\n id=\"create-password\"\n required\n minLength={8}\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.passwordPlaceholder', 'Min. 8 characters')}\n autoComplete=\"new-password\"\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-organization\">\n {t('customer_accounts.admin.createUser.fields.organization', 'Organization')}\n </label>\n <OrganizationSelect\n id=\"create-organization\"\n value={organizationId}\n onChange={handleOrganizationChange}\n required={requireOrganization}\n includeEmptyOption\n emptyOptionLabel={requireOrganization\n ? t('customer_accounts.admin.createUser.fields.organizationPlaceholder', 'Select an organization')\n : t('customer_accounts.admin.createUser.fields.organizationCurrent', 'Current organization')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n </div>\n {roleOrganizationId && (\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium\">{t('customer_accounts.admin.createUser.fields.roles', 'Roles')}</p>\n {currentOrganizationRoles?.status === 'error' ? (\n <ErrorMessage\n label={t('customer_accounts.admin.createUser.error.loadRoles', 'Could not load roles for this organization')}\n action={\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setRoleReloadToken((token) => token + 1)}>\n {t('customer_accounts.admin.createUser.actions.retryRoles', 'Retry')}\n </Button>\n }\n />\n ) : currentOrganizationRoles?.status !== 'loaded' ? (\n <LoadingMessage label={t('customer_accounts.admin.createUser.loadingRoles', 'Loading roles...')} />\n ) : availableRoleOptions.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('customer_accounts.admin.createUser.noRoles', 'No roles are available for this organization')}\n </p>\n ) : null}\n <div className=\"flex flex-wrap gap-2\">\n {availableRoleOptions.map((role) => {\n const isSelected = selectedRoleIds.includes(role.id)\n return (\n <Button\n key={role.id}\n type=\"button\"\n size=\"2xs\"\n variant={isSelected ? 'secondary' : 'outline'}\n aria-pressed={isSelected}\n onClick={() => setSelectedRoleIds((prev) =>\n prev.includes(role.id) ? prev.filter((rid) => rid !== role.id) : [...prev, role.id],\n )}\n className=\"rounded-full\"\n >\n {role.label}\n </Button>\n )\n })}\n </div>\n </div>\n )}\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { resetForm(); onOpenChange(false) }}>\n {t('customer_accounts.admin.createUser.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={isSubmitting}>\n {isSubmitting\n ? t('customer_accounts.admin.createUser.actions.creating', 'Creating...')\n : t('customer_accounts.admin.createUser.actions.create', 'Create User')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type PortalUsersPageClientProps = {\n portalOrigin: string\n portalOrgSlug?: string | null\n activeOrganizationId?: string | null\n}\n\nexport function PortalUsersPageClient({\n portalOrigin,\n portalOrgSlug = null,\n activeOrganizationId = null,\n}: PortalUsersPageClientProps) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<UserRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [roleOptions, setRoleOptions] = React.useState<Array<{ value: string; label: string; id: string }>>([])\n const [createDialogOpen, setCreateDialogOpen] = React.useState(false)\n const { accounts: demoAccounts } = useDemoPortalAccounts()\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n entityType: string\n entityId?: string\n }>({\n contextId: 'customer_accounts:users-list',\n })\n\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { entityType: 'customer_accounts:user' },\n })\n },\n [runMutation],\n )\n\n React.useEffect(() => {\n let cancelled = false\n fetchRoleFilterOptions().then((opts) => {\n if (!cancelled) setRoleOptions(opts ?? [])\n })\n return () => { cancelled = true }\n }, [])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('customer_accounts.admin.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'active', label: t('customer_accounts.admin.filters.active', 'Active') },\n { value: 'inactive', label: t('customer_accounts.admin.filters.inactive', 'Inactive') },\n ],\n },\n {\n id: 'roleId',\n label: t('customer_accounts.admin.filters.role', 'Role'),\n type: 'select',\n options: roleOptions,\n },\n ], [roleOptions, t])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) params.set('status', status)\n const roleId = filterValues.roleId\n if (typeof roleId === 'string' && roleId.trim()) params.set('roleId', roleId)\n return params.toString()\n }, [filterValues, page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: UsersResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<UsersResponse>(\n `/api/customer_accounts/admin/users?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n setTotalIsCapped(payload?.totalIsCapped === true)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleToggleActive = React.useCallback(async (user: UserRow) => {\n const nextActive = !user.isActive\n const actionLabel = nextActive\n ? t('customer_accounts.admin.actions.activate', 'Activate')\n : t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.toggleActive', '{{action}} user \"{{name}}\"?', {\n action: actionLabel,\n name: user.displayName || user.email,\n }),\n variant: nextActive ? 'default' : 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: nextActive }),\n },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.toggleActive', 'Failed to update user status'), 'error')\n return\n }\n flash(\n nextActive\n ? t('customer_accounts.admin.flash.activated', 'User activated')\n : t('customer_accounts.admin.flash.deactivated', 'User deactivated'),\n 'success',\n )\n setReloadToken((token) => token + 1)\n }, { userId: user.id, isActive: nextActive })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.toggleActive', 'Failed to update user status')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleDelete = React.useCallback(async (user: UserRow) => {\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.delete', 'Delete user \"{{name}}\"?', {\n name: user.displayName || user.email,\n }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n { method: 'DELETE' },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.delete', 'Failed to delete user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.flash.deleted', 'User deleted'), 'success')\n setReloadToken((token) => token + 1)\n }, { id: user.id })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.delete', 'Failed to delete user')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const columns = React.useMemo<ColumnDef<UserRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">-</span>\n return [\n {\n accessorKey: 'displayName',\n header: t('customer_accounts.admin.columns.displayName', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/users/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.displayName || row.original.email}\n </Link>\n ),\n },\n {\n accessorKey: 'email',\n header: t('customer_accounts.admin.columns.email', 'Email'),\n },\n {\n accessorKey: 'organizationName',\n header: t('customer_accounts.admin.columns.organization', 'Organization'),\n cell: ({ row }) => {\n const label = row.original.organizationName || row.original.organizationId\n if (!label) return noValue\n return <span className=\"text-sm\">{label}</span>\n },\n },\n {\n accessorKey: 'emailVerified',\n header: t('customer_accounts.admin.columns.emailVerified', 'Verified'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.emailVerified\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}>\n {row.original.emailVerified\n ? t('customer_accounts.admin.verified', 'Yes')\n : t('customer_accounts.admin.unverified', 'No')}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('customer_accounts.admin.columns.status', 'Status'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.isActive\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}>\n {row.original.isActive\n ? t('customer_accounts.admin.active', 'Active')\n : t('customer_accounts.admin.inactive', 'Inactive')}\n </span>\n ),\n },\n {\n accessorKey: 'lastLoginAt',\n header: t('customer_accounts.admin.columns.lastLogin', 'Last Login'),\n cell: ({ row }) => formatDate(row.original.lastLoginAt, '-') || noValue,\n },\n {\n accessorKey: 'roles',\n header: t('customer_accounts.admin.columns.roles', 'Roles'),\n cell: ({ row }) => {\n const roles = row.original.roles\n if (!roles || !roles.length) return noValue\n return <span className=\"text-sm\">{roles.map((r) => r.name).join(', ')}</span>\n },\n },\n {\n accessorKey: 'createdAt',\n header: t('customer_accounts.admin.columns.createdAt', 'Created'),\n cell: ({ row }) => formatDate(row.original.createdAt, '-'),\n },\n ]\n }, [t])\n\n return (\n <>\n <div className=\"rounded-lg border border-status-info-border bg-status-info-bg p-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h3 className=\"text-sm font-medium text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.title', 'Customer Portal')}\n </h3>\n <p className=\"mt-1 text-sm text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.description', 'Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.')}\n </p>\n <p className=\"mt-1.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.url', 'Portal URL: {url}', {\n url: buildPortalUrlPattern(portalOrigin),\n })}\n </p>\n {demoAccounts.length > 0 ? (\n <p className=\"mt-0.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.credentials', 'Seeded demo credentials: {email} / {password}', {\n email: demoAccounts[0].email,\n password: demoAccounts[0].password,\n })}\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 flex-col gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <Link href=\"/backend/customer_accounts/settings\">\n <Settings className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.openConfiguration', 'Open Configuration')}\n </Link>\n </Button>\n {portalOrgSlug ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <a href={buildPortalRootUrl(portalOrigin, portalOrgSlug)} target=\"_blank\" rel=\"noopener noreferrer\">\n <Globe className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.open', 'Open Portal')}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n <DataTable<UserRow>\n stickyActionsColumn\n title={t('customer_accounts.admin.title', 'Users')}\n titleHeadingLevel={1}\n actions={(\n <Button onClick={() => setCreateDialogOpen(true)}>\n {t('customer_accounts.admin.actions.createUser', 'Create User')}\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.searchPlaceholder', 'Search by name or email...')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n perspective={{ tableId: extensionPoints.hosts.usersTable.tableId }}\n emptyState={(\n <ListEmptyState\n entityName={t('customer_accounts.admin.title', 'Users')}\n onCreate={() => setCreateDialogOpen(true)}\n createLabel={t('customer_accounts.admin.actions.createUser', 'Create User')}\n />\n )}\n onRowClick={(row) => router.push(`/backend/customer_accounts/users/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'view',\n label: t('customer_accounts.admin.actions.view', 'View'),\n onSelect: () => { router.push(`/backend/customer_accounts/users/${row.id}`) },\n },\n {\n id: 'toggle-active',\n label: row.isActive\n ? t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n : t('customer_accounts.admin.actions.activate', 'Activate'),\n onSelect: () => { void handleToggleActive(row) },\n },\n {\n id: 'delete',\n label: t('customer_accounts.admin.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n },\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage }}\n isLoading={isLoading}\n />\n <CreateUserDialog\n open={createDialogOpen}\n onOpenChange={setCreateDialogOpen}\n onCreated={() => setReloadToken((token) => token + 1)}\n onRunMutation={runMutationWithContext}\n activeOrganizationId={activeOrganizationId}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { headers } from "next/headers";
|
|
3
|
+
import { getAuthFromCookies } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { resolveActiveOrganizationId } from "@open-mercato/shared/lib/auth/organizationScope";
|
|
3
5
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
4
6
|
import { resolvePortalRequestOrigin } from "../../../lib/portalUrl.js";
|
|
5
7
|
import { resolveCurrentOrgPortalSlug } from "../../../lib/portalOrgSlug.js";
|
|
@@ -7,7 +9,16 @@ import { PortalUsersPageClient } from "./PortalUsersPageClient.js";
|
|
|
7
9
|
async function CustomerAccountsPage() {
|
|
8
10
|
const portalOrigin = resolvePortalRequestOrigin(await headers());
|
|
9
11
|
const portalOrgSlug = await resolveCurrentOrgPortalSlug();
|
|
10
|
-
|
|
12
|
+
const auth = await getAuthFromCookies().catch(() => null);
|
|
13
|
+
const activeOrganizationId = resolveActiveOrganizationId(auth);
|
|
14
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { className: "space-y-4", children: /* @__PURE__ */ jsx(
|
|
15
|
+
PortalUsersPageClient,
|
|
16
|
+
{
|
|
17
|
+
portalOrigin,
|
|
18
|
+
portalOrgSlug,
|
|
19
|
+
activeOrganizationId
|
|
20
|
+
}
|
|
21
|
+
) }) });
|
|
11
22
|
}
|
|
12
23
|
export {
|
|
13
24
|
CustomerAccountsPage as default
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customer_accounts/backend/customer_accounts/users/page.tsx"],
|
|
4
|
-
"sourcesContent": ["import { headers } from 'next/headers'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { resolvePortalRequestOrigin } from '../../../lib/portalUrl'\nimport { resolveCurrentOrgPortalSlug } from '../../../lib/portalOrgSlug'\nimport { PortalUsersPageClient } from './PortalUsersPageClient'\n\nexport default async function CustomerAccountsPage() {\n const portalOrigin = resolvePortalRequestOrigin(await headers())\n const portalOrgSlug = await resolveCurrentOrgPortalSlug()\n return (\n <Page>\n <PageBody className=\"space-y-4\">\n <PortalUsersPageClient
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { headers } from 'next/headers'\nimport { getAuthFromCookies } from '@open-mercato/shared/lib/auth/server'\nimport { resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { resolvePortalRequestOrigin } from '../../../lib/portalUrl'\nimport { resolveCurrentOrgPortalSlug } from '../../../lib/portalOrgSlug'\nimport { PortalUsersPageClient } from './PortalUsersPageClient'\n\nexport default async function CustomerAccountsPage() {\n const portalOrigin = resolvePortalRequestOrigin(await headers())\n const portalOrgSlug = await resolveCurrentOrgPortalSlug()\n const auth = await getAuthFromCookies().catch(() => null)\n const activeOrganizationId = resolveActiveOrganizationId(auth)\n return (\n <Page>\n <PageBody className=\"space-y-4\">\n <PortalUsersPageClient\n portalOrigin={portalOrigin}\n portalOrgSlug={portalOrgSlug}\n activeOrganizationId={activeOrganizationId}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAgBQ;AAhBR,SAAS,eAAe;AACxB,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAC5C,SAAS,MAAM,gBAAgB;AAC/B,SAAS,kCAAkC;AAC3C,SAAS,mCAAmC;AAC5C,SAAS,6BAA6B;AAEtC,eAAO,uBAA8C;AACnD,QAAM,eAAe,2BAA2B,MAAM,QAAQ,CAAC;AAC/D,QAAM,gBAAgB,MAAM,4BAA4B;AACxD,QAAM,OAAO,MAAM,mBAAmB,EAAE,MAAM,MAAM,IAAI;AACxD,QAAM,uBAAuB,4BAA4B,IAAI;AAC7D,SACE,oBAAC,QACC,8BAAC,YAAS,WAAU,aAClB;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF,GACF,GACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -78,7 +78,8 @@ const adminCreateUserSchema = z.object({
|
|
|
78
78
|
password: passwordField,
|
|
79
79
|
displayName: displayNameField,
|
|
80
80
|
roleIds: z.array(z.string().uuid()).optional(),
|
|
81
|
-
customerEntityId: z.string().uuid().optional()
|
|
81
|
+
customerEntityId: z.string().uuid().optional(),
|
|
82
|
+
organizationId: z.string().uuid().optional()
|
|
82
83
|
});
|
|
83
84
|
const adminUpdateUserSchema = z.object({
|
|
84
85
|
displayName: displayNameField.optional(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customer_accounts/data/validators.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { normalizeHostname } from '@open-mercato/core/modules/customer_accounts/lib/hostname'\n\nconst emailField = z.string().email().max(255)\nconst passwordField = z.string().min(8).max(128)\nconst displayNameField = z.string().min(1).max(255)\n\nexport const signupSchema = z.object({\n email: emailField,\n password: passwordField,\n displayName: displayNameField,\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const loginSchema = z.object({\n email: emailField,\n password: z.string().min(1).max(128),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const emailVerifySchema = z.object({\n token: z.string().min(1).max(512),\n})\n\nexport const passwordResetRequestSchema = z.object({\n email: emailField,\n tenantId: z.string().uuid().optional(),\n})\n\nexport const passwordResetConfirmSchema = z.object({\n token: z.string().min(1).max(512),\n password: passwordField,\n})\n\nexport const magicLinkRequestSchema = z.object({\n email: emailField,\n tenantId: z.string().uuid().optional(),\n})\n\nexport const magicLinkVerifySchema = z.object({\n token: z.string().min(1).max(512),\n})\n\nexport const invitationAcceptSchema = z.object({\n token: z.string().min(1).max(512),\n password: passwordField,\n displayName: displayNameField,\n})\n\nexport const profileUpdateSchema = z.object({\n displayName: displayNameField.optional(),\n})\n\nexport const passwordChangeSchema = z.object({\n currentPassword: z.string().min(1).max(128),\n newPassword: passwordField,\n})\n\nexport const createRoleSchema = z.object({\n name: z.string().min(1).max(255),\n slug: z.string().min(1).max(255).regex(/^[a-z0-9_-]+$/),\n description: z.string().max(1000).optional(),\n isDefault: z.boolean().optional(),\n customerAssignable: z.boolean().optional(),\n})\n\nexport const updateRoleSchema = z.object({\n name: z.string().min(1).max(255).optional(),\n description: z.string().max(1000).optional(),\n isDefault: z.boolean().optional(),\n customerAssignable: z.boolean().optional(),\n})\n\nexport const updateRoleAclSchema = z.object({\n features: z.array(z.string().min(1).max(255)),\n isPortalAdmin: z.boolean().optional(),\n})\n\nexport const inviteUserSchema = z.object({\n email: emailField,\n customerEntityId: z.string().uuid().optional(),\n personEntityId: z.string().uuid().optional(),\n roleIds: z.array(z.string().uuid()).min(1),\n displayName: displayNameField.optional(),\n})\n\nexport const assignRolesSchema = z.object({\n roleIds: z.array(z.string().uuid()).min(1),\n})\n\nexport const adminCreateUserSchema = z.object({\n email: emailField,\n password: passwordField,\n displayName: displayNameField,\n roleIds: z.array(z.string().uuid()).optional(),\n customerEntityId: z.string().uuid().optional(),\n})\n\nexport const adminUpdateUserSchema = z.object({\n displayName: displayNameField.optional(),\n isActive: z.boolean().optional(),\n lockedUntil: z.string().datetime().nullable().optional(),\n personEntityId: z.string().uuid().nullable().optional(),\n customerEntityId: z.string().uuid().nullable().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n})\n\nexport const adminResetPasswordSchema = z.object({\n newPassword: passwordField,\n})\n\nexport type AdminCreateUserInput = z.infer<typeof adminCreateUserSchema>\nexport type AdminResetPasswordInput = z.infer<typeof adminResetPasswordSchema>\nexport type SignupInput = z.infer<typeof signupSchema>\nexport type LoginInput = z.infer<typeof loginSchema>\nexport type EmailVerifyInput = z.infer<typeof emailVerifySchema>\nexport type PasswordResetRequestInput = z.infer<typeof passwordResetRequestSchema>\nexport type PasswordResetConfirmInput = z.infer<typeof passwordResetConfirmSchema>\nexport type MagicLinkRequestInput = z.infer<typeof magicLinkRequestSchema>\nexport type MagicLinkVerifyInput = z.infer<typeof magicLinkVerifySchema>\nexport type InvitationAcceptInput = z.infer<typeof invitationAcceptSchema>\nexport type ProfileUpdateInput = z.infer<typeof profileUpdateSchema>\nexport type PasswordChangeInput = z.infer<typeof passwordChangeSchema>\nexport type CreateRoleInput = z.infer<typeof createRoleSchema>\nexport type UpdateRoleInput = z.infer<typeof updateRoleSchema>\nexport type UpdateRoleAclInput = z.infer<typeof updateRoleAclSchema>\nexport type InviteUserInput = z.infer<typeof inviteUserSchema>\nexport type AssignRolesInput = z.infer<typeof assignRolesSchema>\nexport type AdminUpdateUserInput = z.infer<typeof adminUpdateUserSchema>\n\n// Custom domain mapping\nexport const hostnameSchema = z\n .string()\n .min(1, 'Hostname is required')\n .max(512, 'Hostname is too long')\n .transform((value, ctx) => {\n try {\n return normalizeHostname(value)\n } catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: err instanceof Error ? err.message : 'Invalid hostname',\n })\n return z.NEVER\n }\n })\n\nexport const registerDomainSchema = z.object({\n hostname: hostnameSchema,\n organizationId: z.string().uuid(),\n replacesDomainId: z.string().uuid().optional(),\n})\n\nexport type RegisterDomainInput = z.infer<typeof registerDomainSchema>\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport { normalizeHostname } from '@open-mercato/core/modules/customer_accounts/lib/hostname'\n\nconst emailField = z.string().email().max(255)\nconst passwordField = z.string().min(8).max(128)\nconst displayNameField = z.string().min(1).max(255)\n\nexport const signupSchema = z.object({\n email: emailField,\n password: passwordField,\n displayName: displayNameField,\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const loginSchema = z.object({\n email: emailField,\n password: z.string().min(1).max(128),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const emailVerifySchema = z.object({\n token: z.string().min(1).max(512),\n})\n\nexport const passwordResetRequestSchema = z.object({\n email: emailField,\n tenantId: z.string().uuid().optional(),\n})\n\nexport const passwordResetConfirmSchema = z.object({\n token: z.string().min(1).max(512),\n password: passwordField,\n})\n\nexport const magicLinkRequestSchema = z.object({\n email: emailField,\n tenantId: z.string().uuid().optional(),\n})\n\nexport const magicLinkVerifySchema = z.object({\n token: z.string().min(1).max(512),\n})\n\nexport const invitationAcceptSchema = z.object({\n token: z.string().min(1).max(512),\n password: passwordField,\n displayName: displayNameField,\n})\n\nexport const profileUpdateSchema = z.object({\n displayName: displayNameField.optional(),\n})\n\nexport const passwordChangeSchema = z.object({\n currentPassword: z.string().min(1).max(128),\n newPassword: passwordField,\n})\n\nexport const createRoleSchema = z.object({\n name: z.string().min(1).max(255),\n slug: z.string().min(1).max(255).regex(/^[a-z0-9_-]+$/),\n description: z.string().max(1000).optional(),\n isDefault: z.boolean().optional(),\n customerAssignable: z.boolean().optional(),\n})\n\nexport const updateRoleSchema = z.object({\n name: z.string().min(1).max(255).optional(),\n description: z.string().max(1000).optional(),\n isDefault: z.boolean().optional(),\n customerAssignable: z.boolean().optional(),\n})\n\nexport const updateRoleAclSchema = z.object({\n features: z.array(z.string().min(1).max(255)),\n isPortalAdmin: z.boolean().optional(),\n})\n\nexport const inviteUserSchema = z.object({\n email: emailField,\n customerEntityId: z.string().uuid().optional(),\n personEntityId: z.string().uuid().optional(),\n roleIds: z.array(z.string().uuid()).min(1),\n displayName: displayNameField.optional(),\n})\n\nexport const assignRolesSchema = z.object({\n roleIds: z.array(z.string().uuid()).min(1),\n})\n\nexport const adminCreateUserSchema = z.object({\n email: emailField,\n password: passwordField,\n displayName: displayNameField,\n roleIds: z.array(z.string().uuid()).optional(),\n customerEntityId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const adminUpdateUserSchema = z.object({\n displayName: displayNameField.optional(),\n isActive: z.boolean().optional(),\n lockedUntil: z.string().datetime().nullable().optional(),\n personEntityId: z.string().uuid().nullable().optional(),\n customerEntityId: z.string().uuid().nullable().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n})\n\nexport const adminResetPasswordSchema = z.object({\n newPassword: passwordField,\n})\n\nexport type AdminCreateUserInput = z.infer<typeof adminCreateUserSchema>\nexport type AdminResetPasswordInput = z.infer<typeof adminResetPasswordSchema>\nexport type SignupInput = z.infer<typeof signupSchema>\nexport type LoginInput = z.infer<typeof loginSchema>\nexport type EmailVerifyInput = z.infer<typeof emailVerifySchema>\nexport type PasswordResetRequestInput = z.infer<typeof passwordResetRequestSchema>\nexport type PasswordResetConfirmInput = z.infer<typeof passwordResetConfirmSchema>\nexport type MagicLinkRequestInput = z.infer<typeof magicLinkRequestSchema>\nexport type MagicLinkVerifyInput = z.infer<typeof magicLinkVerifySchema>\nexport type InvitationAcceptInput = z.infer<typeof invitationAcceptSchema>\nexport type ProfileUpdateInput = z.infer<typeof profileUpdateSchema>\nexport type PasswordChangeInput = z.infer<typeof passwordChangeSchema>\nexport type CreateRoleInput = z.infer<typeof createRoleSchema>\nexport type UpdateRoleInput = z.infer<typeof updateRoleSchema>\nexport type UpdateRoleAclInput = z.infer<typeof updateRoleAclSchema>\nexport type InviteUserInput = z.infer<typeof inviteUserSchema>\nexport type AssignRolesInput = z.infer<typeof assignRolesSchema>\nexport type AdminUpdateUserInput = z.infer<typeof adminUpdateUserSchema>\n\n// Custom domain mapping\nexport const hostnameSchema = z\n .string()\n .min(1, 'Hostname is required')\n .max(512, 'Hostname is too long')\n .transform((value, ctx) => {\n try {\n return normalizeHostname(value)\n } catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: err instanceof Error ? err.message : 'Invalid hostname',\n })\n return z.NEVER\n }\n })\n\nexport const registerDomainSchema = z.object({\n hostname: hostnameSchema,\n organizationId: z.string().uuid(),\n replacesDomainId: z.string().uuid().optional(),\n})\n\nexport type RegisterDomainInput = z.infer<typeof registerDomainSchema>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
|
+
import {
|
|
5
|
+
organizationScopeRequiredResponse,
|
|
6
|
+
resolveActiveOrganizationId
|
|
7
|
+
} from "@open-mercato/shared/lib/auth/organizationScope";
|
|
8
|
+
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
9
|
+
const organizationIdSchema = z.string().uuid();
|
|
10
|
+
async function organizationNotFound() {
|
|
11
|
+
const { translate } = await resolveTranslations();
|
|
12
|
+
return {
|
|
13
|
+
ok: false,
|
|
14
|
+
response: NextResponse.json({ ok: false, error: translate("customer_accounts.admin.errors.organizationNotFound", "Organization not found") }, { status: 400 })
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async function resolveAdminTargetOrganization({
|
|
18
|
+
container,
|
|
19
|
+
auth,
|
|
20
|
+
request,
|
|
21
|
+
requestedOrganizationId
|
|
22
|
+
}) {
|
|
23
|
+
const requested = typeof requestedOrganizationId === "string" && requestedOrganizationId.trim().length > 0 ? requestedOrganizationId.trim() : null;
|
|
24
|
+
const activeOrganizationId = resolveActiveOrganizationId(auth);
|
|
25
|
+
if (!requested) {
|
|
26
|
+
if (!activeOrganizationId) return { ok: false, response: organizationScopeRequiredResponse() };
|
|
27
|
+
return { ok: true, organizationId: activeOrganizationId };
|
|
28
|
+
}
|
|
29
|
+
if (!organizationIdSchema.safeParse(requested).success) return organizationNotFound();
|
|
30
|
+
if (requested === activeOrganizationId) return { ok: true, organizationId: requested };
|
|
31
|
+
const scope = await resolveOrganizationScopeForRequest({
|
|
32
|
+
container,
|
|
33
|
+
auth,
|
|
34
|
+
request,
|
|
35
|
+
selectedId: requested,
|
|
36
|
+
tenantId: auth.tenantId ?? null
|
|
37
|
+
});
|
|
38
|
+
if (scope.selectionRejected || scope.selectedId !== requested || scope.tenantId !== (auth.tenantId ?? null)) {
|
|
39
|
+
return organizationNotFound();
|
|
40
|
+
}
|
|
41
|
+
return { ok: true, organizationId: requested };
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
resolveAdminTargetOrganization
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=adminOrganizationScope.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customer_accounts/lib/adminOrganizationScope.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n organizationScopeRequiredResponse,\n resolveActiveOrganizationId,\n} from '@open-mercato/shared/lib/auth/organizationScope'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\n\nexport type AdminTargetOrganizationResult =\n | { ok: true; organizationId: string }\n | { ok: false; response: Response }\n\nconst organizationIdSchema = z.string().uuid()\n\nasync function organizationNotFound(): Promise<AdminTargetOrganizationResult> {\n const { translate } = await resolveTranslations()\n return {\n ok: false,\n response: NextResponse.json({ ok: false, error: translate('customer_accounts.admin.errors.organizationNotFound', 'Organization not found') }, { status: 400 }),\n }\n}\n\n/**\n * Resolves the organization a staff-initiated customer-accounts write targets.\n *\n * Without a request the caller's active organization is used, falling back to the\n * actor's own organization under an \"all organizations\" selection.\n *\n * An explicitly requested organization is honored only when the organization-scope\n * resolver grants it for the caller's own tenant: it must exist, not be deleted, and\n * sit inside the caller's allowed organization set.\n */\nexport async function resolveAdminTargetOrganization({\n container,\n auth,\n request,\n requestedOrganizationId,\n}: {\n container: AwilixContainer\n auth: NonNullable<AuthContext>\n request: Request\n requestedOrganizationId?: string | null\n}): Promise<AdminTargetOrganizationResult> {\n const requested = typeof requestedOrganizationId === 'string' && requestedOrganizationId.trim().length > 0\n ? requestedOrganizationId.trim()\n : null\n const activeOrganizationId = resolveActiveOrganizationId(auth)\n\n if (!requested) {\n if (!activeOrganizationId) return { ok: false, response: organizationScopeRequiredResponse() }\n return { ok: true, organizationId: activeOrganizationId }\n }\n\n if (!organizationIdSchema.safeParse(requested).success) return organizationNotFound()\n if (requested === activeOrganizationId) return { ok: true, organizationId: requested }\n\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request,\n selectedId: requested,\n tenantId: auth.tenantId ?? null,\n })\n if (scope.selectionRejected || scope.selectedId !== requested || scope.tenantId !== (auth.tenantId ?? null)) {\n return organizationNotFound()\n }\n return { ok: true, organizationId: requested }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAGlB,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0CAA0C;AAMnD,MAAM,uBAAuB,EAAE,OAAO,EAAE,KAAK;AAE7C,eAAe,uBAA+D;AAC5E,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,uDAAuD,wBAAwB,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/J;AACF;AAYA,eAAsB,+BAA+B;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAK2C;AACzC,QAAM,YAAY,OAAO,4BAA4B,YAAY,wBAAwB,KAAK,EAAE,SAAS,IACrG,wBAAwB,KAAK,IAC7B;AACJ,QAAM,uBAAuB,4BAA4B,IAAI;AAE7D,MAAI,CAAC,WAAW;AACd,QAAI,CAAC,qBAAsB,QAAO,EAAE,IAAI,OAAO,UAAU,kCAAkC,EAAE;AAC7F,WAAO,EAAE,IAAI,MAAM,gBAAgB,qBAAqB;AAAA,EAC1D;AAEA,MAAI,CAAC,qBAAqB,UAAU,SAAS,EAAE,QAAS,QAAO,qBAAqB;AACpF,MAAI,cAAc,qBAAsB,QAAO,EAAE,IAAI,MAAM,gBAAgB,UAAU;AAErF,QAAM,QAAQ,MAAM,mCAAmC;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,UAAU,KAAK,YAAY;AAAA,EAC7B,CAAC;AACD,MAAI,MAAM,qBAAqB,MAAM,eAAe,aAAa,MAAM,cAAc,KAAK,YAAY,OAAO;AAC3G,WAAO,qBAAqB;AAAA,EAC9B;AACA,SAAO,EAAE,IAAI,MAAM,gBAAgB,UAAU;AAC/C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|