@open-mercato/core 0.6.4-develop.3996.1.430e257cfc → 0.6.4-develop.4000.1.450e315cec

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.
@@ -1,4 +1,4 @@
1
- [build:core] found 2636 entry points
1
+ [build:core] found 2637 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 172 entry points
4
4
  [build:core:generated] built successfully
@@ -5,12 +5,33 @@ import { getModules } from "@open-mercato/shared/lib/i18n/server";
5
5
  const metadata = {
6
6
  GET: { requireAuth: true, requireFeatures: ["auth.acl.manage"] }
7
7
  };
8
+ function normalizeDependsOn(value) {
9
+ if (!Array.isArray(value)) return void 0;
10
+ const out = [];
11
+ for (const entry of value) {
12
+ if (typeof entry !== "string") continue;
13
+ const trimmed = entry.trim();
14
+ if (!trimmed) continue;
15
+ out.push(trimmed);
16
+ }
17
+ if (out.length === 0) return void 0;
18
+ return Array.from(new Set(out));
19
+ }
8
20
  async function GET(req) {
9
21
  const auth = await getAuthFromRequest(req);
10
22
  if (!auth) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
11
23
  const modules = getModules();
12
24
  const items = (modules || []).flatMap(
13
- (m) => (m.features || []).map((f) => ({ id: String(f.id), title: String(f.title || f.id), module: String(f.module || m.id) }))
25
+ (m) => (m.features || []).map((f) => {
26
+ const deps = normalizeDependsOn(f?.dependsOn);
27
+ const base = {
28
+ id: String(f.id),
29
+ title: String(f.title || f.id),
30
+ module: String(f.module || m.id)
31
+ };
32
+ if (deps) base.dependsOn = deps;
33
+ return base;
34
+ })
14
35
  );
15
36
  const byId = /* @__PURE__ */ new Map();
16
37
  for (const it of items) if (!byId.has(it.id)) byId.set(it.id, it);
@@ -26,7 +47,8 @@ async function GET(req) {
26
47
  const featureItemSchema = z.object({
27
48
  id: z.string(),
28
49
  title: z.string(),
29
- module: z.string()
50
+ module: z.string(),
51
+ dependsOn: z.array(z.string()).optional()
30
52
  });
31
53
  const featureModuleSchema = z.object({
32
54
  id: z.string(),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/api/features.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.acl.manage'] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const modules = getModules()\n const items = (modules || []).flatMap((m: any) =>\n (m.features || []).map((f: any) => ({ id: String(f.id), title: String(f.title || f.id), module: String(f.module || m.id) }))\n )\n // Deduplicate by id\n const byId = new Map<string, { id: string; title: string; module: string }>()\n for (const it of items) if (!byId.has(it.id)) byId.set(it.id, it)\n const list = Array.from(byId.values()).sort((a, b) => a.module.localeCompare(b.module) || a.id.localeCompare(b.id))\n\n // Build module info map\n const moduleInfo = new Map<string, { id: string; title: string }>()\n for (const m of modules) {\n if (m.id) {\n moduleInfo.set(m.id, { id: m.id, title: (m.info as any)?.title || m.id })\n }\n }\n\n return NextResponse.json({ items: list, modules: Array.from(moduleInfo.values()) })\n}\n\nconst featureItemSchema = z.object({\n id: z.string(),\n title: z.string(),\n module: z.string(),\n})\n\nconst featureModuleSchema = z.object({\n id: z.string(),\n title: z.string(),\n})\n\nconst featuresResponseSchema = z.object({\n items: z.array(featureItemSchema),\n modules: z.array(featureModuleSchema),\n})\n\nconst featuresMethodDoc: OpenApiMethodDoc = {\n summary: 'List declared feature flags',\n description: 'Returns all static features contributed by the enabled modules along with their module source.',\n tags: ['Authentication & Accounts'],\n responses: [\n {\n status: 200,\n description: 'Aggregated feature catalog',\n schema: featuresResponseSchema,\n },\n ],\n errors: [\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'List declared feature flags',\n methods: {\n GET: featuresMethodDoc,\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAEpB,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,iBAAiB,EAAE;AACjE;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAC9E,QAAM,UAAU,WAAW;AAC3B,QAAM,SAAS,WAAW,CAAC,GAAG;AAAA,IAAQ,CAAC,OACpC,EAAE,YAAY,CAAC,GAAG,IAAI,CAAC,OAAY,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,EAAE,GAAG,QAAQ,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE;AAAA,EAC7H;AAEA,QAAM,OAAO,oBAAI,IAA2D;AAC5E,aAAW,MAAM,MAAO,KAAI,CAAC,KAAK,IAAI,GAAG,EAAE,EAAG,MAAK,IAAI,GAAG,IAAI,EAAE;AAChE,QAAM,OAAO,MAAM,KAAK,KAAK,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,KAAK,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAGlH,QAAM,aAAa,oBAAI,IAA2C;AAClE,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,IAAI;AACR,iBAAW,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,OAAQ,EAAE,MAAc,SAAS,EAAE,GAAG,CAAC;AAAA,IAC1E;AAAA,EACF;AAEA,SAAO,aAAa,KAAK,EAAE,OAAO,MAAM,SAAS,MAAM,KAAK,WAAW,OAAO,CAAC,EAAE,CAAC;AACpF;AAEA,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,IAAI,EAAE,OAAO;AAAA,EACb,OAAO,EAAE,OAAO;AAAA,EAChB,QAAQ,EAAE,OAAO;AACnB,CAAC;AAED,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,IAAI,EAAE,OAAO;AAAA,EACb,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,OAAO,EAAE,MAAM,iBAAiB;AAAA,EAChC,SAAS,EAAE,MAAM,mBAAmB;AACtC,CAAC;AAED,MAAM,oBAAsC;AAAA,EAC1C,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,2BAA2B;AAAA,EAClC,WAAW;AAAA,IACT;AAAA,MACE,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,MACE,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAAA,IACxC;AAAA,EACF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.acl.manage'] },\n}\n\ntype FeatureItem = {\n id: string\n title: string\n module: string\n dependsOn?: string[]\n}\n\nfunction normalizeDependsOn(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined\n const out: string[] = []\n for (const entry of value) {\n if (typeof entry !== 'string') continue\n const trimmed = entry.trim()\n if (!trimmed) continue\n out.push(trimmed)\n }\n if (out.length === 0) return undefined\n return Array.from(new Set(out))\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const modules = getModules()\n const items: FeatureItem[] = (modules || []).flatMap((m: any) =>\n (m.features || []).map((f: any) => {\n const deps = normalizeDependsOn(f?.dependsOn)\n const base: FeatureItem = {\n id: String(f.id),\n title: String(f.title || f.id),\n module: String(f.module || m.id),\n }\n if (deps) base.dependsOn = deps\n return base\n })\n )\n // Deduplicate by id (keep first occurrence)\n const byId = new Map<string, FeatureItem>()\n for (const it of items) if (!byId.has(it.id)) byId.set(it.id, it)\n const list = Array.from(byId.values()).sort((a, b) => a.module.localeCompare(b.module) || a.id.localeCompare(b.id))\n\n // Build module info map\n const moduleInfo = new Map<string, { id: string; title: string }>()\n for (const m of modules) {\n if (m.id) {\n moduleInfo.set(m.id, { id: m.id, title: (m.info as any)?.title || m.id })\n }\n }\n\n return NextResponse.json({ items: list, modules: Array.from(moduleInfo.values()) })\n}\n\nconst featureItemSchema = z.object({\n id: z.string(),\n title: z.string(),\n module: z.string(),\n dependsOn: z.array(z.string()).optional(),\n})\n\nconst featureModuleSchema = z.object({\n id: z.string(),\n title: z.string(),\n})\n\nconst featuresResponseSchema = z.object({\n items: z.array(featureItemSchema),\n modules: z.array(featureModuleSchema),\n})\n\nconst featuresMethodDoc: OpenApiMethodDoc = {\n summary: 'List declared feature flags',\n description: 'Returns all static features contributed by the enabled modules along with their module source.',\n tags: ['Authentication & Accounts'],\n responses: [\n {\n status: 200,\n description: 'Aggregated feature catalog',\n schema: featuresResponseSchema,\n },\n ],\n errors: [\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'List declared feature flags',\n methods: {\n GET: featuresMethodDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,135 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button } from "@open-mercato/ui/primitives/button";
5
+ import { Alert } from "@open-mercato/ui/primitives/alert";
6
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
7
+ import {
8
+ applyAddMissingDependency,
9
+ applyRemoveDependents,
10
+ applyRestoreDependency,
11
+ resolveAclDependencyDiagnostics
12
+ } from "@open-mercato/shared/security/aclDependencies";
13
+ function AclDependencyDiagnosticsPanel({
14
+ granted,
15
+ catalog,
16
+ onGrantedChange,
17
+ hideUnknownReferences
18
+ }) {
19
+ const t = useT();
20
+ const diagnostics = React.useMemo(
21
+ () => resolveAclDependencyDiagnostics(granted, catalog),
22
+ [granted, catalog]
23
+ );
24
+ const titleById = React.useMemo(() => {
25
+ const map = /* @__PURE__ */ new Map();
26
+ for (const entry of catalog) {
27
+ if (entry?.title && !map.has(entry.id)) map.set(entry.id, entry.title);
28
+ }
29
+ return map;
30
+ }, [catalog]);
31
+ const featureLabel = React.useCallback((id) => titleById.get(id) ?? id, [titleById]);
32
+ const hasMissing = diagnostics.missingDependencies.length > 0;
33
+ const hasOrphaned = diagnostics.orphanedDependents.length > 0;
34
+ const showUnknown = !hideUnknownReferences && diagnostics.unknownReferences.length > 0;
35
+ if (!hasMissing && !hasOrphaned && !showUnknown) return null;
36
+ const handleAdd = (dep) => onGrantedChange((prev) => applyAddMissingDependency(prev, dep));
37
+ const handleRestore = (dep) => onGrantedChange((prev) => applyRestoreDependency(prev, dep));
38
+ const handleDropDependents = (dependents) => onGrantedChange((prev) => applyRemoveDependents(prev, dependents));
39
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", "data-testid": "acl-dependency-diagnostics", children: [
40
+ hasMissing && /* @__PURE__ */ jsx(Alert, { status: "warning", style: "lighter", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
41
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: t(
42
+ "auth.acl.deps.missing.title",
43
+ "Some granted permissions need other permissions to work:"
44
+ ) }),
45
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-sm", children: diagnostics.missingDependencies.map((row) => /* @__PURE__ */ jsxs(
46
+ "li",
47
+ {
48
+ className: "flex flex-wrap items-center gap-x-2 gap-y-1",
49
+ "data-testid": `missing-${row.feature}`,
50
+ children: [
51
+ /* @__PURE__ */ jsx("span", { children: t("auth.acl.deps.missing.item", '"{feature}" needs:', {
52
+ feature: featureLabel(row.feature)
53
+ }) }),
54
+ row.missing.map((dep) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
55
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: featureLabel(dep) }),
56
+ /* @__PURE__ */ jsx(
57
+ Button,
58
+ {
59
+ type: "button",
60
+ size: "sm",
61
+ variant: "outline",
62
+ onClick: () => handleAdd(dep),
63
+ "data-testid": `add-missing-${row.feature}-${dep}`,
64
+ children: t("auth.acl.deps.missing.add", 'Add "{dep}"', {
65
+ dep: featureLabel(dep)
66
+ })
67
+ }
68
+ )
69
+ ] }, dep))
70
+ ]
71
+ },
72
+ row.feature
73
+ )) })
74
+ ] }) }),
75
+ hasOrphaned && /* @__PURE__ */ jsx(Alert, { status: "warning", style: "lighter", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
76
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: t(
77
+ "auth.acl.deps.orphaned.title",
78
+ "Removing a permission that other granted permissions need:"
79
+ ) }),
80
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-sm", children: diagnostics.orphanedDependents.map((row) => /* @__PURE__ */ jsxs(
81
+ "li",
82
+ {
83
+ className: "flex flex-wrap items-center gap-x-2 gap-y-1",
84
+ "data-testid": `orphaned-${row.dependency}`,
85
+ children: [
86
+ /* @__PURE__ */ jsx("span", { children: t("auth.acl.deps.orphaned.item", '"{dependency}" is required by:', {
87
+ dependency: featureLabel(row.dependency)
88
+ }) }),
89
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: row.dependents.map((dependent) => featureLabel(dependent)).join(", ") }),
90
+ /* @__PURE__ */ jsx(
91
+ Button,
92
+ {
93
+ type: "button",
94
+ size: "sm",
95
+ variant: "outline",
96
+ onClick: () => handleRestore(row.dependency),
97
+ "data-testid": `restore-${row.dependency}`,
98
+ children: t("auth.acl.deps.orphaned.restore", 'Restore "{dependency}"', {
99
+ dependency: featureLabel(row.dependency)
100
+ })
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx(
104
+ Button,
105
+ {
106
+ type: "button",
107
+ size: "sm",
108
+ variant: "outline",
109
+ onClick: () => handleDropDependents(row.dependents),
110
+ "data-testid": `drop-dependents-${row.dependency}`,
111
+ children: t("auth.acl.deps.orphaned.drop", "Drop dependents")
112
+ }
113
+ )
114
+ ]
115
+ },
116
+ row.dependency
117
+ )) })
118
+ ] }) }),
119
+ showUnknown && /* @__PURE__ */ jsx(Alert, { status: "warning", style: "lighter", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
120
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: t(
121
+ "auth.acl.deps.unknown.title",
122
+ "Some declared dependencies do not match any known permission:"
123
+ ) }),
124
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-sm font-mono text-xs text-muted-foreground", children: diagnostics.unknownReferences.map((row) => /* @__PURE__ */ jsxs("li", { "data-testid": `unknown-${row.feature}`, children: [
125
+ row.feature,
126
+ " \u2192 ",
127
+ row.missing.join(", ")
128
+ ] }, row.feature)) })
129
+ ] }) })
130
+ ] });
131
+ }
132
+ export {
133
+ AclDependencyDiagnosticsPanel
134
+ };
135
+ //# sourceMappingURL=AclDependencyDiagnosticsPanel.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/auth/components/AclDependencyDiagnosticsPanel.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n applyAddMissingDependency,\n applyRemoveDependents,\n applyRestoreDependency,\n resolveAclDependencyDiagnostics,\n type FeatureDescriptor,\n} from '@open-mercato/shared/security/aclDependencies'\n\nexport type AclDependencyDiagnosticsPanelProps = {\n granted: readonly string[]\n catalog: readonly FeatureDescriptor[]\n onGrantedChange: (updater: (prev: string[]) => string[]) => void\n hideUnknownReferences?: boolean\n}\n\nexport function AclDependencyDiagnosticsPanel({\n granted,\n catalog,\n onGrantedChange,\n hideUnknownReferences,\n}: AclDependencyDiagnosticsPanelProps) {\n const t = useT()\n const diagnostics = React.useMemo(\n () => resolveAclDependencyDiagnostics(granted, catalog),\n [granted, catalog],\n )\n const titleById = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const entry of catalog) {\n if (entry?.title && !map.has(entry.id)) map.set(entry.id, entry.title)\n }\n return map\n }, [catalog])\n const featureLabel = React.useCallback((id: string) => titleById.get(id) ?? id, [titleById])\n\n const hasMissing = diagnostics.missingDependencies.length > 0\n const hasOrphaned = diagnostics.orphanedDependents.length > 0\n const showUnknown = !hideUnknownReferences && diagnostics.unknownReferences.length > 0\n if (!hasMissing && !hasOrphaned && !showUnknown) return null\n\n const handleAdd = (dep: string) => onGrantedChange((prev) => applyAddMissingDependency(prev, dep))\n const handleRestore = (dep: string) => onGrantedChange((prev) => applyRestoreDependency(prev, dep))\n const handleDropDependents = (dependents: readonly string[]) =>\n onGrantedChange((prev) => applyRemoveDependents(prev, dependents))\n\n return (\n <div className=\"space-y-3\" data-testid=\"acl-dependency-diagnostics\">\n {hasMissing && (\n <Alert status=\"warning\" style=\"lighter\">\n <div className=\"space-y-2\">\n <div className=\"text-sm font-medium\">\n {t(\n 'auth.acl.deps.missing.title',\n 'Some granted permissions need other permissions to work:',\n )}\n </div>\n <ul className=\"space-y-1 text-sm\">\n {diagnostics.missingDependencies.map((row) => (\n <li\n key={row.feature}\n className=\"flex flex-wrap items-center gap-x-2 gap-y-1\"\n data-testid={`missing-${row.feature}`}\n >\n <span>\n {t('auth.acl.deps.missing.item', '\"{feature}\" needs:', {\n feature: featureLabel(row.feature),\n })}\n </span>\n {row.missing.map((dep) => (\n <span key={dep} className=\"inline-flex items-center gap-1\">\n <span className=\"font-mono text-xs text-muted-foreground\">\n {featureLabel(dep)}\n </span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => handleAdd(dep)}\n data-testid={`add-missing-${row.feature}-${dep}`}\n >\n {t('auth.acl.deps.missing.add', 'Add \"{dep}\"', {\n dep: featureLabel(dep),\n })}\n </Button>\n </span>\n ))}\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )}\n\n {hasOrphaned && (\n <Alert status=\"warning\" style=\"lighter\">\n <div className=\"space-y-2\">\n <div className=\"text-sm font-medium\">\n {t(\n 'auth.acl.deps.orphaned.title',\n 'Removing a permission that other granted permissions need:',\n )}\n </div>\n <ul className=\"space-y-1 text-sm\">\n {diagnostics.orphanedDependents.map((row) => (\n <li\n key={row.dependency}\n className=\"flex flex-wrap items-center gap-x-2 gap-y-1\"\n data-testid={`orphaned-${row.dependency}`}\n >\n <span>\n {t('auth.acl.deps.orphaned.item', '\"{dependency}\" is required by:', {\n dependency: featureLabel(row.dependency),\n })}\n </span>\n <span className=\"font-mono text-xs text-muted-foreground\">\n {row.dependents\n .map((dependent) => featureLabel(dependent))\n .join(', ')}\n </span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => handleRestore(row.dependency)}\n data-testid={`restore-${row.dependency}`}\n >\n {t('auth.acl.deps.orphaned.restore', 'Restore \"{dependency}\"', {\n dependency: featureLabel(row.dependency),\n })}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => handleDropDependents(row.dependents)}\n data-testid={`drop-dependents-${row.dependency}`}\n >\n {t('auth.acl.deps.orphaned.drop', 'Drop dependents')}\n </Button>\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )}\n\n {showUnknown && (\n <Alert status=\"warning\" style=\"lighter\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium\">\n {t(\n 'auth.acl.deps.unknown.title',\n 'Some declared dependencies do not match any known permission:',\n )}\n </div>\n <ul className=\"space-y-1 text-sm font-mono text-xs text-muted-foreground\">\n {diagnostics.unknownReferences.map((row) => (\n <li key={row.feature} data-testid={`unknown-${row.feature}`}>\n {row.feature} \u2192 {row.missing.join(', ')}\n </li>\n ))}\n </ul>\n </div>\n </Alert>\n )}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -6,6 +6,7 @@ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
6
6
  import Link from "next/link";
7
7
  import { hasFeature, matchFeature } from "@open-mercato/shared/security/features";
8
8
  import { useT } from "@open-mercato/shared/lib/i18n/context";
9
+ import { AclDependencyDiagnosticsPanel } from "./AclDependencyDiagnosticsPanel.js";
9
10
  function toTitleCase(value) {
10
11
  return value.replace(/[-_.]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
11
12
  }
@@ -313,6 +314,15 @@ function AclEditor({
313
314
  }
314
315
  )
315
316
  ] }),
317
+ !hasGlobalWildcard && /* @__PURE__ */ jsx(
318
+ AclDependencyDiagnosticsPanel,
319
+ {
320
+ granted,
321
+ catalog: features,
322
+ onGrantedChange: updateGranted,
323
+ hideUnknownReferences: process.env.NODE_ENV === "production"
324
+ }
325
+ ),
316
326
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: grouped.map((group) => {
317
327
  const moduleWildcard = isModuleWildcardEnabled(group.moduleId);
318
328
  const nestedWildcards = Array.from(
@@ -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 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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",
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'\nimport type { FeatureDescriptor } from '@open-mercato/shared/security/aclDependencies'\nimport { AclDependencyDiagnosticsPanel } from './AclDependencyDiagnosticsPanel'\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; dependsOn?: 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 {!hasGlobalWildcard && (\n <AclDependencyDiagnosticsPanel\n granted={granted}\n catalog={features as readonly FeatureDescriptor[]}\n onGrantedChange={updateGranted}\n hideUnknownReferences={process.env.NODE_ENV === 'production'}\n />\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
  }
@@ -1,23 +1,83 @@
1
1
  const features = [
2
2
  { id: "customers.people.view", title: "View people", module: "customers" },
3
- { id: "customers.people.manage", title: "Manage people", module: "customers" },
3
+ {
4
+ id: "customers.people.manage",
5
+ title: "Manage people",
6
+ module: "customers",
7
+ dependsOn: ["customers.people.view"]
8
+ },
4
9
  { id: "customers.companies.view", title: "View companies", module: "customers" },
5
- { id: "customers.companies.manage", title: "Manage companies", module: "customers" },
6
- { id: "customers.deals.view", title: "View deals", module: "customers" },
7
- { id: "customers.deals.manage", title: "Manage deals", module: "customers" },
10
+ {
11
+ id: "customers.companies.manage",
12
+ title: "Manage companies",
13
+ module: "customers",
14
+ dependsOn: ["customers.companies.view"]
15
+ },
16
+ {
17
+ id: "customers.deals.view",
18
+ title: "View deals",
19
+ module: "customers",
20
+ dependsOn: ["customers.people.view"]
21
+ },
22
+ {
23
+ id: "customers.deals.manage",
24
+ title: "Manage deals",
25
+ module: "customers",
26
+ dependsOn: ["customers.deals.view"]
27
+ },
8
28
  { id: "customers.activities.view", title: "View activities", module: "customers" },
9
- { id: "customers.activities.manage", title: "Manage activities", module: "customers" },
29
+ {
30
+ id: "customers.activities.manage",
31
+ title: "Manage activities",
32
+ module: "customers",
33
+ dependsOn: ["customers.activities.view"]
34
+ },
10
35
  { id: "customers.settings.manage", title: "Manage customer settings", module: "customers" },
11
36
  { id: "customers.pipelines.view", title: "View pipelines", module: "customers" },
12
- { id: "customers.pipelines.manage", title: "Manage pipelines", module: "customers" },
13
- { id: "customers.widgets.todos", title: "Use customer todos widget", module: "customers" },
14
- { id: "customers.widgets.next-interactions", title: "Use customer next interactions widget", module: "customers" },
15
- { id: "customers.widgets.new-customers", title: "Use customer new customers widget", module: "customers" },
16
- { id: "customers.widgets.new-deals", title: "Use customer new deals widget", module: "customers" },
37
+ {
38
+ id: "customers.pipelines.manage",
39
+ title: "Manage pipelines",
40
+ module: "customers",
41
+ dependsOn: ["customers.pipelines.view"]
42
+ },
43
+ {
44
+ id: "customers.widgets.todos",
45
+ title: "Use customer todos widget",
46
+ module: "customers",
47
+ dependsOn: ["customers.activities.view"]
48
+ },
49
+ {
50
+ id: "customers.widgets.next-interactions",
51
+ title: "Use customer next interactions widget",
52
+ module: "customers",
53
+ dependsOn: ["customers.interactions.view"]
54
+ },
55
+ {
56
+ id: "customers.widgets.new-customers",
57
+ title: "Use customer new customers widget",
58
+ module: "customers",
59
+ dependsOn: ["customers.people.view"]
60
+ },
61
+ {
62
+ id: "customers.widgets.new-deals",
63
+ title: "Use customer new deals widget",
64
+ module: "customers",
65
+ dependsOn: ["customers.deals.view"]
66
+ },
17
67
  { id: "customers.interactions.view", title: "View interactions", module: "customers" },
18
- { id: "customers.interactions.manage", title: "Manage interactions", module: "customers" },
68
+ {
69
+ id: "customers.interactions.manage",
70
+ title: "Manage interactions",
71
+ module: "customers",
72
+ dependsOn: ["customers.interactions.view"]
73
+ },
19
74
  { id: "customers.roles.view", title: "View entity roles", module: "customers" },
20
- { id: "customers.roles.manage", title: "Manage entity roles", module: "customers" }
75
+ {
76
+ id: "customers.roles.manage",
77
+ title: "Manage entity roles",
78
+ module: "customers",
79
+ dependsOn: ["customers.roles.view"]
80
+ }
21
81
  ];
22
82
  var acl_default = features;
23
83
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/customers/acl.ts"],
4
- "sourcesContent": ["export const features = [\n { id: 'customers.people.view', title: 'View people', module: 'customers' },\n { id: 'customers.people.manage', title: 'Manage people', module: 'customers' },\n { id: 'customers.companies.view', title: 'View companies', module: 'customers' },\n { id: 'customers.companies.manage', title: 'Manage companies', module: 'customers' },\n { id: 'customers.deals.view', title: 'View deals', module: 'customers' },\n { id: 'customers.deals.manage', title: 'Manage deals', module: 'customers' },\n { id: 'customers.activities.view', title: 'View activities', module: 'customers' },\n { id: 'customers.activities.manage', title: 'Manage activities', module: 'customers' },\n { id: 'customers.settings.manage', title: 'Manage customer settings', module: 'customers' },\n { id: 'customers.pipelines.view', title: 'View pipelines', module: 'customers' },\n { id: 'customers.pipelines.manage', title: 'Manage pipelines', module: 'customers' },\n { id: 'customers.widgets.todos', title: 'Use customer todos widget', module: 'customers' },\n { id: 'customers.widgets.next-interactions', title: 'Use customer next interactions widget', module: 'customers' },\n { id: 'customers.widgets.new-customers', title: 'Use customer new customers widget', module: 'customers' },\n { id: 'customers.widgets.new-deals', title: 'Use customer new deals widget', module: 'customers' },\n { id: 'customers.interactions.view', title: 'View interactions', module: 'customers' },\n { id: 'customers.interactions.manage', title: 'Manage interactions', module: 'customers' },\n { id: 'customers.roles.view', title: 'View entity roles', module: 'customers' },\n { id: 'customers.roles.manage', title: 'Manage entity roles', module: 'customers' },\n]\n\nexport default features\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,yBAAyB,OAAO,eAAe,QAAQ,YAAY;AAAA,EACzE,EAAE,IAAI,2BAA2B,OAAO,iBAAiB,QAAQ,YAAY;AAAA,EAC7E,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E,EAAE,IAAI,8BAA8B,OAAO,oBAAoB,QAAQ,YAAY;AAAA,EACnF,EAAE,IAAI,wBAAwB,OAAO,cAAc,QAAQ,YAAY;AAAA,EACvE,EAAE,IAAI,0BAA0B,OAAO,gBAAgB,QAAQ,YAAY;AAAA,EAC3E,EAAE,IAAI,6BAA6B,OAAO,mBAAmB,QAAQ,YAAY;AAAA,EACjF,EAAE,IAAI,+BAA+B,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EACrF,EAAE,IAAI,6BAA6B,OAAO,4BAA4B,QAAQ,YAAY;AAAA,EAC1F,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E,EAAE,IAAI,8BAA8B,OAAO,oBAAoB,QAAQ,YAAY;AAAA,EACnF,EAAE,IAAI,2BAA2B,OAAO,6BAA6B,QAAQ,YAAY;AAAA,EACzF,EAAE,IAAI,uCAAuC,OAAO,yCAAyC,QAAQ,YAAY;AAAA,EACjH,EAAE,IAAI,mCAAmC,OAAO,qCAAqC,QAAQ,YAAY;AAAA,EACzG,EAAE,IAAI,+BAA+B,OAAO,iCAAiC,QAAQ,YAAY;AAAA,EACjG,EAAE,IAAI,+BAA+B,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EACrF,EAAE,IAAI,iCAAiC,OAAO,uBAAuB,QAAQ,YAAY;AAAA,EACzF,EAAE,IAAI,wBAAwB,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EAC9E,EAAE,IAAI,0BAA0B,OAAO,uBAAuB,QAAQ,YAAY;AACpF;AAEA,IAAO,cAAQ;",
4
+ "sourcesContent": ["export const features = [\n { id: 'customers.people.view', title: 'View people', module: 'customers' },\n {\n id: 'customers.people.manage',\n title: 'Manage people',\n module: 'customers',\n dependsOn: ['customers.people.view'],\n },\n { id: 'customers.companies.view', title: 'View companies', module: 'customers' },\n {\n id: 'customers.companies.manage',\n title: 'Manage companies',\n module: 'customers',\n dependsOn: ['customers.companies.view'],\n },\n {\n id: 'customers.deals.view',\n title: 'View deals',\n module: 'customers',\n dependsOn: ['customers.people.view'],\n },\n {\n id: 'customers.deals.manage',\n title: 'Manage deals',\n module: 'customers',\n dependsOn: ['customers.deals.view'],\n },\n { id: 'customers.activities.view', title: 'View activities', module: 'customers' },\n {\n id: 'customers.activities.manage',\n title: 'Manage activities',\n module: 'customers',\n dependsOn: ['customers.activities.view'],\n },\n { id: 'customers.settings.manage', title: 'Manage customer settings', module: 'customers' },\n { id: 'customers.pipelines.view', title: 'View pipelines', module: 'customers' },\n {\n id: 'customers.pipelines.manage',\n title: 'Manage pipelines',\n module: 'customers',\n dependsOn: ['customers.pipelines.view'],\n },\n {\n id: 'customers.widgets.todos',\n title: 'Use customer todos widget',\n module: 'customers',\n dependsOn: ['customers.activities.view'],\n },\n {\n id: 'customers.widgets.next-interactions',\n title: 'Use customer next interactions widget',\n module: 'customers',\n dependsOn: ['customers.interactions.view'],\n },\n {\n id: 'customers.widgets.new-customers',\n title: 'Use customer new customers widget',\n module: 'customers',\n dependsOn: ['customers.people.view'],\n },\n {\n id: 'customers.widgets.new-deals',\n title: 'Use customer new deals widget',\n module: 'customers',\n dependsOn: ['customers.deals.view'],\n },\n { id: 'customers.interactions.view', title: 'View interactions', module: 'customers' },\n {\n id: 'customers.interactions.manage',\n title: 'Manage interactions',\n module: 'customers',\n dependsOn: ['customers.interactions.view'],\n },\n { id: 'customers.roles.view', title: 'View entity roles', module: 'customers' },\n {\n id: 'customers.roles.manage',\n title: 'Manage entity roles',\n module: 'customers',\n dependsOn: ['customers.roles.view'],\n },\n]\n\nexport default features\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,yBAAyB,OAAO,eAAe,QAAQ,YAAY;AAAA,EACzE;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,uBAAuB;AAAA,EACrC;AAAA,EACA,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,sBAAsB;AAAA,EACpC;AAAA,EACA,EAAE,IAAI,6BAA6B,OAAO,mBAAmB,QAAQ,YAAY;AAAA,EACjF;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,2BAA2B;AAAA,EACzC;AAAA,EACA,EAAE,IAAI,6BAA6B,OAAO,4BAA4B,QAAQ,YAAY;AAAA,EAC1F,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,6BAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,sBAAsB;AAAA,EACpC;AAAA,EACA,EAAE,IAAI,+BAA+B,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EACrF;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,6BAA6B;AAAA,EAC3C;AAAA,EACA,EAAE,IAAI,wBAAwB,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EAC9E;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,sBAAsB;AAAA,EACpC;AACF;AAEA,IAAO,cAAQ;",
6
6
  "names": []
7
7
  }