@open-mercato/core 0.6.3-develop.3525.2.cc18b8a43f → 0.6.3-develop.3562.1.71049275ff
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/auth/api/users/route.js +49 -9
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +32 -14
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +1 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +1 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/roleOptions.js +1 -1
- package/dist/modules/auth/backend/users/roleOptions.js.map +2 -2
- package/dist/modules/auth/commands/roles.js +8 -0
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +53 -26
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +41 -12
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +104 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/data/validators.js +20 -1
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/api/users/route.ts +51 -9
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +35 -13
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +1 -1
- package/src/modules/auth/backend/users/create/page.tsx +1 -1
- package/src/modules/auth/backend/users/roleOptions.ts +2 -1
- package/src/modules/auth/commands/roles.ts +8 -0
- package/src/modules/auth/components/AclEditor.tsx +50 -24
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +38 -12
- package/src/modules/sales/commands/documents.ts +116 -0
- package/src/modules/sales/data/validators.ts +38 -0
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
|
@@ -67,7 +67,8 @@ function AclEditor({
|
|
|
67
67
|
onChange,
|
|
68
68
|
userRoles,
|
|
69
69
|
currentUserIsSuperAdmin,
|
|
70
|
-
tenantId
|
|
70
|
+
tenantId,
|
|
71
|
+
preserveOnTenantChange = false
|
|
71
72
|
}) {
|
|
72
73
|
const actorIsSuperAdmin = !!currentUserIsSuperAdmin;
|
|
73
74
|
const [loading, setLoading] = React.useState(true);
|
|
@@ -98,8 +99,34 @@ function AclEditor({
|
|
|
98
99
|
},
|
|
99
100
|
[actorSanitizeFeatures]
|
|
100
101
|
);
|
|
102
|
+
const tenantIdRef = React.useRef(tenantId);
|
|
101
103
|
React.useEffect(() => {
|
|
102
|
-
|
|
104
|
+
tenantIdRef.current = tenantId;
|
|
105
|
+
}, [tenantId]);
|
|
106
|
+
const hasMountedRef = React.useRef(false);
|
|
107
|
+
const fetchAclState = React.useCallback(async (forTenantId, cancelledRef) => {
|
|
108
|
+
try {
|
|
109
|
+
const aclQuery = new URLSearchParams();
|
|
110
|
+
aclQuery.set(kind === "user" ? "userId" : "roleId", targetId);
|
|
111
|
+
if (forTenantId) aclQuery.set("tenantId", forTenantId);
|
|
112
|
+
const aclQueryString = aclQuery.toString();
|
|
113
|
+
const aclJson = await readJsonOr(
|
|
114
|
+
`/api/auth/${kind === "user" ? "users" : "roles"}/acl${aclQueryString ? `?${aclQueryString}` : ""}`,
|
|
115
|
+
void 0,
|
|
116
|
+
{ hasCustomAcl: true, isSuperAdmin: false, features: [], organizations: null }
|
|
117
|
+
);
|
|
118
|
+
if (cancelledRef.current) return;
|
|
119
|
+
const customAclExists = aclJson.hasCustomAcl !== false;
|
|
120
|
+
setHasCustomAcl(customAclExists);
|
|
121
|
+
setOverrideEnabled(customAclExists);
|
|
122
|
+
setIsSuperAdmin(!!aclJson.isSuperAdmin);
|
|
123
|
+
setGranted(actorSanitizeFeatures(aclJson.features));
|
|
124
|
+
setOrganizations(aclJson.organizations == null ? null : Array.isArray(aclJson.organizations) ? aclJson.organizations : []);
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}, [kind, targetId, actorSanitizeFeatures]);
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
const cancelled = { current: false };
|
|
103
130
|
async function load() {
|
|
104
131
|
setLoading(true);
|
|
105
132
|
try {
|
|
@@ -108,32 +135,33 @@ function AclEditor({
|
|
|
108
135
|
void 0,
|
|
109
136
|
{ items: [], modules: [] }
|
|
110
137
|
);
|
|
111
|
-
if (!cancelled) {
|
|
138
|
+
if (!cancelled.current) {
|
|
112
139
|
setFeatures(fJson.items || []);
|
|
113
140
|
setModules(fJson.modules || []);
|
|
114
141
|
}
|
|
115
142
|
} catch {
|
|
116
143
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
await fetchAclState(tenantIdRef.current, cancelled);
|
|
145
|
+
hasMountedRef.current = true;
|
|
146
|
+
if (!cancelled.current) setLoading(false);
|
|
147
|
+
}
|
|
148
|
+
load();
|
|
149
|
+
return () => {
|
|
150
|
+
cancelled.current = true;
|
|
151
|
+
};
|
|
152
|
+
}, [kind, targetId, fetchAclState]);
|
|
153
|
+
React.useEffect(() => {
|
|
154
|
+
if (!hasMountedRef.current) return;
|
|
155
|
+
if (preserveOnTenantChange) return;
|
|
156
|
+
const cancelled = { current: false };
|
|
157
|
+
fetchAclState(tenantId, cancelled);
|
|
158
|
+
return () => {
|
|
159
|
+
cancelled.current = true;
|
|
160
|
+
};
|
|
161
|
+
}, [tenantId, preserveOnTenantChange, fetchAclState]);
|
|
162
|
+
React.useEffect(() => {
|
|
163
|
+
let cancelled = false;
|
|
164
|
+
async function loadTenantScoped() {
|
|
137
165
|
if (canEditOrganizations) {
|
|
138
166
|
try {
|
|
139
167
|
const orgQuery = new URLSearchParams();
|
|
@@ -166,13 +194,12 @@ function AclEditor({
|
|
|
166
194
|
} catch {
|
|
167
195
|
}
|
|
168
196
|
}
|
|
169
|
-
if (!cancelled) setLoading(false);
|
|
170
197
|
}
|
|
171
|
-
|
|
198
|
+
loadTenantScoped();
|
|
172
199
|
return () => {
|
|
173
200
|
cancelled = true;
|
|
174
201
|
};
|
|
175
|
-
}, [kind,
|
|
202
|
+
}, [kind, canEditOrganizations, userRoles, tenantId]);
|
|
176
203
|
React.useEffect(() => {
|
|
177
204
|
onChange?.({ isSuperAdmin, features: granted, organizations });
|
|
178
205
|
}, [isSuperAdmin, granted, organizations, onChange]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/auth/components/AclEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport Link from 'next/link'\nimport { hasFeature, matchFeature } from '@open-mercato/shared/security/features'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nfunction toTitleCase(value: string): string {\n return value.replace(/[-_.]/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())\n}\n\nfunction normalizeFeatureArray(input: unknown): string[] {\n if (!Array.isArray(input)) return []\n const dedup = new Set<string>()\n for (const value of input) {\n if (typeof value !== 'string') continue\n const trimmed = value.trim()\n if (!trimmed) continue\n dedup.add(trimmed)\n }\n return Array.from(dedup)\n}\n\nfunction isTenantRestrictedFeature(feature: string): boolean {\n if (feature === '*' || feature === 'directory.*') return true\n if (feature.startsWith('directory.tenants')) return true\n return false\n}\n\nfunction formatWildcardLabel(moduleId: string, wildcard: string): string {\n if (!wildcard.endsWith('.*')) return wildcard\n const prefix = `${moduleId}.`\n const suffix = wildcard.startsWith(prefix) ? wildcard.slice(prefix.length, -2) : wildcard.slice(0, -2)\n if (!suffix) return 'All features'\n return `All ${suffix.split('.').map(toTitleCase).join(' / ')}`\n}\n\ntype Feature = { id: string; title: string; module: string }\ntype ModuleInfo = { id: string; title: string }\ntype RoleListItem = { id?: string | null; name?: string | null }\ntype RoleListResponse = { items?: RoleListItem[] }\ntype RoleSummary = { id: string; name: string }\n\nfunction buildRoleSummaries(items: RoleListItem[], allowedNames: string[]): RoleSummary[] {\n const summaries: RoleSummary[] = []\n for (const role of items) {\n const name = typeof role?.name === 'string' ? role.name : ''\n if (!name || !allowedNames.includes(name)) continue\n const hasValidId = typeof role?.id === 'string' && role.id.length > 0\n const id = hasValidId ? (role!.id as string) : name\n summaries.push({ id, name })\n }\n return summaries\n}\n\nexport type AclData = {\n isSuperAdmin: boolean\n features: string[]\n organizations: string[] | null\n}\n\ntype FeatureListResponse = { items?: Feature[]; modules?: ModuleInfo[] }\ntype AclPayload = {\n hasCustomAcl?: boolean\n isSuperAdmin?: boolean\n features?: unknown\n organizations?: unknown\n}\ntype OrganizationListResponse = { items?: Array<{ id?: string; name?: string }> }\n\nfunction normalizeOrganizationOptions(items: OrganizationListResponse['items']): Array<{ id: string; name: string }> {\n if (!Array.isArray(items)) return []\n return items.reduce<Array<{ id: string; name: string }>>((acc, org) => {\n if (!org) return acc\n const id = typeof org.id === 'string' && org.id.trim().length > 0 ? org.id : null\n if (!id) return acc\n const name = typeof org.name === 'string' && org.name.trim().length > 0 ? org.name : id\n acc.push({ id, name })\n return acc\n }, [])\n}\n\nasync function readJsonOr<T>(\n url: string,\n init: RequestInit | undefined,\n fallback: T,\n): Promise<T> {\n const call = await apiCall<T>(url, init, { fallback })\n if (!call.ok) return fallback\n return call.result ?? fallback\n}\n\nexport function AclEditor({\n kind,\n targetId,\n canEditOrganizations,\n value,\n onChange,\n userRoles,\n currentUserIsSuperAdmin,\n tenantId,\n}: {\n kind: 'user' | 'role'\n targetId: string\n canEditOrganizations: boolean\n value?: AclData\n onChange?: (data: AclData) => void\n userRoles?: string[]\n currentUserIsSuperAdmin?: boolean\n tenantId?: string | null\n}) {\n const actorIsSuperAdmin = !!currentUserIsSuperAdmin\n const [loading, setLoading] = React.useState(true)\n const [features, setFeatures] = React.useState<Feature[]>([])\n const t = useT()\n const [modules, setModules] = React.useState<ModuleInfo[]>([])\n const [granted, setGranted] = React.useState<string[]>(() => {\n const normalized = normalizeFeatureArray(value?.features)\n return actorIsSuperAdmin ? normalized : normalized.filter((feature) => !isTenantRestrictedFeature(feature))\n })\n const [isSuperAdmin, setIsSuperAdmin] = React.useState(value?.isSuperAdmin || false)\n const [organizations, setOrganizations] = React.useState<string[] | null>(value?.organizations ?? null)\n const [orgOptions, setOrgOptions] = React.useState<{ id: string; name: string }[]>([])\n const [hasCustomAcl, setHasCustomAcl] = React.useState(true)\n const [overrideEnabled, setOverrideEnabled] = React.useState(false)\n const [roleDetails, setRoleDetails] = React.useState<RoleSummary[]>([])\n\n const actorSanitizeFeatures = React.useCallback(\n (list: unknown): string[] => {\n const normalized = normalizeFeatureArray(list)\n if (actorIsSuperAdmin) return normalized\n return normalized.filter((feature) => !isTenantRestrictedFeature(feature))\n },\n [actorIsSuperAdmin],\n )\n\n const updateGranted = React.useCallback(\n (updater: (prev: string[]) => string[]) => {\n setGranted((prev) => actorSanitizeFeatures(updater(prev)))\n },\n [actorSanitizeFeatures],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n try {\n const fJson = await readJsonOr<FeatureListResponse>(\n '/api/auth/features',\n undefined,\n { items: [], modules: [] },\n )\n if (!cancelled) {\n setFeatures(fJson.items || [])\n setModules(fJson.modules || [])\n }\n } catch {}\n try {\n const aclQuery = new URLSearchParams()\n aclQuery.set(kind === 'user' ? 'userId' : 'roleId', targetId)\n if (tenantId) aclQuery.set('tenantId', tenantId)\n const aclQueryString = aclQuery.toString()\n const aclJson = await readJsonOr<AclPayload>(\n `/api/auth/${kind === 'user' ? 'users' : 'roles'}/acl${aclQueryString ? `?${aclQueryString}` : ''}`,\n undefined,\n { hasCustomAcl: true, isSuperAdmin: false, features: [], organizations: null },\n )\n if (!cancelled) {\n const customAclExists = aclJson.hasCustomAcl !== false\n setHasCustomAcl(customAclExists)\n setOverrideEnabled(customAclExists)\n setIsSuperAdmin(!!aclJson.isSuperAdmin)\n setGranted(actorSanitizeFeatures(aclJson.features))\n setOrganizations(aclJson.organizations == null ? null : Array.isArray(aclJson.organizations) ? aclJson.organizations : [])\n }\n } catch {}\n if (canEditOrganizations) {\n try {\n const orgQuery = new URLSearchParams()\n if (tenantId) orgQuery.set('tenantId', tenantId)\n const orgQueryString = orgQuery.toString()\n const oJson = await readJsonOr<OrganizationListResponse>(\n `/api/directory/organizations${orgQueryString ? `?${orgQueryString}` : ''}`,\n undefined,\n { items: [] },\n )\n if (!cancelled) setOrgOptions(normalizeOrganizationOptions(oJson.items))\n } catch {}\n }\n if (kind === 'user' && userRoles && userRoles.length > 0) {\n try {\n const roleQuery = new URLSearchParams({ pageSize: '100' })\n if (tenantId) roleQuery.set('tenantId', tenantId)\n const roleQueryString = roleQuery.toString()\n const rolesJson = await readJsonOr<RoleListResponse>(\n `/api/auth/roles${roleQueryString ? `?${roleQueryString}` : ''}`,\n undefined,\n { items: [] },\n )\n if (!cancelled) {\n const allRoles = Array.isArray(rolesJson.items) ? rolesJson.items : []\n const userRoleDetails: RoleSummary[] = buildRoleSummaries(allRoles, userRoles)\n setRoleDetails(userRoleDetails)\n }\n } catch {}\n }\n if (!cancelled) setLoading(false)\n }\n load()\n return () => { cancelled = true }\n }, [kind, targetId, canEditOrganizations, userRoles, actorSanitizeFeatures, tenantId])\n\n // Notify parent of changes\n React.useEffect(() => {\n onChange?.({ isSuperAdmin, features: granted, organizations })\n }, [isSuperAdmin, granted, organizations, onChange])\n\n const grouped = React.useMemo(() => {\n const moduleMap = new Map<string, string>()\n for (const m of modules) {\n moduleMap.set(m.id, m.title)\n }\n const map = new Map<string, { moduleId: string; moduleTitle: string; features: Feature[] }>()\n for (const f of features) {\n const moduleId = f.module\n const moduleTitle = moduleMap.get(moduleId) || moduleId\n if (!map.has(moduleId)) {\n map.set(moduleId, { moduleId, moduleTitle, features: [] })\n }\n map.get(moduleId)!.features.push(f)\n }\n return Array.from(map.values()).sort((a, b) => a.moduleTitle.localeCompare(b.moduleTitle))\n }, [features, modules])\n\n const hasGlobalWildcard = granted.includes('*')\n const hasOrganizationRestriction = Array.isArray(organizations) && organizations.length > 0\n const showOrganizationWarning =\n (kind === 'role' || overrideEnabled) &&\n canEditOrganizations &&\n !isSuperAdmin &&\n hasOrganizationRestriction &&\n granted.length === 0\n\n \n const toggleWildcard = React.useCallback((wildcard: string, enable: boolean) => {\n if (!actorIsSuperAdmin && enable && isTenantRestrictedFeature(wildcard)) return\n updateGranted((prev) => {\n if (enable) {\n if (prev.includes(wildcard)) return prev\n return [...prev, wildcard]\n }\n return prev.filter((feature) => feature !== wildcard)\n })\n }, [actorIsSuperAdmin, updateGranted])\n\n const toggleModuleWildcard = React.useCallback((moduleId: string, enable: boolean) => {\n toggleWildcard(`${moduleId}.*`, enable)\n }, [toggleWildcard])\n\n const isModuleWildcardEnabled = (moduleId: string) => {\n return granted.includes(`${moduleId}.*`)\n }\n\n const isFeatureCoveredByWildcard = (featureId: string) =>\n granted.some((feature) => (feature === '*' || feature.endsWith('.*')) && matchFeature(featureId, feature))\n\n const isFeatureChecked = (featureId: string) => hasFeature(granted, featureId)\n\n if (loading) return <div className=\"text-sm text-muted-foreground\">Loading ACL\u2026</div>\n\n const showRoleBanner = kind === 'user' && !hasCustomAcl && !overrideEnabled\n\n return (\n <div className=\"space-y-4\">\n {showRoleBanner && (\n <div className=\"rounded-lg border border-blue-200 bg-blue-50 p-4\">\n <div className=\"text-sm font-medium text-blue-900 mb-2\">\n Permissions inherited from roles\n </div>\n <div className=\"text-sm text-blue-700 mb-3\">\n This user currently inherits permissions from their assigned roles.\n {roleDetails.length > 0 && (\n <span>\n {' '}Assigned roles:{' '}\n {roleDetails.map((role, idx) => {\n const roleId = typeof role?.id === 'string' && role.id.length > 0 ? role.id : `role-${idx}`\n const roleName = typeof role?.name === 'string' && role.name.length > 0 ? role.name : roleId\n return (\n <React.Fragment key={roleId}>\n {idx > 0 && ', '}\n <Link \n href={`/backend/roles/${roleId}/edit`}\n className=\"font-semibold text-blue-900 underline hover:text-blue-950 transition-colors\"\n >\n {roleName}\n </Link>\n </React.Fragment>\n )\n })}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <input \n id=\"overrideAcl\" \n type=\"checkbox\" \n className=\"h-4 w-4\" \n checked={overrideEnabled} \n onChange={(e) => setOverrideEnabled(e.target.checked)} \n />\n <label htmlFor=\"overrideAcl\" className=\"text-sm text-blue-900 font-medium\">\n Override permissions for this user only\n </label>\n </div>\n </div>\n )}\n {(kind === 'role' || overrideEnabled) && (\n <>\n <div className=\"flex items-center gap-2\">\n <input\n id=\"isSuperAdmin\"\n type=\"checkbox\"\n className=\"h-4 w-4\"\n checked={isSuperAdmin}\n disabled={!actorIsSuperAdmin}\n onChange={(e) => setIsSuperAdmin(!!e.target.checked)}\n />\n <label htmlFor=\"isSuperAdmin\" className=\"text-sm\">Super Admin (all features)</label>\n </div>\n {!actorIsSuperAdmin && (\n <p className=\"text-xs text-muted-foreground\">Only super administrators can change this option.</p>\n )}\n {!isSuperAdmin && (\n <>\n {hasGlobalWildcard && (\n <div className=\"rounded border border-blue-200 bg-blue-50 p-3\">\n <div className=\"text-sm font-medium text-blue-900\">Global wildcard (*) enabled</div>\n <div className=\"text-xs text-blue-700 mt-1\">This grants access to all features in the system.</div>\n <Button \n variant=\"outline\" \n size=\"sm\" \n className=\"mt-2\"\n onClick={() => updateGranted((prev) => prev.filter((x) => x !== '*'))}\n >\n Remove global wildcard\n </Button>\n </div>\n )}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {grouped.map((group) => {\n const moduleWildcard = isModuleWildcardEnabled(group.moduleId)\n const nestedWildcards = Array.from(\n new Set(\n granted.filter(\n (feature) =>\n feature !== '*' &&\n feature.endsWith('.*') &&\n feature.startsWith(`${group.moduleId}.`) &&\n feature !== `${group.moduleId}.*`,\n ),\n ),\n )\n .map((wildcard) => {\n const prefix = wildcard.slice(0, -1)\n const relatedFeatures = group.features.filter((feature) => feature.id.startsWith(prefix))\n return { wildcard, features: relatedFeatures }\n })\n .sort((a, b) => a.wildcard.localeCompare(b.wildcard))\n const nestedCoveredIds = new Set<string>()\n for (const entry of nestedWildcards) {\n for (const feature of entry.features) nestedCoveredIds.add(feature.id)\n }\n const moduleRestricted = !actorIsSuperAdmin && isTenantRestrictedFeature(`${group.moduleId}.*`)\n const moduleCheckboxDisabled = hasGlobalWildcard || moduleRestricted\n return (\n <div key={group.moduleId} className=\"rounded border p-3\">\n <div className=\"flex items-center justify-between mb-3 pb-2 border-b\">\n <div className=\"text-sm font-medium\">{group.moduleTitle}</div>\n <div className=\"flex items-center gap-2\">\n <input \n id={`module-${group.moduleId}`} \n type=\"checkbox\" \n className=\"h-4 w-4\" \n checked={moduleWildcard || hasGlobalWildcard} \n disabled={moduleCheckboxDisabled}\n onChange={(e) => toggleModuleWildcard(group.moduleId, e.target.checked)} \n />\n <label htmlFor={`module-${group.moduleId}`} className=\"text-sm text-muted-foreground\">\n All {moduleWildcard && !hasGlobalWildcard ? <span className=\"font-medium text-blue-600\">({group.moduleId}.*)</span> : ''}\n {moduleRestricted ? <span className=\"ml-2 text-xs font-medium text-muted-foreground\">(manage via super admin)</span> : null}\n </label>\n </div>\n </div>\n {nestedWildcards.length > 0 && (\n <div className=\"space-y-3 mb-3\">\n {nestedWildcards.map(({ wildcard, features: wildcardFeatures }) => {\n const checked = granted.includes(wildcard) || hasGlobalWildcard || moduleWildcard\n const wildcardRestricted = !actorIsSuperAdmin && isTenantRestrictedFeature(wildcard)\n const disabled = hasGlobalWildcard || moduleWildcard || wildcardRestricted\n return (\n <div key={wildcard} className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <input\n id={`wildcard-${wildcard}`}\n type=\"checkbox\"\n className=\"h-4 w-4\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => toggleWildcard(wildcard, !!e.target.checked)}\n />\n <label\n htmlFor={`wildcard-${wildcard}`}\n className={`text-sm ${disabled ? 'text-muted-foreground' : ''}`}\n >\n {formatWildcardLabel(group.moduleId, wildcard)}{' '}\n <span className=\"text-muted-foreground text-xs font-mono\">({wildcard})</span>\n {wildcardRestricted ? (\n <span className=\"ml-2 text-xs font-medium text-muted-foreground\">\n {t('auth.acl.restricted', 'Restricted')}\n </span>\n ) : null}\n </label>\n </div>\n {wildcardFeatures.length > 0 && (\n <div className=\"relative ml-6 pl-4 text-sm text-muted-foreground space-y-1\">\n <div className=\"absolute left-0 top-1 bottom-1 w-px bg-border\" aria-hidden />\n {wildcardFeatures.map((wf) => (\n <div key={`${wildcard}-${wf.id}`} className=\"pl-2\">\n <span>\n {wf.title}{' '}\n <span className=\"text-xs font-mono text-muted-foreground\">({wf.id})</span>\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n <div className=\"space-y-2\">\n {group.features.map((f) => {\n if (nestedCoveredIds.has(f.id)) return null\n const checked = isFeatureChecked(f.id)\n const isWildcardCovered = isFeatureCoveredByWildcard(f.id)\n const restricted = !actorIsSuperAdmin && isTenantRestrictedFeature(f.id)\n const disabled = isWildcardCovered || restricted\n return (\n <div key={f.id} className=\"flex items-center gap-2\">\n <input\n id={`f-${f.id}`}\n type=\"checkbox\"\n className=\"h-4 w-4\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => {\n const on = !!e.target.checked\n updateGranted((prev) => {\n if (on) return [...prev, f.id]\n return prev.filter((x) => x !== f.id)\n })\n }}\n />\n <label\n htmlFor={`f-${f.id}`}\n className={`text-sm ${disabled ? 'text-muted-foreground' : ''}`}\n >\n {f.title} <span className=\"text-muted-foreground text-xs\">({f.id})</span>\n {restricted ? (\n <span className=\"ml-2 text-xs font-medium text-muted-foreground\">\n {t('auth.acl.restricted', 'Restricted')}\n </span>\n ) : null}\n </label>\n </div>\n )\n })}\n </div>\n </div>\n )\n })}\n </div>\n </>\n )}\n {canEditOrganizations && (\n <div className=\"rounded border p-3\">\n <div className=\"text-sm font-medium mb-2\">\n {t('auth.acl.organizationsScope', 'Organizations scope')}\n </div>\n <div className=\"text-xs text-muted-foreground mb-2\">Empty = all organizations. Select one or more to restrict.</div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2\">\n {orgOptions.map((o) => {\n const checked = organizations == null ? false : (organizations || []).includes(o.id)\n return (\n <div key={o.id} className=\"flex items-center gap-2\">\n <input id={`org-${o.id}`} type=\"checkbox\" className=\"h-4 w-4\" checked={checked} onChange={(e) => {\n const on = !!e.target.checked\n setOrganizations((prev) => {\n if (prev == null) return on ? [o.id] : []\n return on ? Array.from(new Set([...(prev || []), o.id])) : (prev || []).filter((x) => x !== o.id)\n })\n }} />\n <label htmlFor={`org-${o.id}`} className=\"text-sm\">{o.name}</label>\n </div>\n )\n })}\n </div>\n <div className=\"mt-2\">\n <Button variant=\"outline\" onClick={() => setOrganizations(null)}>{t('auth.acl.allowAllOrganizations', 'Allow all organizations')}</Button>\n </div>\n {showOrganizationWarning && (\n <div className=\"mt-3 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n {t('auth.acl.organizationWarning', 'Organization restrictions are saved only when at least one feature override is selected. Add a feature or enable a module wildcard before saving.')}\n </div>\n )}\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport Link from 'next/link'\nimport { hasFeature, matchFeature } from '@open-mercato/shared/security/features'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nfunction toTitleCase(value: string): string {\n return value.replace(/[-_.]/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())\n}\n\nfunction normalizeFeatureArray(input: unknown): string[] {\n if (!Array.isArray(input)) return []\n const dedup = new Set<string>()\n for (const value of input) {\n if (typeof value !== 'string') continue\n const trimmed = value.trim()\n if (!trimmed) continue\n dedup.add(trimmed)\n }\n return Array.from(dedup)\n}\n\nfunction isTenantRestrictedFeature(feature: string): boolean {\n if (feature === '*' || feature === 'directory.*') return true\n if (feature.startsWith('directory.tenants')) return true\n return false\n}\n\nfunction formatWildcardLabel(moduleId: string, wildcard: string): string {\n if (!wildcard.endsWith('.*')) return wildcard\n const prefix = `${moduleId}.`\n const suffix = wildcard.startsWith(prefix) ? wildcard.slice(prefix.length, -2) : wildcard.slice(0, -2)\n if (!suffix) return 'All features'\n return `All ${suffix.split('.').map(toTitleCase).join(' / ')}`\n}\n\ntype Feature = { id: string; title: string; module: string }\ntype ModuleInfo = { id: string; title: string }\ntype RoleListItem = { id?: string | null; name?: string | null }\ntype RoleListResponse = { items?: RoleListItem[] }\ntype RoleSummary = { id: string; name: string }\n\nfunction buildRoleSummaries(items: RoleListItem[], allowedNames: string[]): RoleSummary[] {\n const summaries: RoleSummary[] = []\n for (const role of items) {\n const name = typeof role?.name === 'string' ? role.name : ''\n if (!name || !allowedNames.includes(name)) continue\n const hasValidId = typeof role?.id === 'string' && role.id.length > 0\n const id = hasValidId ? (role!.id as string) : name\n summaries.push({ id, name })\n }\n return summaries\n}\n\nexport type AclData = {\n isSuperAdmin: boolean\n features: string[]\n organizations: string[] | null\n}\n\ntype FeatureListResponse = { items?: Feature[]; modules?: ModuleInfo[] }\ntype AclPayload = {\n hasCustomAcl?: boolean\n isSuperAdmin?: boolean\n features?: unknown\n organizations?: unknown\n}\ntype OrganizationListResponse = { items?: Array<{ id?: string; name?: string }> }\n\nfunction normalizeOrganizationOptions(items: OrganizationListResponse['items']): Array<{ id: string; name: string }> {\n if (!Array.isArray(items)) return []\n return items.reduce<Array<{ id: string; name: string }>>((acc, org) => {\n if (!org) return acc\n const id = typeof org.id === 'string' && org.id.trim().length > 0 ? org.id : null\n if (!id) return acc\n const name = typeof org.name === 'string' && org.name.trim().length > 0 ? org.name : id\n acc.push({ id, name })\n return acc\n }, [])\n}\n\nasync function readJsonOr<T>(\n url: string,\n init: RequestInit | undefined,\n fallback: T,\n): Promise<T> {\n const call = await apiCall<T>(url, init, { fallback })\n if (!call.ok) return fallback\n return call.result ?? fallback\n}\n\nexport function AclEditor({\n kind,\n targetId,\n canEditOrganizations,\n value,\n onChange,\n userRoles,\n currentUserIsSuperAdmin,\n tenantId,\n preserveOnTenantChange = false,\n}: {\n kind: 'user' | 'role'\n targetId: string\n canEditOrganizations: boolean\n value?: AclData\n onChange?: (data: AclData) => void\n userRoles?: string[]\n currentUserIsSuperAdmin?: boolean\n tenantId?: string | null\n preserveOnTenantChange?: boolean\n}) {\n const actorIsSuperAdmin = !!currentUserIsSuperAdmin\n const [loading, setLoading] = React.useState(true)\n const [features, setFeatures] = React.useState<Feature[]>([])\n const t = useT()\n const [modules, setModules] = React.useState<ModuleInfo[]>([])\n const [granted, setGranted] = React.useState<string[]>(() => {\n const normalized = normalizeFeatureArray(value?.features)\n return actorIsSuperAdmin ? normalized : normalized.filter((feature) => !isTenantRestrictedFeature(feature))\n })\n const [isSuperAdmin, setIsSuperAdmin] = React.useState(value?.isSuperAdmin || false)\n const [organizations, setOrganizations] = React.useState<string[] | null>(value?.organizations ?? null)\n const [orgOptions, setOrgOptions] = React.useState<{ id: string; name: string }[]>([])\n const [hasCustomAcl, setHasCustomAcl] = React.useState(true)\n const [overrideEnabled, setOverrideEnabled] = React.useState(false)\n const [roleDetails, setRoleDetails] = React.useState<RoleSummary[]>([])\n\n const actorSanitizeFeatures = React.useCallback(\n (list: unknown): string[] => {\n const normalized = normalizeFeatureArray(list)\n if (actorIsSuperAdmin) return normalized\n return normalized.filter((feature) => !isTenantRestrictedFeature(feature))\n },\n [actorIsSuperAdmin],\n )\n\n const updateGranted = React.useCallback(\n (updater: (prev: string[]) => string[]) => {\n setGranted((prev) => actorSanitizeFeatures(updater(prev)))\n },\n [actorSanitizeFeatures],\n )\n\n const tenantIdRef = React.useRef(tenantId)\n React.useEffect(() => { tenantIdRef.current = tenantId }, [tenantId])\n const hasMountedRef = React.useRef(false)\n\n const fetchAclState = React.useCallback(async (forTenantId: string | null | undefined, cancelledRef: { current: boolean }) => {\n try {\n const aclQuery = new URLSearchParams()\n aclQuery.set(kind === 'user' ? 'userId' : 'roleId', targetId)\n if (forTenantId) aclQuery.set('tenantId', forTenantId)\n const aclQueryString = aclQuery.toString()\n const aclJson = await readJsonOr<AclPayload>(\n `/api/auth/${kind === 'user' ? 'users' : 'roles'}/acl${aclQueryString ? `?${aclQueryString}` : ''}`,\n undefined,\n { hasCustomAcl: true, isSuperAdmin: false, features: [], organizations: null },\n )\n if (cancelledRef.current) return\n const customAclExists = aclJson.hasCustomAcl !== false\n setHasCustomAcl(customAclExists)\n setOverrideEnabled(customAclExists)\n setIsSuperAdmin(!!aclJson.isSuperAdmin)\n setGranted(actorSanitizeFeatures(aclJson.features))\n setOrganizations(aclJson.organizations == null ? null : Array.isArray(aclJson.organizations) ? aclJson.organizations : [])\n } catch {}\n }, [kind, targetId, actorSanitizeFeatures])\n\n React.useEffect(() => {\n const cancelled = { current: false }\n async function load() {\n setLoading(true)\n try {\n const fJson = await readJsonOr<FeatureListResponse>(\n '/api/auth/features',\n undefined,\n { items: [], modules: [] },\n )\n if (!cancelled.current) {\n setFeatures(fJson.items || [])\n setModules(fJson.modules || [])\n }\n } catch {}\n await fetchAclState(tenantIdRef.current, cancelled)\n hasMountedRef.current = true\n if (!cancelled.current) setLoading(false)\n }\n load()\n return () => { cancelled.current = true }\n }, [kind, targetId, fetchAclState])\n\n React.useEffect(() => {\n if (!hasMountedRef.current) return\n if (preserveOnTenantChange) return\n const cancelled = { current: false }\n fetchAclState(tenantId, cancelled)\n return () => { cancelled.current = true }\n }, [tenantId, preserveOnTenantChange, fetchAclState])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadTenantScoped() {\n if (canEditOrganizations) {\n try {\n const orgQuery = new URLSearchParams()\n if (tenantId) orgQuery.set('tenantId', tenantId)\n const orgQueryString = orgQuery.toString()\n const oJson = await readJsonOr<OrganizationListResponse>(\n `/api/directory/organizations${orgQueryString ? `?${orgQueryString}` : ''}`,\n undefined,\n { items: [] },\n )\n if (!cancelled) setOrgOptions(normalizeOrganizationOptions(oJson.items))\n } catch {}\n }\n if (kind === 'user' && userRoles && userRoles.length > 0) {\n try {\n const roleQuery = new URLSearchParams({ pageSize: '100' })\n if (tenantId) roleQuery.set('tenantId', tenantId)\n const roleQueryString = roleQuery.toString()\n const rolesJson = await readJsonOr<RoleListResponse>(\n `/api/auth/roles${roleQueryString ? `?${roleQueryString}` : ''}`,\n undefined,\n { items: [] },\n )\n if (!cancelled) {\n const allRoles = Array.isArray(rolesJson.items) ? rolesJson.items : []\n const userRoleDetails: RoleSummary[] = buildRoleSummaries(allRoles, userRoles)\n setRoleDetails(userRoleDetails)\n }\n } catch {}\n }\n }\n loadTenantScoped()\n return () => { cancelled = true }\n }, [kind, canEditOrganizations, userRoles, tenantId])\n\n // Notify parent of changes\n React.useEffect(() => {\n onChange?.({ isSuperAdmin, features: granted, organizations })\n }, [isSuperAdmin, granted, organizations, onChange])\n\n const grouped = React.useMemo(() => {\n const moduleMap = new Map<string, string>()\n for (const m of modules) {\n moduleMap.set(m.id, m.title)\n }\n const map = new Map<string, { moduleId: string; moduleTitle: string; features: Feature[] }>()\n for (const f of features) {\n const moduleId = f.module\n const moduleTitle = moduleMap.get(moduleId) || moduleId\n if (!map.has(moduleId)) {\n map.set(moduleId, { moduleId, moduleTitle, features: [] })\n }\n map.get(moduleId)!.features.push(f)\n }\n return Array.from(map.values()).sort((a, b) => a.moduleTitle.localeCompare(b.moduleTitle))\n }, [features, modules])\n\n const hasGlobalWildcard = granted.includes('*')\n const hasOrganizationRestriction = Array.isArray(organizations) && organizations.length > 0\n const showOrganizationWarning =\n (kind === 'role' || overrideEnabled) &&\n canEditOrganizations &&\n !isSuperAdmin &&\n hasOrganizationRestriction &&\n granted.length === 0\n\n \n const toggleWildcard = React.useCallback((wildcard: string, enable: boolean) => {\n if (!actorIsSuperAdmin && enable && isTenantRestrictedFeature(wildcard)) return\n updateGranted((prev) => {\n if (enable) {\n if (prev.includes(wildcard)) return prev\n return [...prev, wildcard]\n }\n return prev.filter((feature) => feature !== wildcard)\n })\n }, [actorIsSuperAdmin, updateGranted])\n\n const toggleModuleWildcard = React.useCallback((moduleId: string, enable: boolean) => {\n toggleWildcard(`${moduleId}.*`, enable)\n }, [toggleWildcard])\n\n const isModuleWildcardEnabled = (moduleId: string) => {\n return granted.includes(`${moduleId}.*`)\n }\n\n const isFeatureCoveredByWildcard = (featureId: string) =>\n granted.some((feature) => (feature === '*' || feature.endsWith('.*')) && matchFeature(featureId, feature))\n\n const isFeatureChecked = (featureId: string) => hasFeature(granted, featureId)\n\n if (loading) return <div className=\"text-sm text-muted-foreground\">Loading ACL\u2026</div>\n\n const showRoleBanner = kind === 'user' && !hasCustomAcl && !overrideEnabled\n\n return (\n <div className=\"space-y-4\">\n {showRoleBanner && (\n <div className=\"rounded-lg border border-blue-200 bg-blue-50 p-4\">\n <div className=\"text-sm font-medium text-blue-900 mb-2\">\n Permissions inherited from roles\n </div>\n <div className=\"text-sm text-blue-700 mb-3\">\n This user currently inherits permissions from their assigned roles.\n {roleDetails.length > 0 && (\n <span>\n {' '}Assigned roles:{' '}\n {roleDetails.map((role, idx) => {\n const roleId = typeof role?.id === 'string' && role.id.length > 0 ? role.id : `role-${idx}`\n const roleName = typeof role?.name === 'string' && role.name.length > 0 ? role.name : roleId\n return (\n <React.Fragment key={roleId}>\n {idx > 0 && ', '}\n <Link \n href={`/backend/roles/${roleId}/edit`}\n className=\"font-semibold text-blue-900 underline hover:text-blue-950 transition-colors\"\n >\n {roleName}\n </Link>\n </React.Fragment>\n )\n })}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <input \n id=\"overrideAcl\" \n type=\"checkbox\" \n className=\"h-4 w-4\" \n checked={overrideEnabled} \n onChange={(e) => setOverrideEnabled(e.target.checked)} \n />\n <label htmlFor=\"overrideAcl\" className=\"text-sm text-blue-900 font-medium\">\n Override permissions for this user only\n </label>\n </div>\n </div>\n )}\n {(kind === 'role' || overrideEnabled) && (\n <>\n <div className=\"flex items-center gap-2\">\n <input\n id=\"isSuperAdmin\"\n type=\"checkbox\"\n className=\"h-4 w-4\"\n checked={isSuperAdmin}\n disabled={!actorIsSuperAdmin}\n onChange={(e) => setIsSuperAdmin(!!e.target.checked)}\n />\n <label htmlFor=\"isSuperAdmin\" className=\"text-sm\">Super Admin (all features)</label>\n </div>\n {!actorIsSuperAdmin && (\n <p className=\"text-xs text-muted-foreground\">Only super administrators can change this option.</p>\n )}\n {!isSuperAdmin && (\n <>\n {hasGlobalWildcard && (\n <div className=\"rounded border border-blue-200 bg-blue-50 p-3\">\n <div className=\"text-sm font-medium text-blue-900\">Global wildcard (*) enabled</div>\n <div className=\"text-xs text-blue-700 mt-1\">This grants access to all features in the system.</div>\n <Button \n variant=\"outline\" \n size=\"sm\" \n className=\"mt-2\"\n onClick={() => updateGranted((prev) => prev.filter((x) => x !== '*'))}\n >\n Remove global wildcard\n </Button>\n </div>\n )}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {grouped.map((group) => {\n const moduleWildcard = isModuleWildcardEnabled(group.moduleId)\n const nestedWildcards = Array.from(\n new Set(\n granted.filter(\n (feature) =>\n feature !== '*' &&\n feature.endsWith('.*') &&\n feature.startsWith(`${group.moduleId}.`) &&\n feature !== `${group.moduleId}.*`,\n ),\n ),\n )\n .map((wildcard) => {\n const prefix = wildcard.slice(0, -1)\n const relatedFeatures = group.features.filter((feature) => feature.id.startsWith(prefix))\n return { wildcard, features: relatedFeatures }\n })\n .sort((a, b) => a.wildcard.localeCompare(b.wildcard))\n const nestedCoveredIds = new Set<string>()\n for (const entry of nestedWildcards) {\n for (const feature of entry.features) nestedCoveredIds.add(feature.id)\n }\n const moduleRestricted = !actorIsSuperAdmin && isTenantRestrictedFeature(`${group.moduleId}.*`)\n const moduleCheckboxDisabled = hasGlobalWildcard || moduleRestricted\n return (\n <div key={group.moduleId} className=\"rounded border p-3\">\n <div className=\"flex items-center justify-between mb-3 pb-2 border-b\">\n <div className=\"text-sm font-medium\">{group.moduleTitle}</div>\n <div className=\"flex items-center gap-2\">\n <input \n id={`module-${group.moduleId}`} \n type=\"checkbox\" \n className=\"h-4 w-4\" \n checked={moduleWildcard || hasGlobalWildcard} \n disabled={moduleCheckboxDisabled}\n onChange={(e) => toggleModuleWildcard(group.moduleId, e.target.checked)} \n />\n <label htmlFor={`module-${group.moduleId}`} className=\"text-sm text-muted-foreground\">\n All {moduleWildcard && !hasGlobalWildcard ? <span className=\"font-medium text-blue-600\">({group.moduleId}.*)</span> : ''}\n {moduleRestricted ? <span className=\"ml-2 text-xs font-medium text-muted-foreground\">(manage via super admin)</span> : null}\n </label>\n </div>\n </div>\n {nestedWildcards.length > 0 && (\n <div className=\"space-y-3 mb-3\">\n {nestedWildcards.map(({ wildcard, features: wildcardFeatures }) => {\n const checked = granted.includes(wildcard) || hasGlobalWildcard || moduleWildcard\n const wildcardRestricted = !actorIsSuperAdmin && isTenantRestrictedFeature(wildcard)\n const disabled = hasGlobalWildcard || moduleWildcard || wildcardRestricted\n return (\n <div key={wildcard} className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <input\n id={`wildcard-${wildcard}`}\n type=\"checkbox\"\n className=\"h-4 w-4\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => toggleWildcard(wildcard, !!e.target.checked)}\n />\n <label\n htmlFor={`wildcard-${wildcard}`}\n className={`text-sm ${disabled ? 'text-muted-foreground' : ''}`}\n >\n {formatWildcardLabel(group.moduleId, wildcard)}{' '}\n <span className=\"text-muted-foreground text-xs font-mono\">({wildcard})</span>\n {wildcardRestricted ? (\n <span className=\"ml-2 text-xs font-medium text-muted-foreground\">\n {t('auth.acl.restricted', 'Restricted')}\n </span>\n ) : null}\n </label>\n </div>\n {wildcardFeatures.length > 0 && (\n <div className=\"relative ml-6 pl-4 text-sm text-muted-foreground space-y-1\">\n <div className=\"absolute left-0 top-1 bottom-1 w-px bg-border\" aria-hidden />\n {wildcardFeatures.map((wf) => (\n <div key={`${wildcard}-${wf.id}`} className=\"pl-2\">\n <span>\n {wf.title}{' '}\n <span className=\"text-xs font-mono text-muted-foreground\">({wf.id})</span>\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n <div className=\"space-y-2\">\n {group.features.map((f) => {\n if (nestedCoveredIds.has(f.id)) return null\n const checked = isFeatureChecked(f.id)\n const isWildcardCovered = isFeatureCoveredByWildcard(f.id)\n const restricted = !actorIsSuperAdmin && isTenantRestrictedFeature(f.id)\n const disabled = isWildcardCovered || restricted\n return (\n <div key={f.id} className=\"flex items-center gap-2\">\n <input\n id={`f-${f.id}`}\n type=\"checkbox\"\n className=\"h-4 w-4\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => {\n const on = !!e.target.checked\n updateGranted((prev) => {\n if (on) return [...prev, f.id]\n return prev.filter((x) => x !== f.id)\n })\n }}\n />\n <label\n htmlFor={`f-${f.id}`}\n className={`text-sm ${disabled ? 'text-muted-foreground' : ''}`}\n >\n {f.title} <span className=\"text-muted-foreground text-xs\">({f.id})</span>\n {restricted ? (\n <span className=\"ml-2 text-xs font-medium text-muted-foreground\">\n {t('auth.acl.restricted', 'Restricted')}\n </span>\n ) : null}\n </label>\n </div>\n )\n })}\n </div>\n </div>\n )\n })}\n </div>\n </>\n )}\n {canEditOrganizations && (\n <div className=\"rounded border p-3\">\n <div className=\"text-sm font-medium mb-2\">\n {t('auth.acl.organizationsScope', 'Organizations scope')}\n </div>\n <div className=\"text-xs text-muted-foreground mb-2\">Empty = all organizations. Select one or more to restrict.</div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2\">\n {orgOptions.map((o) => {\n const checked = organizations == null ? false : (organizations || []).includes(o.id)\n return (\n <div key={o.id} className=\"flex items-center gap-2\">\n <input id={`org-${o.id}`} type=\"checkbox\" className=\"h-4 w-4\" checked={checked} onChange={(e) => {\n const on = !!e.target.checked\n setOrganizations((prev) => {\n if (prev == null) return on ? [o.id] : []\n return on ? Array.from(new Set([...(prev || []), o.id])) : (prev || []).filter((x) => x !== o.id)\n })\n }} />\n <label htmlFor={`org-${o.id}`} className=\"text-sm\">{o.name}</label>\n </div>\n )\n })}\n </div>\n <div className=\"mt-2\">\n <Button variant=\"outline\" onClick={() => setOrganizations(null)}>{t('auth.acl.allowAllOrganizations', 'Allow all organizations')}</Button>\n </div>\n {showOrganizationWarning && (\n <div className=\"mt-3 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n {t('auth.acl.organizationWarning', 'Organization restrictions are saved only when at least one feature override is selected. Add a feature or enable a module wildcard before saving.')}\n </div>\n )}\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -38,7 +38,7 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
38
38
|
(widget) => resolveWidgetText(t, widget.id, "description", widget.description || ""),
|
|
39
39
|
[t]
|
|
40
40
|
);
|
|
41
|
-
const { kind, targetId, tenantId, organizationId } = props;
|
|
41
|
+
const { kind, targetId, tenantId, organizationId, preserveOnTenantChange = false } = props;
|
|
42
42
|
const [catalog, setCatalog] = React.useState([]);
|
|
43
43
|
const [loading, setLoading] = React.useState(true);
|
|
44
44
|
const [saving, setSaving] = React.useState(false);
|
|
@@ -74,10 +74,19 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
74
74
|
}).filter((item) => item !== null);
|
|
75
75
|
setCatalog(mapped);
|
|
76
76
|
}, []);
|
|
77
|
-
const
|
|
77
|
+
const tenantIdRef = React.useRef(tenantId);
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
tenantIdRef.current = tenantId;
|
|
80
|
+
}, [tenantId]);
|
|
81
|
+
const organizationIdRef = React.useRef(organizationId);
|
|
82
|
+
React.useEffect(() => {
|
|
83
|
+
organizationIdRef.current = organizationId;
|
|
84
|
+
}, [organizationId]);
|
|
85
|
+
const hasMountedRef = React.useRef(false);
|
|
86
|
+
const loadRoleData = React.useCallback(async (forTenantId, forOrganizationId) => {
|
|
78
87
|
const params = new URLSearchParams({ roleId: targetId });
|
|
79
|
-
if (
|
|
80
|
-
if (
|
|
88
|
+
if (forTenantId) params.set("tenantId", forTenantId);
|
|
89
|
+
if (forOrganizationId) params.set("organizationId", forOrganizationId);
|
|
81
90
|
const data = await readApiResultOrThrow(
|
|
82
91
|
`/api/dashboards/roles/widgets?${params.toString()}`,
|
|
83
92
|
void 0,
|
|
@@ -89,11 +98,11 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
89
98
|
setMode("override");
|
|
90
99
|
setOriginalMode("override");
|
|
91
100
|
setEffective(ids);
|
|
92
|
-
}, [
|
|
93
|
-
const loadUserData = React.useCallback(async () => {
|
|
101
|
+
}, [targetId]);
|
|
102
|
+
const loadUserData = React.useCallback(async (forTenantId, forOrganizationId) => {
|
|
94
103
|
const params = new URLSearchParams({ userId: targetId });
|
|
95
|
-
if (
|
|
96
|
-
if (
|
|
104
|
+
if (forTenantId) params.set("tenantId", forTenantId);
|
|
105
|
+
if (forOrganizationId) params.set("organizationId", forOrganizationId);
|
|
97
106
|
const data = await readApiResultOrThrow(
|
|
98
107
|
`/api/dashboards/users/widgets?${params.toString()}`,
|
|
99
108
|
void 0,
|
|
@@ -105,7 +114,7 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
105
114
|
setMode(data.mode || "inherit");
|
|
106
115
|
setOriginalMode(data.mode || "inherit");
|
|
107
116
|
setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : []);
|
|
108
|
-
}, [
|
|
117
|
+
}, [targetId]);
|
|
109
118
|
React.useEffect(() => {
|
|
110
119
|
let cancelled = false;
|
|
111
120
|
async function load() {
|
|
@@ -113,15 +122,18 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
113
122
|
setError(null);
|
|
114
123
|
try {
|
|
115
124
|
await loadCatalog();
|
|
116
|
-
if (kind === "role") await loadRoleData();
|
|
117
|
-
else await loadUserData();
|
|
125
|
+
if (kind === "role") await loadRoleData(tenantIdRef.current, organizationIdRef.current);
|
|
126
|
+
else await loadUserData(tenantIdRef.current, organizationIdRef.current);
|
|
118
127
|
} catch (err) {
|
|
119
128
|
console.error("Failed to load widget visibility data", err);
|
|
120
129
|
if (!cancelled) {
|
|
121
130
|
setError(tRef.current("dashboards.widgets.error.load", "Unable to load widget configuration."));
|
|
122
131
|
}
|
|
123
132
|
} finally {
|
|
124
|
-
if (!cancelled)
|
|
133
|
+
if (!cancelled) {
|
|
134
|
+
setLoading(false);
|
|
135
|
+
hasMountedRef.current = true;
|
|
136
|
+
}
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
load();
|
|
@@ -129,6 +141,23 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
129
141
|
cancelled = true;
|
|
130
142
|
};
|
|
131
143
|
}, [kind, loadCatalog, loadRoleData, loadUserData]);
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
if (!hasMountedRef.current) return;
|
|
146
|
+
if (preserveOnTenantChange) return;
|
|
147
|
+
let cancelled = false;
|
|
148
|
+
async function refetch() {
|
|
149
|
+
try {
|
|
150
|
+
if (kind === "role") await loadRoleData(tenantId, organizationId);
|
|
151
|
+
else await loadUserData(tenantId, organizationId);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
console.error("Failed to reload widget visibility data", err);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
refetch();
|
|
157
|
+
return () => {
|
|
158
|
+
cancelled = true;
|
|
159
|
+
};
|
|
160
|
+
}, [tenantId, organizationId, kind, loadRoleData, loadUserData, preserveOnTenantChange]);
|
|
132
161
|
const toggle = React.useCallback((id) => {
|
|
133
162
|
setSelected((prev) => prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]);
|
|
134
163
|
}, []);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/dashboards/components/WidgetVisibilityEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { RadioGroup } from '@open-mercato/ui/primitives/radio'\nimport { RadioField } from '@open-mercato/ui/primitives/radio-field'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WidgetCatalogItem = {\n id: string\n title: string\n description: string | null\n}\n\ntype RoleResponse = {\n widgetIds: string[]\n hasCustom: boolean\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype UserResponse = {\n mode: 'inherit' | 'override'\n widgetIds: string[]\n hasCustom: boolean\n effectiveWidgetIds: string[]\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype BaseProps = {\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype RoleProps = BaseProps & {\n kind: 'role'\n targetId: string\n}\n\ntype UserProps = BaseProps & {\n kind: 'user'\n targetId: string\n}\n\ntype WidgetVisibilityEditorProps = RoleProps | UserProps\n\nexport type WidgetVisibilityEditorHandle = {\n save: () => Promise<void>\n}\n\nconst EMPTY: string[] = []\n\nfunction resolveWidgetText(\n t: (key: string, fallback: string) => string,\n id: string,\n field: 'title' | 'description',\n fallback: string,\n): string {\n const key1 = `${id}.${field}`\n const result1 = t(key1, '')\n if (result1 && result1 !== key1) return result1\n\n const key2 = `dashboard.widgets.${id}.${field}`\n const result2 = t(key2, '')\n if (result2 && result2 !== key2) return result2\n\n const dotIndex = id.lastIndexOf('.')\n if (dotIndex > 0) {\n const prefix = id.slice(0, dotIndex)\n const lastPart = id.slice(dotIndex + 1)\n const key3 = `${prefix}.widgets.${lastPart}.${field}`\n const result3 = t(key3, '')\n if (result3 && result3 !== key3) return result3\n }\n\n return fallback\n}\n\nexport const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {\n const t = useT()\n const tRef = React.useRef(t)\n tRef.current = t\n\n const resolveTitle = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'title', widget.title),\n [t],\n )\n\n const resolveDescription = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'description', widget.description || ''),\n [t],\n )\n const { kind, targetId, tenantId, organizationId } = props\n const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const [selected, setSelected] = React.useState<string[]>(EMPTY)\n const [original, setOriginal] = React.useState<string[]>(EMPTY)\n const [mode, setMode] = React.useState<'inherit' | 'override'>('inherit')\n const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')\n const [effective, setEffective] = React.useState<string[]>(EMPTY)\n\n const dirty = React.useMemo(() => {\n if (kind === 'user') {\n if (mode !== originalMode) return true\n if (mode === 'override') return selected.join('|') !== original.join('|')\n return false\n }\n return selected.join('|') !== original.join('|')\n }, [kind, mode, original, originalMode, selected])\n\n const loadCatalog = React.useCallback(async () => {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/dashboards/widgets/catalog',\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n const mapped = items\n .map((item: unknown): WidgetCatalogItem | null => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const id = typeof entry.id === 'string' ? entry.id : null\n if (!id || !id.length) return null\n const title =\n typeof entry.title === 'string' && entry.title.length ? entry.title : id\n const description =\n typeof entry.description === 'string' && entry.description.length ? entry.description : null\n return { id, title, description }\n })\n .filter((item: WidgetCatalogItem | null): item is WidgetCatalogItem => item !== null)\n setCatalog(mapped)\n }, [])\n\n const loadRoleData = React.useCallback(async () => {\n const params = new URLSearchParams({ roleId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<RoleResponse>(\n `/api/dashboards/roles/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode('override')\n setOriginalMode('override')\n setEffective(ids)\n }, [organizationId, targetId, tenantId])\n\n const loadUserData = React.useCallback(async () => {\n const params = new URLSearchParams({ userId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode(data.mode || 'inherit')\n setOriginalMode(data.mode || 'inherit')\n setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])\n }, [organizationId, targetId, tenantId])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n await loadCatalog()\n if (kind === 'role') await loadRoleData()\n else await loadUserData()\n } catch (err) {\n console.error('Failed to load widget visibility data', err)\n if (!cancelled) {\n setError(tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [kind, loadCatalog, loadRoleData, loadUserData])\n\n const toggle = React.useCallback((id: string) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const resetSelections = React.useCallback(() => {\n setSelected(original)\n setMode(originalMode)\n }, [original, originalMode])\n\n const save = React.useCallback(async () => {\n if (loading) return\n if (error && catalog.length === 0) return\n if (!dirty) return\n setSaving(true)\n setError(null)\n try {\n const saveError = t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.')\n if (kind === 'role') {\n const payload = {\n roleId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/roles/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n setOriginalMode('override')\n setEffective(selected)\n } else {\n const payload = {\n userId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n mode,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/users/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n if (mode === 'inherit') {\n const refreshed = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?userId=${encodeURIComponent(targetId)}`,\n undefined,\n { errorMessage: saveError },\n )\n setEffective(Array.isArray(refreshed.effectiveWidgetIds) ? refreshed.effectiveWidgetIds : [])\n } else {\n setEffective(selected)\n }\n setOriginal(selected)\n setOriginalMode(mode)\n }\n try { flash(t('dashboards.widgets.flash.saved', 'Dashboard widgets updated'), 'success') } catch {}\n } catch (err) {\n console.error('Failed to save widget visibility', err)\n setError(t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.'))\n } finally {\n setSaving(false)\n }\n }, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])\n\n React.useImperativeHandle(ref, () => ({ save }), [save])\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('dashboards.widgets.loading', 'Loading widget options\u2026')}\n </div>\n )\n }\n\n if (error && catalog.length === 0) {\n return <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n\n {kind === 'user' && (\n <RadioGroup\n className=\"flex flex-row items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\"\n name=\"widgetOverride\"\n value={mode}\n onValueChange={(next) => setMode(next as 'inherit' | 'override')}\n >\n <RadioField\n value=\"inherit\"\n label={t('dashboards.widgets.mode.inherit', 'Inherit from roles')}\n />\n <RadioField\n value=\"override\"\n label={t('dashboards.widgets.mode.override', 'Override for this user')}\n />\n </RadioGroup>\n )}\n\n {kind === 'user' && mode === 'inherit' && (\n <div className=\"rounded-md border border-muted bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.mode.hint', 'This user currently inherits widgets from their assigned roles. Switch to override to customize.')}\n </div>\n )}\n\n {(kind === 'role' || mode === 'override') && (\n <div className=\"space-y-3\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => toggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{resolveTitle(widget)}</div>\n {widget.description ? <div className=\"mt-1 text-xs text-muted-foreground\">{resolveDescription(widget)}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n\n {kind === 'user' && effective.length > 0 && (\n <div className=\"rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.effective', 'Effective widgets:')} {effective.map((id) => { const meta = catalog.find((m) => m.id === id); return meta ? resolveTitle(meta) : id }).join(', ')}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={save} disabled={saving || !dirty}>\n {saving ? t('dashboards.widgets.saving', 'Saving\u2026') : t('dashboards.widgets.save', 'Save widgets')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={resetSelections} disabled={!dirty}>\n {t('dashboards.widgets.reset', 'Reset')}\n </Button>\n </div>\n </div>\n )\n})\n\nWidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { RadioGroup } from '@open-mercato/ui/primitives/radio'\nimport { RadioField } from '@open-mercato/ui/primitives/radio-field'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WidgetCatalogItem = {\n id: string\n title: string\n description: string | null\n}\n\ntype RoleResponse = {\n widgetIds: string[]\n hasCustom: boolean\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype UserResponse = {\n mode: 'inherit' | 'override'\n widgetIds: string[]\n hasCustom: boolean\n effectiveWidgetIds: string[]\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype BaseProps = {\n tenantId?: string | null\n organizationId?: string | null\n preserveOnTenantChange?: boolean\n}\n\ntype RoleProps = BaseProps & {\n kind: 'role'\n targetId: string\n}\n\ntype UserProps = BaseProps & {\n kind: 'user'\n targetId: string\n}\n\ntype WidgetVisibilityEditorProps = RoleProps | UserProps\n\nexport type WidgetVisibilityEditorHandle = {\n save: () => Promise<void>\n}\n\nconst EMPTY: string[] = []\n\nfunction resolveWidgetText(\n t: (key: string, fallback: string) => string,\n id: string,\n field: 'title' | 'description',\n fallback: string,\n): string {\n const key1 = `${id}.${field}`\n const result1 = t(key1, '')\n if (result1 && result1 !== key1) return result1\n\n const key2 = `dashboard.widgets.${id}.${field}`\n const result2 = t(key2, '')\n if (result2 && result2 !== key2) return result2\n\n const dotIndex = id.lastIndexOf('.')\n if (dotIndex > 0) {\n const prefix = id.slice(0, dotIndex)\n const lastPart = id.slice(dotIndex + 1)\n const key3 = `${prefix}.widgets.${lastPart}.${field}`\n const result3 = t(key3, '')\n if (result3 && result3 !== key3) return result3\n }\n\n return fallback\n}\n\nexport const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {\n const t = useT()\n const tRef = React.useRef(t)\n tRef.current = t\n\n const resolveTitle = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'title', widget.title),\n [t],\n )\n\n const resolveDescription = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'description', widget.description || ''),\n [t],\n )\n const { kind, targetId, tenantId, organizationId, preserveOnTenantChange = false } = props\n const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const [selected, setSelected] = React.useState<string[]>(EMPTY)\n const [original, setOriginal] = React.useState<string[]>(EMPTY)\n const [mode, setMode] = React.useState<'inherit' | 'override'>('inherit')\n const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')\n const [effective, setEffective] = React.useState<string[]>(EMPTY)\n\n const dirty = React.useMemo(() => {\n if (kind === 'user') {\n if (mode !== originalMode) return true\n if (mode === 'override') return selected.join('|') !== original.join('|')\n return false\n }\n return selected.join('|') !== original.join('|')\n }, [kind, mode, original, originalMode, selected])\n\n const loadCatalog = React.useCallback(async () => {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/dashboards/widgets/catalog',\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n const mapped = items\n .map((item: unknown): WidgetCatalogItem | null => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const id = typeof entry.id === 'string' ? entry.id : null\n if (!id || !id.length) return null\n const title =\n typeof entry.title === 'string' && entry.title.length ? entry.title : id\n const description =\n typeof entry.description === 'string' && entry.description.length ? entry.description : null\n return { id, title, description }\n })\n .filter((item: WidgetCatalogItem | null): item is WidgetCatalogItem => item !== null)\n setCatalog(mapped)\n }, [])\n\n const tenantIdRef = React.useRef(tenantId)\n React.useEffect(() => { tenantIdRef.current = tenantId }, [tenantId])\n const organizationIdRef = React.useRef(organizationId)\n React.useEffect(() => { organizationIdRef.current = organizationId }, [organizationId])\n const hasMountedRef = React.useRef(false)\n\n const loadRoleData = React.useCallback(async (forTenantId: string | null | undefined, forOrganizationId: string | null | undefined) => {\n const params = new URLSearchParams({ roleId: targetId })\n if (forTenantId) params.set('tenantId', forTenantId)\n if (forOrganizationId) params.set('organizationId', forOrganizationId)\n const data = await readApiResultOrThrow<RoleResponse>(\n `/api/dashboards/roles/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode('override')\n setOriginalMode('override')\n setEffective(ids)\n }, [targetId])\n\n const loadUserData = React.useCallback(async (forTenantId: string | null | undefined, forOrganizationId: string | null | undefined) => {\n const params = new URLSearchParams({ userId: targetId })\n if (forTenantId) params.set('tenantId', forTenantId)\n if (forOrganizationId) params.set('organizationId', forOrganizationId)\n const data = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode(data.mode || 'inherit')\n setOriginalMode(data.mode || 'inherit')\n setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])\n }, [targetId])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n await loadCatalog()\n if (kind === 'role') await loadRoleData(tenantIdRef.current, organizationIdRef.current)\n else await loadUserData(tenantIdRef.current, organizationIdRef.current)\n } catch (err) {\n console.error('Failed to load widget visibility data', err)\n if (!cancelled) {\n setError(tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.'))\n }\n } finally {\n if (!cancelled) {\n setLoading(false)\n hasMountedRef.current = true\n }\n }\n }\n load()\n return () => { cancelled = true }\n }, [kind, loadCatalog, loadRoleData, loadUserData])\n\n React.useEffect(() => {\n if (!hasMountedRef.current) return\n if (preserveOnTenantChange) return\n let cancelled = false\n async function refetch() {\n try {\n if (kind === 'role') await loadRoleData(tenantId, organizationId)\n else await loadUserData(tenantId, organizationId)\n } catch (err) {\n console.error('Failed to reload widget visibility data', err)\n }\n }\n refetch()\n return () => { cancelled = true }\n }, [tenantId, organizationId, kind, loadRoleData, loadUserData, preserveOnTenantChange])\n\n const toggle = React.useCallback((id: string) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const resetSelections = React.useCallback(() => {\n setSelected(original)\n setMode(originalMode)\n }, [original, originalMode])\n\n const save = React.useCallback(async () => {\n if (loading) return\n if (error && catalog.length === 0) return\n if (!dirty) return\n setSaving(true)\n setError(null)\n try {\n const saveError = t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.')\n if (kind === 'role') {\n const payload = {\n roleId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/roles/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n setOriginalMode('override')\n setEffective(selected)\n } else {\n const payload = {\n userId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n mode,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/users/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n if (mode === 'inherit') {\n const refreshed = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?userId=${encodeURIComponent(targetId)}`,\n undefined,\n { errorMessage: saveError },\n )\n setEffective(Array.isArray(refreshed.effectiveWidgetIds) ? refreshed.effectiveWidgetIds : [])\n } else {\n setEffective(selected)\n }\n setOriginal(selected)\n setOriginalMode(mode)\n }\n try { flash(t('dashboards.widgets.flash.saved', 'Dashboard widgets updated'), 'success') } catch {}\n } catch (err) {\n console.error('Failed to save widget visibility', err)\n setError(t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.'))\n } finally {\n setSaving(false)\n }\n }, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])\n\n React.useImperativeHandle(ref, () => ({ save }), [save])\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('dashboards.widgets.loading', 'Loading widget options\u2026')}\n </div>\n )\n }\n\n if (error && catalog.length === 0) {\n return <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n\n {kind === 'user' && (\n <RadioGroup\n className=\"flex flex-row items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\"\n name=\"widgetOverride\"\n value={mode}\n onValueChange={(next) => setMode(next as 'inherit' | 'override')}\n >\n <RadioField\n value=\"inherit\"\n label={t('dashboards.widgets.mode.inherit', 'Inherit from roles')}\n />\n <RadioField\n value=\"override\"\n label={t('dashboards.widgets.mode.override', 'Override for this user')}\n />\n </RadioGroup>\n )}\n\n {kind === 'user' && mode === 'inherit' && (\n <div className=\"rounded-md border border-muted bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.mode.hint', 'This user currently inherits widgets from their assigned roles. Switch to override to customize.')}\n </div>\n )}\n\n {(kind === 'role' || mode === 'override') && (\n <div className=\"space-y-3\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => toggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{resolveTitle(widget)}</div>\n {widget.description ? <div className=\"mt-1 text-xs text-muted-foreground\">{resolveDescription(widget)}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n\n {kind === 'user' && effective.length > 0 && (\n <div className=\"rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.effective', 'Effective widgets:')} {effective.map((id) => { const meta = catalog.find((m) => m.id === id); return meta ? resolveTitle(meta) : id }).join(', ')}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={save} disabled={saving || !dirty}>\n {saving ? t('dashboards.widgets.saving', 'Saving\u2026') : t('dashboards.widgets.save', 'Save widgets')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={resetSelections} disabled={!dirty}>\n {t('dashboards.widgets.reset', 'Reset')}\n </Button>\n </div>\n </div>\n )\n})\n\nWidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["WidgetVisibilityEditor"]
|
|
7
7
|
}
|