@carlonicora/nextjs-jsonapi 1.78.0 → 1.80.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.
- package/dist/{AssistantMessageInterface-DS_tyJTV.d.ts → AssistantMessageInterface-BpEhx2pC.d.ts} +19 -2
- package/dist/{AssistantMessageInterface-D0Kwf8CR.d.mts → AssistantMessageInterface-DJ3Me16Y.d.mts} +19 -2
- package/dist/{AuthComponent-Blbs06ud.d.ts → AuthComponent-B6DIk8Vf.d.ts} +1 -1
- package/dist/{AuthComponent-huIaK5rm.d.mts → AuthComponent-BKI0ZbtD.d.mts} +1 -1
- package/dist/{BlockNoteEditor-JXK3JGKJ.mjs → BlockNoteEditor-3M5PD3BZ.mjs} +4 -4
- package/dist/{BlockNoteEditor-2G5UYALC.js → BlockNoteEditor-YLTPJPTV.js} +14 -14
- package/dist/{BlockNoteEditor-2G5UYALC.js.map → BlockNoteEditor-YLTPJPTV.js.map} +1 -1
- package/dist/RbacTypes-BTbr27Ew.d.mts +43 -0
- package/dist/RbacTypes-BTbr27Ew.d.ts +43 -0
- package/dist/{auth.interface-CQJ6A2Cj.d.ts → auth.interface-BBUgMZzs.d.ts} +1 -1
- package/dist/{auth.interface-Bdq7-8iV.d.mts → auth.interface-XYEREOD6.d.mts} +1 -1
- package/dist/billing/index.js +346 -346
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-ZEDB6JVB.js → chunk-4NOQNTFI.js} +1585 -1405
- package/dist/chunk-4NOQNTFI.js.map +1 -0
- package/dist/{chunk-I65SSQ5Z.mjs → chunk-6UMB5LTQ.mjs} +157 -7
- package/dist/chunk-6UMB5LTQ.mjs.map +1 -0
- package/dist/{chunk-FDJQRIMY.js → chunk-N4YZ45SK.js} +174 -24
- package/dist/chunk-N4YZ45SK.js.map +1 -0
- package/dist/{chunk-NB6TIKHK.mjs → chunk-NQV5RDCK.mjs} +2524 -2344
- package/dist/chunk-NQV5RDCK.mjs.map +1 -0
- package/dist/{chunk-NZOUEN67.mjs → chunk-PV5V6CVW.mjs} +38 -29
- package/dist/{chunk-NZOUEN67.mjs.map → chunk-PV5V6CVW.mjs.map} +1 -1
- package/dist/{chunk-X4YDETTD.js → chunk-ZEJSPTHS.js} +39 -30
- package/dist/chunk-ZEJSPTHS.js.map +1 -0
- package/dist/client/index.d.mts +6 -24
- package/dist/client/index.d.ts +6 -24
- package/dist/client/index.js +4 -10
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -9
- package/dist/components/index.d.mts +55 -39
- package/dist/components/index.d.ts +55 -39
- package/dist/components/index.js +4 -8
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +5 -9
- package/dist/{config-B3jKt9P7.d.ts → config-B5oBQVEA.d.ts} +1 -1
- package/dist/{config-DkHF61xA.d.mts → config-Bx_uh22h.d.mts} +1 -1
- package/dist/contexts/index.d.mts +41 -4
- package/dist/contexts/index.d.ts +41 -4
- package/dist/contexts/index.js +8 -4
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +7 -3
- package/dist/core/index.d.mts +51 -11
- package/dist/core/index.d.ts +51 -11
- package/dist/core/index.js +8 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +7 -1
- package/dist/index.d.mts +117 -20
- package/dist/index.d.ts +117 -20
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -2
- package/dist/{notification.interface-DG6obXUH.d.mts → notification.interface-DLZGtV7Z.d.mts} +1 -1
- package/dist/{notification.interface-DcSuc9CL.d.ts → notification.interface-aLEJbA_g.d.ts} +1 -1
- package/dist/{s3.service-DGilbikH.d.mts → s3.service-CVgLWaDc.d.mts} +2 -2
- package/dist/{s3.service-DjwEQJPe.d.ts → s3.service-SLlX0Zbz.d.ts} +2 -2
- package/dist/server/index.d.mts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/useDataListRetriever-BqJSFBck.d.mts +33 -0
- package/dist/useDataListRetriever-BqJSFBck.d.ts +33 -0
- package/dist/{useSocket-CmzVtg32.d.mts → useSocket-BkxHHujj.d.mts} +1 -1
- package/dist/{useSocket-8eUtnL7J.d.ts → useSocket-CMDjWFYm.d.ts} +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +0 -4
- package/src/components/index.ts +0 -3
- package/src/contexts/index.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/registry/ModuleRegistry.ts +2 -0
- package/src/features/assistant/components/parts/AssistantThread.tsx +1 -1
- package/src/features/assistant-message/AssistantMessageModule.ts +4 -0
- package/src/features/assistant-message/components/MessageItem.tsx +7 -7
- package/src/features/assistant-message/components/MessageList.tsx +1 -1
- package/src/features/assistant-message/components/__tests__/MessageItem.spec.tsx +11 -7
- package/src/features/assistant-message/components/index.ts +1 -0
- package/src/features/assistant-message/components/parts/MessageSourcesContainer.tsx +135 -0
- package/src/features/assistant-message/components/parts/MessageSourcesPanel.tsx +151 -0
- package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +29 -0
- package/src/features/assistant-message/components/parts/__tests__/MessageSourcesPanel.spec.tsx +70 -0
- package/src/features/assistant-message/components/parts/tabs/CitationsTab.tsx +105 -0
- package/src/features/assistant-message/components/parts/tabs/ContentsTab.tsx +88 -0
- package/src/features/assistant-message/components/parts/tabs/ReferencesTab.tsx +51 -0
- package/src/features/assistant-message/components/parts/tabs/SuggestedQuestionsTab.tsx +24 -0
- package/src/features/assistant-message/components/parts/tabs/UsersTab.tsx +142 -0
- package/src/features/assistant-message/data/AssistantMessage.ts +20 -0
- package/src/features/assistant-message/data/AssistantMessageInterface.ts +2 -0
- package/src/features/assistant-message/data/AssistantMessageService.ts +13 -4
- package/src/features/assistant-message/data/__tests__/AssistantMessage.citations.spec.ts +65 -0
- package/src/features/assistant-message/data/__tests__/AssistantMessage.spec.ts +8 -0
- package/src/features/chunk/ChunkModule.ts +18 -0
- package/src/features/chunk/data/Chunk.ts +49 -0
- package/src/features/chunk/data/ChunkInput.ts +3 -0
- package/src/features/chunk/data/ChunkInterface.ts +18 -0
- package/src/features/chunk/data/__tests__/Chunk.spec.ts +83 -0
- package/src/features/chunk/data/index.ts +3 -0
- package/src/features/chunk/index.ts +2 -0
- package/src/features/rbac/components/RbacContainer.tsx +318 -49
- package/src/features/rbac/components/RbacPermissionPicker.tsx +144 -121
- package/src/features/rbac/contexts/RbacContext.tsx +209 -0
- package/src/features/rbac/contexts/index.ts +1 -0
- package/src/features/rbac/data/RbacMatrixModel.ts +84 -0
- package/src/features/rbac/data/RbacService.ts +61 -33
- package/src/features/rbac/data/RbacTypes.ts +28 -0
- package/src/features/rbac/data/index.ts +1 -0
- package/src/features/rbac/index.ts +1 -10
- package/src/features/rbac/rbac.module.ts +13 -0
- package/dist/ModulePathsInterface-BrdqgteS.d.mts +0 -31
- package/dist/ModulePathsInterface-DJKs7s_s.d.ts +0 -31
- package/dist/chunk-FDJQRIMY.js.map +0 -1
- package/dist/chunk-I65SSQ5Z.mjs.map +0 -1
- package/dist/chunk-NB6TIKHK.mjs.map +0 -1
- package/dist/chunk-X4YDETTD.js.map +0 -1
- package/dist/chunk-ZEDB6JVB.js.map +0 -1
- package/dist/useRbacState-C88O-5L8.d.ts +0 -77
- package/dist/useRbacState-mqYiRp3J.d.mts +0 -77
- package/src/features/assistant-message/components/parts/ReferenceBadges.tsx +0 -46
- package/src/features/assistant-message/components/parts/SuggestedFollowUps.tsx +0 -52
- package/src/features/assistant-message/components/parts/__tests__/ReferenceBadges.spec.tsx +0 -59
- package/src/features/assistant-message/components/parts/__tests__/SuggestedFollowUps.spec.tsx +0 -29
- package/src/features/rbac/components/RbacFeatureSection.tsx +0 -66
- package/src/features/rbac/components/RbacModuleTable.tsx +0 -121
- package/src/features/rbac/components/RbacToolbar.tsx +0 -40
- package/src/features/rbac/hooks/useRbacState.test.ts +0 -180
- package/src/features/rbac/hooks/useRbacState.ts +0 -319
- package/src/features/rbac/utils/RbacMigrationGenerator.test.ts +0 -124
- package/src/features/rbac/utils/RbacMigrationGenerator.ts +0 -184
- /package/dist/{BlockNoteEditor-JXK3JGKJ.mjs.map → BlockNoteEditor-3M5PD3BZ.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,46 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { useTranslations } from "next-intl";
|
|
5
|
-
import type { ApiDataInterface } from "../../../../core";
|
|
6
|
-
import { usePageUrlGenerator } from "../../../../hooks";
|
|
7
|
-
import { ModuleRegistry } from "../../../../core/registry/ModuleRegistry";
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
references: ApiDataInterface[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function ReferenceBadges({ references }: Props) {
|
|
14
|
-
const t = useTranslations();
|
|
15
|
-
const generate = usePageUrlGenerator();
|
|
16
|
-
|
|
17
|
-
if (references.length === 0) return null;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="mt-2 flex flex-wrap items-center gap-2">
|
|
21
|
-
<span className="text-muted-foreground text-xs">{t("features.assistant.references_label")}</span>
|
|
22
|
-
{references.map((ref) => {
|
|
23
|
-
// ref.type is the JSON:API type string (same as module.name)
|
|
24
|
-
let module;
|
|
25
|
-
try {
|
|
26
|
-
module = ModuleRegistry.findByName(ref.type);
|
|
27
|
-
} catch {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
const href = generate({ page: module, id: ref.id });
|
|
31
|
-
return (
|
|
32
|
-
<Link
|
|
33
|
-
key={`${ref.type}/${ref.id}`}
|
|
34
|
-
href={href}
|
|
35
|
-
target="_blank"
|
|
36
|
-
rel="noopener noreferrer"
|
|
37
|
-
className="bg-background border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs"
|
|
38
|
-
>
|
|
39
|
-
<span className="text-muted-foreground text-[10px]">{module.name}</span>
|
|
40
|
-
<span className="font-medium">{ref.identifier}</span>
|
|
41
|
-
</Link>
|
|
42
|
-
);
|
|
43
|
-
})}
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { useTranslations } from "next-intl";
|
|
5
|
-
import { ChevronRight, ChevronDown } from "lucide-react";
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
questions: string[];
|
|
9
|
-
onSelect: (q: string) => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function SuggestedFollowUps({ questions, onSelect }: Props) {
|
|
13
|
-
const t = useTranslations();
|
|
14
|
-
const [open, setOpen] = useState(false);
|
|
15
|
-
if (questions.length === 0) return null;
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<div className="mt-2">
|
|
19
|
-
<button
|
|
20
|
-
type="button"
|
|
21
|
-
onClick={() => setOpen((v) => !v)}
|
|
22
|
-
className="text-primary inline-flex items-center gap-1 text-xs font-medium"
|
|
23
|
-
>
|
|
24
|
-
{open ? (
|
|
25
|
-
<>
|
|
26
|
-
<ChevronDown className="h-3 w-3" />
|
|
27
|
-
{t("features.assistant.hide_suggestions")}
|
|
28
|
-
</>
|
|
29
|
-
) : (
|
|
30
|
-
<>
|
|
31
|
-
<ChevronRight className="h-3 w-3" />
|
|
32
|
-
{t("features.assistant.show_suggestions", { count: questions.length })}
|
|
33
|
-
</>
|
|
34
|
-
)}
|
|
35
|
-
</button>
|
|
36
|
-
{open && (
|
|
37
|
-
<div className="mt-2 flex flex-col gap-1">
|
|
38
|
-
{questions.map((q) => (
|
|
39
|
-
<button
|
|
40
|
-
key={q}
|
|
41
|
-
type="button"
|
|
42
|
-
onClick={() => onSelect(q)}
|
|
43
|
-
className="border-border bg-muted/30 hover:bg-muted rounded-md border px-3 py-1.5 text-left text-sm"
|
|
44
|
-
>
|
|
45
|
-
{q}
|
|
46
|
-
</button>
|
|
47
|
-
))}
|
|
48
|
-
</div>
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import type { ApiDataInterface } from "../../../../../core";
|
|
4
|
-
import { AbstractApiData } from "../../../../../core/abstracts/AbstractApiData";
|
|
5
|
-
import { DataClassRegistry } from "../../../../../core/registry/DataClassRegistry";
|
|
6
|
-
import { ModuleRegistry } from "../../../../../core/registry/ModuleRegistry";
|
|
7
|
-
import type { ApiRequestDataTypeInterface } from "../../../../../core/interfaces/ApiRequestDataTypeInterface";
|
|
8
|
-
import { ReferenceBadges } from "../ReferenceBadges";
|
|
9
|
-
|
|
10
|
-
class TestAccount extends AbstractApiData {
|
|
11
|
-
static identifierFields: string[] = ["name"];
|
|
12
|
-
rehydrate(data: any): this {
|
|
13
|
-
super.rehydrate(data);
|
|
14
|
-
return this;
|
|
15
|
-
}
|
|
16
|
-
createJsonApi(): any {
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const testAccountModule = {
|
|
22
|
-
name: "test-accounts",
|
|
23
|
-
pageUrl: "/accounts",
|
|
24
|
-
model: TestAccount,
|
|
25
|
-
} as unknown as ApiRequestDataTypeInterface;
|
|
26
|
-
|
|
27
|
-
beforeAll(() => {
|
|
28
|
-
DataClassRegistry.clear();
|
|
29
|
-
DataClassRegistry.registerObjectClass(testAccountModule, TestAccount);
|
|
30
|
-
ModuleRegistry.register("TestAccount" as any, testAccountModule as any);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterAll(() => {
|
|
34
|
-
DataClassRegistry.clear();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
function makeRehydratedAccount({ id, name }: { id: string; name: string }): ApiDataInterface {
|
|
38
|
-
const acct = new TestAccount();
|
|
39
|
-
acct.rehydrate({
|
|
40
|
-
jsonApi: { type: "test-accounts", id, attributes: { name } },
|
|
41
|
-
included: [],
|
|
42
|
-
});
|
|
43
|
-
return acct as unknown as ApiDataInterface;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
describe("ReferenceBadges", () => {
|
|
47
|
-
it("renders a chip per reference with type label + identifier", () => {
|
|
48
|
-
const references = [makeRehydratedAccount({ id: "acc-1", name: "Acme" })];
|
|
49
|
-
render(<ReferenceBadges references={references} />);
|
|
50
|
-
const link = screen.getByRole("link", { name: /acme/i });
|
|
51
|
-
expect(link).toBeInTheDocument();
|
|
52
|
-
expect(link.getAttribute("href")).toContain("/accounts/acc-1");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("renders nothing for empty references", () => {
|
|
56
|
-
const { container } = render(<ReferenceBadges references={[]} />);
|
|
57
|
-
expect(container.firstChild).toBeNull();
|
|
58
|
-
});
|
|
59
|
-
});
|
package/src/features/assistant-message/components/parts/__tests__/SuggestedFollowUps.spec.tsx
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { SuggestedFollowUps } from "../SuggestedFollowUps";
|
|
5
|
-
|
|
6
|
-
describe("SuggestedFollowUps", () => {
|
|
7
|
-
it("is collapsed by default; toggling reveals the buttons", async () => {
|
|
8
|
-
render(<SuggestedFollowUps questions={["q1", "q2", "q3"]} onSelect={vi.fn()} />);
|
|
9
|
-
expect(screen.queryByRole("button", { name: "q1" })).not.toBeInTheDocument();
|
|
10
|
-
const toggle = screen.getByRole("button", { name: /show_suggestions/ });
|
|
11
|
-
await userEvent.click(toggle);
|
|
12
|
-
expect(screen.getByRole("button", { name: "q1" })).toBeInTheDocument();
|
|
13
|
-
expect(screen.getByRole("button", { name: "q2" })).toBeInTheDocument();
|
|
14
|
-
expect(screen.getByRole("button", { name: "q3" })).toBeInTheDocument();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("clicking a question calls onSelect immediately", async () => {
|
|
18
|
-
const onSelect = vi.fn();
|
|
19
|
-
render(<SuggestedFollowUps questions={["q1"]} onSelect={onSelect} />);
|
|
20
|
-
await userEvent.click(screen.getByRole("button", { name: /show_suggestions/ }));
|
|
21
|
-
await userEvent.click(screen.getByRole("button", { name: "q1" }));
|
|
22
|
-
expect(onSelect).toHaveBeenCalledWith("q1");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("renders nothing for empty list", () => {
|
|
26
|
-
const { container } = render(<SuggestedFollowUps questions={[]} onSelect={vi.fn()} />);
|
|
27
|
-
expect(container.firstChild).toBeNull();
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -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 };
|