@carlonicora/nextjs-jsonapi 1.78.0 → 1.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/{AssistantMessageInterface-DS_tyJTV.d.ts → AssistantMessageInterface-DWnbd6J7.d.ts} +1 -1
  2. package/dist/{AssistantMessageInterface-D0Kwf8CR.d.mts → AssistantMessageInterface-Mla6kgPe.d.mts} +1 -1
  3. package/dist/{AuthComponent-Blbs06ud.d.ts → AuthComponent-B6DIk8Vf.d.ts} +1 -1
  4. package/dist/{AuthComponent-huIaK5rm.d.mts → AuthComponent-BKI0ZbtD.d.mts} +1 -1
  5. package/dist/{BlockNoteEditor-JXK3JGKJ.mjs → BlockNoteEditor-6CBDTVKV.mjs} +4 -4
  6. package/dist/{BlockNoteEditor-2G5UYALC.js → BlockNoteEditor-EH4HWI7H.js} +14 -14
  7. package/dist/{BlockNoteEditor-2G5UYALC.js.map → BlockNoteEditor-EH4HWI7H.js.map} +1 -1
  8. package/dist/RbacTypes-BTbr27Ew.d.mts +43 -0
  9. package/dist/RbacTypes-BTbr27Ew.d.ts +43 -0
  10. package/dist/{auth.interface-CQJ6A2Cj.d.ts → auth.interface-BBUgMZzs.d.ts} +1 -1
  11. package/dist/{auth.interface-Bdq7-8iV.d.mts → auth.interface-XYEREOD6.d.mts} +1 -1
  12. package/dist/billing/index.js +346 -346
  13. package/dist/billing/index.mjs +3 -3
  14. package/dist/{chunk-FDJQRIMY.js → chunk-5IEWLLLD.js} +61 -2
  15. package/dist/chunk-5IEWLLLD.js.map +1 -0
  16. package/dist/{chunk-I65SSQ5Z.mjs → chunk-BKM5U3DE.mjs} +60 -1
  17. package/dist/chunk-BKM5U3DE.mjs.map +1 -0
  18. package/dist/{chunk-NB6TIKHK.mjs → chunk-ENRSFVOS.mjs} +2064 -2295
  19. package/dist/chunk-ENRSFVOS.mjs.map +1 -0
  20. package/dist/{chunk-NZOUEN67.mjs → chunk-MEWXQEVE.mjs} +38 -29
  21. package/dist/{chunk-NZOUEN67.mjs.map → chunk-MEWXQEVE.mjs.map} +1 -1
  22. package/dist/{chunk-X4YDETTD.js → chunk-TWDSDTHU.js} +39 -30
  23. package/dist/chunk-TWDSDTHU.js.map +1 -0
  24. package/dist/{chunk-ZEDB6JVB.js → chunk-ZDP3MBUI.js} +1142 -1373
  25. package/dist/chunk-ZDP3MBUI.js.map +1 -0
  26. package/dist/client/index.d.mts +6 -24
  27. package/dist/client/index.d.ts +6 -24
  28. package/dist/client/index.js +4 -10
  29. package/dist/client/index.js.map +1 -1
  30. package/dist/client/index.mjs +3 -9
  31. package/dist/components/index.d.mts +32 -34
  32. package/dist/components/index.d.ts +32 -34
  33. package/dist/components/index.js +4 -10
  34. package/dist/components/index.js.map +1 -1
  35. package/dist/components/index.mjs +3 -9
  36. package/dist/{config-B3jKt9P7.d.ts → config-B5oBQVEA.d.ts} +1 -1
  37. package/dist/{config-DkHF61xA.d.mts → config-Bx_uh22h.d.mts} +1 -1
  38. package/dist/contexts/index.d.mts +41 -4
  39. package/dist/contexts/index.d.ts +41 -4
  40. package/dist/contexts/index.js +8 -4
  41. package/dist/contexts/index.js.map +1 -1
  42. package/dist/contexts/index.mjs +7 -3
  43. package/dist/core/index.d.mts +19 -11
  44. package/dist/core/index.d.ts +19 -11
  45. package/dist/core/index.js +4 -2
  46. package/dist/core/index.js.map +1 -1
  47. package/dist/core/index.mjs +3 -1
  48. package/dist/index.d.mts +117 -20
  49. package/dist/index.d.ts +117 -20
  50. package/dist/index.js +7 -3
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +6 -2
  53. package/dist/{notification.interface-DG6obXUH.d.mts → notification.interface-DLZGtV7Z.d.mts} +1 -1
  54. package/dist/{notification.interface-DcSuc9CL.d.ts → notification.interface-aLEJbA_g.d.ts} +1 -1
  55. package/dist/{s3.service-DGilbikH.d.mts → s3.service-CVgLWaDc.d.mts} +2 -2
  56. package/dist/{s3.service-DjwEQJPe.d.ts → s3.service-SLlX0Zbz.d.ts} +2 -2
  57. package/dist/server/index.d.mts +3 -3
  58. package/dist/server/index.d.ts +3 -3
  59. package/dist/server/index.js +3 -3
  60. package/dist/server/index.mjs +1 -1
  61. package/dist/useDataListRetriever-BqJSFBck.d.mts +33 -0
  62. package/dist/useDataListRetriever-BqJSFBck.d.ts +33 -0
  63. package/dist/{useSocket-CmzVtg32.d.mts → useSocket-BkxHHujj.d.mts} +1 -1
  64. package/dist/{useSocket-8eUtnL7J.d.ts → useSocket-CMDjWFYm.d.ts} +1 -1
  65. package/package.json +1 -1
  66. package/src/client/index.ts +0 -4
  67. package/src/components/index.ts +0 -3
  68. package/src/contexts/index.ts +1 -0
  69. package/src/core/registry/ModuleRegistry.ts +1 -0
  70. package/src/features/rbac/components/RbacContainer.tsx +318 -49
  71. package/src/features/rbac/components/RbacPermissionPicker.tsx +144 -121
  72. package/src/features/rbac/contexts/RbacContext.tsx +209 -0
  73. package/src/features/rbac/contexts/index.ts +1 -0
  74. package/src/features/rbac/data/RbacMatrixModel.ts +84 -0
  75. package/src/features/rbac/data/RbacService.ts +61 -33
  76. package/src/features/rbac/data/RbacTypes.ts +28 -0
  77. package/src/features/rbac/data/index.ts +1 -0
  78. package/src/features/rbac/index.ts +1 -10
  79. package/src/features/rbac/rbac.module.ts +13 -0
  80. package/dist/ModulePathsInterface-BrdqgteS.d.mts +0 -31
  81. package/dist/ModulePathsInterface-DJKs7s_s.d.ts +0 -31
  82. package/dist/chunk-FDJQRIMY.js.map +0 -1
  83. package/dist/chunk-I65SSQ5Z.mjs.map +0 -1
  84. package/dist/chunk-NB6TIKHK.mjs.map +0 -1
  85. package/dist/chunk-X4YDETTD.js.map +0 -1
  86. package/dist/chunk-ZEDB6JVB.js.map +0 -1
  87. package/dist/useRbacState-C88O-5L8.d.ts +0 -77
  88. package/dist/useRbacState-mqYiRp3J.d.mts +0 -77
  89. package/src/features/rbac/components/RbacFeatureSection.tsx +0 -66
  90. package/src/features/rbac/components/RbacModuleTable.tsx +0 -121
  91. package/src/features/rbac/components/RbacToolbar.tsx +0 -40
  92. package/src/features/rbac/hooks/useRbacState.test.ts +0 -180
  93. package/src/features/rbac/hooks/useRbacState.ts +0 -319
  94. package/src/features/rbac/utils/RbacMigrationGenerator.test.ts +0 -124
  95. package/src/features/rbac/utils/RbacMigrationGenerator.ts +0 -184
  96. /package/dist/{BlockNoteEditor-JXK3JGKJ.mjs.map → BlockNoteEditor-6CBDTVKV.mjs.map} +0 -0
@@ -1,77 +0,0 @@
1
- import { F as FeatureInterface } from './feature.interface-BO25VLlx.js';
2
- import { R as RoleInterface } from './notification.interface-DcSuc9CL.js';
3
- import { P as PermissionMappingInterface, M as ModulePathsInterface, A as ActionType, a as PermissionValue, b as PermissionsMap } from './ModulePathsInterface-DJKs7s_s.js';
4
-
5
- type PageInfo = {
6
- startItem: number;
7
- endItem: number;
8
- pageSize: number;
9
- };
10
- type DataListRetriever<T> = {
11
- ready?: boolean;
12
- setReady: (state: boolean) => void;
13
- isLoaded: boolean;
14
- data: T[] | undefined;
15
- total?: number;
16
- next?: (onlyNewRecords?: boolean) => Promise<void>;
17
- previous?: (onlyNewRecords?: boolean) => Promise<void>;
18
- search: (search: string) => Promise<void>;
19
- refresh: () => Promise<void>;
20
- addAdditionalParameter: (key: string, value: any | null) => void;
21
- removeAdditionalParameter: (key: string) => void;
22
- setRefreshedElement: (element: T) => void;
23
- removeElement: (element: T) => void;
24
- isSearch: boolean;
25
- pageInfo?: PageInfo;
26
- };
27
- declare function useDataListRetriever<T>(params: {
28
- ready?: boolean;
29
- retriever: (params: any) => Promise<T[]>;
30
- retrieverParams?: any;
31
- search?: string;
32
- addAdditionalParameter?: (key: string, value: any | null) => void;
33
- requiresSearch?: boolean;
34
- module: any;
35
- }): DataListRetriever<T>;
36
-
37
- interface OriginalData {
38
- features: FeatureInterface[];
39
- roles: RoleInterface[];
40
- permissionMappings: PermissionMappingInterface[];
41
- moduleRelationshipPaths: Map<string, string[]>;
42
- }
43
- declare function useRbacState(): {
44
- original: OriginalData | null;
45
- isDirty: boolean;
46
- init: (features: FeatureInterface[], roles: RoleInterface[], permissionMappings: PermissionMappingInterface[], modulePaths: ModulePathsInterface[]) => void;
47
- setFeatureIsCore: (featureId: string, isCore: boolean) => void;
48
- setModuleDefaultPermission: (moduleId: string, actionType: ActionType, value: PermissionValue) => void;
49
- setRolePermission: (roleId: string, moduleId: string, actionType: ActionType, value: PermissionValue) => void;
50
- clearRolePermission: (roleId: string, moduleId: string, actionType: ActionType) => void;
51
- clearAllRolePermissions: (roleId: string, moduleId: string) => void;
52
- resetModulePermissions: (moduleId: string, roles: {
53
- id: string;
54
- }[]) => void;
55
- reset: () => void;
56
- getFeatureIsCore: (featureId: string) => boolean;
57
- getModuleDefaultPermission: (moduleId: string, actionType: ActionType) => PermissionValue | undefined;
58
- getRolePermission: (roleId: string, moduleId: string, actionType: ActionType) => PermissionValue | undefined | null;
59
- getEffectiveConfiguration: () => {
60
- features: {
61
- id: string;
62
- name: string;
63
- isCore: boolean;
64
- modules: {
65
- id: string;
66
- name: string;
67
- permissions: PermissionsMap;
68
- }[];
69
- }[];
70
- roles: RoleInterface[];
71
- rolePermissionsMap: Map<string, PermissionsMap>;
72
- } | null;
73
- getModuleRelationshipPaths: (moduleId: string) => string[];
74
- };
75
- type RbacStateApi = ReturnType<typeof useRbacState>;
76
-
77
- export { type DataListRetriever as D, type RbacStateApi as R, useDataListRetriever as a, useRbacState as u };
@@ -1,77 +0,0 @@
1
- import { F as FeatureInterface } from './feature.interface-CXb1-vNq.mjs';
2
- import { R as RoleInterface } from './notification.interface-DG6obXUH.mjs';
3
- import { P as PermissionMappingInterface, M as ModulePathsInterface, A as ActionType, a as PermissionValue, b as PermissionsMap } from './ModulePathsInterface-BrdqgteS.mjs';
4
-
5
- type PageInfo = {
6
- startItem: number;
7
- endItem: number;
8
- pageSize: number;
9
- };
10
- type DataListRetriever<T> = {
11
- ready?: boolean;
12
- setReady: (state: boolean) => void;
13
- isLoaded: boolean;
14
- data: T[] | undefined;
15
- total?: number;
16
- next?: (onlyNewRecords?: boolean) => Promise<void>;
17
- previous?: (onlyNewRecords?: boolean) => Promise<void>;
18
- search: (search: string) => Promise<void>;
19
- refresh: () => Promise<void>;
20
- addAdditionalParameter: (key: string, value: any | null) => void;
21
- removeAdditionalParameter: (key: string) => void;
22
- setRefreshedElement: (element: T) => void;
23
- removeElement: (element: T) => void;
24
- isSearch: boolean;
25
- pageInfo?: PageInfo;
26
- };
27
- declare function useDataListRetriever<T>(params: {
28
- ready?: boolean;
29
- retriever: (params: any) => Promise<T[]>;
30
- retrieverParams?: any;
31
- search?: string;
32
- addAdditionalParameter?: (key: string, value: any | null) => void;
33
- requiresSearch?: boolean;
34
- module: any;
35
- }): DataListRetriever<T>;
36
-
37
- interface OriginalData {
38
- features: FeatureInterface[];
39
- roles: RoleInterface[];
40
- permissionMappings: PermissionMappingInterface[];
41
- moduleRelationshipPaths: Map<string, string[]>;
42
- }
43
- declare function useRbacState(): {
44
- original: OriginalData | null;
45
- isDirty: boolean;
46
- init: (features: FeatureInterface[], roles: RoleInterface[], permissionMappings: PermissionMappingInterface[], modulePaths: ModulePathsInterface[]) => void;
47
- setFeatureIsCore: (featureId: string, isCore: boolean) => void;
48
- setModuleDefaultPermission: (moduleId: string, actionType: ActionType, value: PermissionValue) => void;
49
- setRolePermission: (roleId: string, moduleId: string, actionType: ActionType, value: PermissionValue) => void;
50
- clearRolePermission: (roleId: string, moduleId: string, actionType: ActionType) => void;
51
- clearAllRolePermissions: (roleId: string, moduleId: string) => void;
52
- resetModulePermissions: (moduleId: string, roles: {
53
- id: string;
54
- }[]) => void;
55
- reset: () => void;
56
- getFeatureIsCore: (featureId: string) => boolean;
57
- getModuleDefaultPermission: (moduleId: string, actionType: ActionType) => PermissionValue | undefined;
58
- getRolePermission: (roleId: string, moduleId: string, actionType: ActionType) => PermissionValue | undefined | null;
59
- getEffectiveConfiguration: () => {
60
- features: {
61
- id: string;
62
- name: string;
63
- isCore: boolean;
64
- modules: {
65
- id: string;
66
- name: string;
67
- permissions: PermissionsMap;
68
- }[];
69
- }[];
70
- roles: RoleInterface[];
71
- rolePermissionsMap: Map<string, PermissionsMap>;
72
- } | null;
73
- getModuleRelationshipPaths: (moduleId: string) => string[];
74
- };
75
- type RbacStateApi = ReturnType<typeof useRbacState>;
76
-
77
- export { type DataListRetriever as D, type RbacStateApi as R, useDataListRetriever as a, useRbacState as u };
@@ -1,66 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "../../../lib/utils";
4
- import { ChevronDownIcon } from "lucide-react";
5
- import { useTranslations } from "next-intl";
6
- import { useState } from "react";
7
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../shadcnui";
8
- import { Switch } from "../../../shadcnui";
9
- import { FeatureInterface } from "../../feature";
10
- import { RoleInterface } from "../../role";
11
- import { RbacStateApi } from "../hooks/useRbacState";
12
- import RbacModuleTable from "./RbacModuleTable";
13
-
14
- interface RbacFeatureSectionProps {
15
- feature: FeatureInterface;
16
- roles: RoleInterface[];
17
- stateApi: RbacStateApi;
18
- }
19
-
20
- export default function RbacFeatureSection({ feature, roles, stateApi }: RbacFeatureSectionProps) {
21
- const t = useTranslations();
22
- const [isOpen, setIsOpen] = useState(true);
23
- const featureIsCore = stateApi.getFeatureIsCore(feature.id);
24
-
25
- return (
26
- <Collapsible open={isOpen} onOpenChange={setIsOpen}>
27
- <div className="rounded-lg border bg-card">
28
- {/* Feature header */}
29
- <CollapsibleTrigger className="w-full">
30
- <div className="flex cursor-pointer items-center justify-between px-4 py-3 hover:bg-muted/50 transition-colors">
31
- <div className="flex items-center gap-3">
32
- <ChevronDownIcon
33
- className={cn("h-4 w-4 text-muted-foreground transition-transform", !isOpen && "-rotate-90")}
34
- />
35
- <h3 className="text-base font-semibold">{feature.name}</h3>
36
- <span className="text-xs text-muted-foreground">
37
- {t("rbac.module_count", { count: feature.modules.length })}
38
- </span>
39
- </div>
40
- <div className="flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
41
- <span className="text-xs text-muted-foreground">{t("rbac.core")}</span>
42
- <Switch
43
- checked={featureIsCore}
44
- onCheckedChange={(checked) => stateApi.setFeatureIsCore(feature.id, checked)}
45
- className="data-checked:bg-accent data-unchecked:bg-gray-300"
46
- />
47
- </div>
48
- </div>
49
- </CollapsibleTrigger>
50
-
51
- <CollapsibleContent>
52
- <div className="space-y-3 p-4 pt-0">
53
- {feature.modules.map((mod) => (
54
- <RbacModuleTable key={mod.id} module={mod} roles={roles} stateApi={stateApi} />
55
- ))}
56
- {feature.modules.length === 0 && (
57
- <p className="text-sm text-muted-foreground italic py-4 text-center">{t("rbac.no_modules")}</p>
58
- )}
59
- </div>
60
- </CollapsibleContent>
61
- </div>
62
- </Collapsible>
63
- );
64
- }
65
-
66
- export { RbacFeatureSection };
@@ -1,121 +0,0 @@
1
- "use client";
2
-
3
- import { RotateCcwIcon } from "lucide-react";
4
- import { useTranslations } from "next-intl";
5
- import { Button } from "../../../shadcnui";
6
- import { ModuleInterface } from "../../module";
7
- import { RoleInterface } from "../../role";
8
- import { ACTION_TYPES, ActionType, COMPANY_ADMINISTRATOR_ROLE_ID } from "../data/RbacTypes";
9
- import { RbacStateApi } from "../hooks/useRbacState";
10
- import RbacPermissionPicker from "./RbacPermissionPicker";
11
-
12
- interface RbacModuleTableProps {
13
- module: ModuleInterface;
14
- roles: RoleInterface[];
15
- stateApi: RbacStateApi;
16
- }
17
-
18
- const ACTION_LABELS: Record<ActionType, string> = {
19
- read: "Read",
20
- create: "Create",
21
- update: "Update",
22
- delete: "Delete",
23
- };
24
-
25
- export default function RbacModuleTable({ module, roles, stateApi }: RbacModuleTableProps) {
26
- const t = useTranslations();
27
- const handleReset = () => {
28
- stateApi.resetModulePermissions(module.id, roles);
29
- };
30
-
31
- return (
32
- <div className="rounded-lg border border-accent bg-card">
33
- {/* Module header */}
34
- <div className="flex items-center justify-between border-b px-4 py-2">
35
- <h4 className="text-sm font-medium">{module.name}</h4>
36
- <Button
37
- variant="ghost"
38
- size="sm"
39
- onClick={handleReset}
40
- className="h-7 px-2 text-muted-foreground hover:text-foreground"
41
- >
42
- <RotateCcwIcon className="h-3.5 w-3.5" />
43
- </Button>
44
- </div>
45
-
46
- {/* Permission grid - Rows = Default + Roles, Columns = Actions */}
47
- <div className="overflow-x-auto">
48
- <table className="w-full text-sm">
49
- <thead>
50
- <tr className="border-b bg-muted/50">
51
- <th className="px-4 py-2 text-left text-xs font-medium text-muted-foreground w-40">{t("rbac.role")}</th>
52
- {ACTION_TYPES.map((actionType) => (
53
- <th
54
- key={actionType}
55
- className="px-2 py-2 text-center text-xs font-medium text-muted-foreground min-w-28"
56
- >
57
- {ACTION_LABELS[actionType]}
58
- </th>
59
- ))}
60
- </tr>
61
- </thead>
62
- <tbody>
63
- {/* Default permissions row */}
64
- <tr className="border-b">
65
- <td className="px-4 py-1 text-xs font-medium text-muted-foreground">{t("rbac.defaults")}</td>
66
- {ACTION_TYPES.map((actionType) => {
67
- const defaultValue = stateApi.getModuleDefaultPermission(module.id, actionType) ?? false;
68
- const originalDefaultValue = module.permissions[actionType] ?? false;
69
-
70
- return (
71
- <td key={actionType} className="px-2 py-1">
72
- <RbacPermissionPicker
73
- value={defaultValue}
74
- originalValue={originalDefaultValue}
75
- isRoleColumn={false}
76
- knownSegments={stateApi.getModuleRelationshipPaths(module.id)}
77
- onSetValue={(value) => stateApi.setModuleDefaultPermission(module.id, actionType, value)}
78
- />
79
- </td>
80
- );
81
- })}
82
- </tr>
83
-
84
- {/* Role rows (CompanyAdministrator hidden — always all-true in migration) */}
85
- {roles
86
- .filter((role) => role.id !== COMPANY_ADMINISTRATOR_ROLE_ID)
87
- .map((role) => (
88
- <tr key={role.id} className="border-b last:border-b-0">
89
- <td className="px-4 py-1 text-xs font-medium text-muted-foreground">{role.name}</td>
90
- {ACTION_TYPES.map((actionType) => {
91
- const roleValue = stateApi.getRolePermission(role.id, module.id, actionType);
92
- const originalMapping = stateApi.original?.permissionMappings.find(
93
- (pm) => pm.roleId === role.id && pm.moduleId === module.id,
94
- );
95
- const originalRoleValue = originalMapping
96
- ? (originalMapping.permissions[actionType] ?? null)
97
- : undefined;
98
-
99
- return (
100
- <td key={actionType} className="px-2 py-1">
101
- <RbacPermissionPicker
102
- value={roleValue}
103
- originalValue={originalRoleValue}
104
- isRoleColumn={true}
105
- knownSegments={stateApi.getModuleRelationshipPaths(module.id)}
106
- onSetValue={(value) => stateApi.setRolePermission(role.id, module.id, actionType, value)}
107
- onClear={() => stateApi.clearRolePermission(role.id, module.id, actionType)}
108
- />
109
- </td>
110
- );
111
- })}
112
- </tr>
113
- ))}
114
- </tbody>
115
- </table>
116
- </div>
117
- </div>
118
- );
119
- }
120
-
121
- export { RbacModuleTable };
@@ -1,40 +0,0 @@
1
- "use client";
2
-
3
- import { DownloadIcon, RotateCcwIcon } from "lucide-react";
4
- import { useTranslations } from "next-intl";
5
- import { Badge, Button } from "../../../shadcnui";
6
-
7
- interface RbacToolbarProps {
8
- isDirty: boolean;
9
- onGenerate: () => void;
10
- onReset: () => void;
11
- }
12
-
13
- export default function RbacToolbar({ isDirty, onGenerate, onReset }: RbacToolbarProps) {
14
- const t = useTranslations();
15
-
16
- return (
17
- <div className="flex items-center justify-between rounded-lg border bg-card px-4 py-3">
18
- <div className="flex items-center gap-3">
19
- <h2 className="text-lg font-semibold">{t("rbac.title")}</h2>
20
- {isDirty && (
21
- <Badge variant="outline" className="border-amber-400 text-amber-600">
22
- {t("rbac.unsaved_changes")}
23
- </Badge>
24
- )}
25
- </div>
26
- <div className="flex items-center gap-2">
27
- <Button variant="outline" size="sm" onClick={onReset} disabled={!isDirty} className="gap-1">
28
- <RotateCcwIcon className="h-3.5 w-3.5" />
29
- {t("rbac.reset")}
30
- </Button>
31
- <Button size="sm" onClick={onGenerate} disabled={!isDirty} className="gap-1">
32
- <DownloadIcon className="h-3.5 w-3.5" />
33
- {t("rbac.generate_migration")}
34
- </Button>
35
- </div>
36
- </div>
37
- );
38
- }
39
-
40
- export { RbacToolbar };
@@ -1,180 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useRbacState } from "./useRbacState";
4
- import { FeatureInterface } from "../../feature";
5
- import { RoleInterface } from "../../role";
6
- import { PermissionMappingInterface } from "../data/PermissionMappingInterface";
7
- import { ModulePathsInterface } from "../data/ModulePathsInterface";
8
- import { ModuleInterface } from "../../module";
9
-
10
- const mockModule: ModuleInterface = {
11
- id: "mod-1",
12
- type: "modules",
13
- included: [],
14
- createdAt: new Date(),
15
- updatedAt: new Date(),
16
- identifier: "pipelines",
17
- name: "pipelines",
18
- permissions: { create: true, read: true, update: true, delete: false },
19
- } as ModuleInterface;
20
-
21
- const mockFeatures: FeatureInterface[] = [
22
- {
23
- id: "feat-1",
24
- type: "features",
25
- included: [],
26
- createdAt: new Date(),
27
- updatedAt: new Date(),
28
- identifier: "CRM",
29
- name: "CRM",
30
- isCore: false,
31
- modules: [mockModule],
32
- } as FeatureInterface,
33
- ];
34
-
35
- const mockRoles: RoleInterface[] = [
36
- {
37
- id: "role-1",
38
- type: "roles",
39
- included: [],
40
- createdAt: new Date(),
41
- updatedAt: new Date(),
42
- name: "Manager",
43
- description: "",
44
- isSelectable: true,
45
- requiredFeature: undefined,
46
- } as unknown as RoleInterface,
47
- {
48
- id: "role-2",
49
- type: "roles",
50
- included: [],
51
- createdAt: new Date(),
52
- updatedAt: new Date(),
53
- name: "Viewer",
54
- description: "",
55
- isSelectable: true,
56
- requiredFeature: undefined,
57
- } as unknown as RoleInterface,
58
- ];
59
-
60
- const mockPermissionMappings: PermissionMappingInterface[] = [];
61
-
62
- const mockModulePaths: ModulePathsInterface[] = [
63
- {
64
- id: "mod-1",
65
- type: "module-paths",
66
- included: [],
67
- createdAt: new Date(),
68
- updatedAt: new Date(),
69
- moduleId: "mod-1",
70
- paths: ["owner", "company.user"],
71
- } as unknown as ModulePathsInterface,
72
- ];
73
-
74
- function initHook() {
75
- const { result } = renderHook(() => useRbacState());
76
- act(() => {
77
- result.current.init(mockFeatures, mockRoles, mockPermissionMappings, mockModulePaths);
78
- });
79
- return result;
80
- }
81
-
82
- describe("useRbacState", () => {
83
- describe("Scenario: Initialize state from fetched data", () => {
84
- it("should initialize with features and roles", () => {
85
- const result = initHook();
86
-
87
- expect(result.current.original).not.toBeNull();
88
- expect(result.current.original!.features).toHaveLength(1);
89
- expect(result.current.original!.features[0].name).toBe("CRM");
90
- expect(result.current.isDirty).toBe(false);
91
- });
92
- });
93
-
94
- describe("Scenario: Set module default permission", () => {
95
- it("should update default permission and mark as dirty", () => {
96
- const result = initHook();
97
-
98
- act(() => {
99
- result.current.setModuleDefaultPermission("mod-1", "delete", true);
100
- });
101
-
102
- expect(result.current.getModuleDefaultPermission("mod-1", "delete")).toBe(true);
103
- expect(result.current.isDirty).toBe(true);
104
- });
105
- });
106
-
107
- describe("Scenario: Set role-specific permission override", () => {
108
- it("should set role permission for a specific module and action", () => {
109
- const result = initHook();
110
-
111
- act(() => {
112
- result.current.setRolePermission("role-1", "mod-1", "create", false);
113
- });
114
-
115
- expect(result.current.getRolePermission("role-1", "mod-1", "create")).toBe(false);
116
- expect(result.current.isDirty).toBe(true);
117
- });
118
-
119
- it("should support string path values for role permissions", () => {
120
- const result = initHook();
121
-
122
- act(() => {
123
- result.current.setRolePermission("role-2", "mod-1", "update", "owner");
124
- });
125
-
126
- expect(result.current.getRolePermission("role-2", "mod-1", "update")).toBe("owner");
127
- });
128
- });
129
-
130
- describe("Scenario: Clear role permission (inherit from default)", () => {
131
- it("should clear a role permission so it inherits from default", () => {
132
- const result = initHook();
133
-
134
- act(() => {
135
- result.current.setRolePermission("role-1", "mod-1", "create", false);
136
- });
137
-
138
- act(() => {
139
- result.current.clearRolePermission("role-1", "mod-1", "create");
140
- });
141
-
142
- expect(result.current.getRolePermission("role-1", "mod-1", "create")).toBeNull();
143
- });
144
- });
145
-
146
- describe("Scenario: Reset to initial state", () => {
147
- it("should reset all changes and clear dirty flag", () => {
148
- const result = initHook();
149
-
150
- act(() => {
151
- result.current.setModuleDefaultPermission("mod-1", "delete", true);
152
- });
153
-
154
- expect(result.current.isDirty).toBe(true);
155
-
156
- act(() => {
157
- result.current.reset();
158
- });
159
-
160
- expect(result.current.isDirty).toBe(false);
161
- expect(result.current.getModuleDefaultPermission("mod-1", "delete")).toBe(false);
162
- });
163
- });
164
-
165
- describe("Scenario: Get effective configuration for migration", () => {
166
- it("should return complete configuration with all changes applied", () => {
167
- const result = initHook();
168
-
169
- act(() => {
170
- result.current.setRolePermission("role-1", "mod-1", "delete", true);
171
- });
172
-
173
- const config = result.current.getEffectiveConfiguration();
174
- expect(config).not.toBeNull();
175
- expect(config!.features).toHaveLength(1);
176
- expect(config!.roles).toHaveLength(2);
177
- expect(config!.rolePermissionsMap).toBeInstanceOf(Map);
178
- });
179
- });
180
- });