@carlonicora/nextjs-jsonapi 1.79.0 → 1.81.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-DWnbd6J7.d.ts → AssistantMessageInterface-BpEhx2pC.d.ts} +18 -1
- package/dist/{AssistantMessageInterface-Mla6kgPe.d.mts → AssistantMessageInterface-DJ3Me16Y.d.mts} +18 -1
- package/dist/{BlockNoteEditor-6CBDTVKV.mjs → BlockNoteEditor-DCQA2PNW.mjs} +4 -4
- package/dist/{BlockNoteEditor-EH4HWI7H.js → BlockNoteEditor-ZISJ4KYX.js} +14 -14
- package/dist/{BlockNoteEditor-EH4HWI7H.js.map → BlockNoteEditor-ZISJ4KYX.js.map} +1 -1
- package/dist/billing/index.js +346 -346
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-BKM5U3DE.mjs → chunk-6UMB5LTQ.mjs} +98 -7
- package/dist/chunk-6UMB5LTQ.mjs.map +1 -0
- package/dist/{chunk-ENRSFVOS.mjs → chunk-FZFJLDJY.mjs} +1290 -701
- package/dist/chunk-FZFJLDJY.mjs.map +1 -0
- package/dist/{chunk-5IEWLLLD.js → chunk-N4YZ45SK.js} +115 -24
- package/dist/chunk-N4YZ45SK.js.map +1 -0
- package/dist/{chunk-MEWXQEVE.mjs → chunk-PV5V6CVW.mjs} +2 -2
- package/dist/{chunk-ZDP3MBUI.js → chunk-TZJFHXDU.js} +1329 -740
- package/dist/chunk-TZJFHXDU.js.map +1 -0
- package/dist/{chunk-TWDSDTHU.js → chunk-ZEJSPTHS.js} +7 -7
- package/dist/{chunk-TWDSDTHU.js.map → chunk-ZEJSPTHS.js.map} +1 -1
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +27 -7
- package/dist/components/index.d.ts +27 -7
- package/dist/components/index.js +8 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +7 -3
- package/dist/contexts/index.d.mts +1 -1
- package/dist/contexts/index.d.ts +1 -1
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +35 -3
- package/dist/core/index.d.ts +35 -3
- package/dist/core/index.js +6 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +5 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/registry/ModuleRegistry.ts +1 -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/RbacByRoleContainer.tsx +270 -0
- package/src/features/rbac/index.ts +1 -0
- package/dist/chunk-5IEWLLLD.js.map +0 -1
- package/dist/chunk-BKM5U3DE.mjs.map +0 -1
- package/dist/chunk-ENRSFVOS.mjs.map +0 -1
- package/dist/chunk-ZDP3MBUI.js.map +0 -1
- 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/dist/{BlockNoteEditor-6CBDTVKV.mjs.map → BlockNoteEditor-DCQA2PNW.mjs.map} +0 -0
- /package/dist/{chunk-MEWXQEVE.mjs.map → chunk-PV5V6CVW.mjs.map} +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ApiDataInterface } from "../../../core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Edge properties on the (:AssistantMessage)-[:CITES]->(:Chunk) relationship.
|
|
5
|
+
* Surfaced via _readIncludedWithMeta on AssistantMessage.citations.
|
|
6
|
+
*/
|
|
7
|
+
export interface ChunkRelationshipMeta {
|
|
8
|
+
relevance: number;
|
|
9
|
+
reason?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ChunkInterface extends ApiDataInterface {
|
|
13
|
+
get content(): string;
|
|
14
|
+
get nodeId(): string | undefined;
|
|
15
|
+
get nodeType(): string | undefined;
|
|
16
|
+
get imagePath(): string | undefined;
|
|
17
|
+
get source(): ApiDataInterface | undefined;
|
|
18
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { AbstractApiData } from "../../../../core/abstracts/AbstractApiData";
|
|
3
|
+
import { DataClassRegistry } from "../../../../core/registry/DataClassRegistry";
|
|
4
|
+
import { JsonApiHydratedDataInterface } from "../../../../core/interfaces/JsonApiHydratedDataInterface";
|
|
5
|
+
import { ApiRequestDataTypeInterface } from "../../../../core/interfaces/ApiRequestDataTypeInterface";
|
|
6
|
+
import { Chunk } from "../Chunk";
|
|
7
|
+
import { resolveReferenceableModules } from "../../../assistant/utils/resolveReferenceableModules";
|
|
8
|
+
|
|
9
|
+
vi.mock("../../../assistant/utils/resolveReferenceableModules", () => ({
|
|
10
|
+
resolveReferenceableModules: vi.fn(() => []),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
// Minimal AbstractApiData-derived class so RehydrationFactory can `new` it
|
|
14
|
+
// and call `instance.rehydrate(data)`.
|
|
15
|
+
class TestDocument extends AbstractApiData {
|
|
16
|
+
static identifierFields: string[] = ["name"];
|
|
17
|
+
|
|
18
|
+
rehydrate(data: JsonApiHydratedDataInterface): this {
|
|
19
|
+
super.rehydrate(data);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
createJsonApi(_data?: any): any {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const testDocumentModule: ApiRequestDataTypeInterface = {
|
|
29
|
+
name: "documents",
|
|
30
|
+
model: TestDocument,
|
|
31
|
+
} as any;
|
|
32
|
+
|
|
33
|
+
beforeAll(() => {
|
|
34
|
+
DataClassRegistry.clear();
|
|
35
|
+
DataClassRegistry.registerObjectClass(testDocumentModule, TestDocument);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterAll(() => {
|
|
39
|
+
DataClassRegistry.clear();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("Chunk model", () => {
|
|
43
|
+
it("rehydrates content, nodeId, nodeType from attributes", () => {
|
|
44
|
+
const chunk = new Chunk();
|
|
45
|
+
chunk.rehydrate({
|
|
46
|
+
jsonApi: {
|
|
47
|
+
id: "c1",
|
|
48
|
+
type: "chunks",
|
|
49
|
+
attributes: { content: "hello", nodeId: "doc-1", nodeType: "documents" },
|
|
50
|
+
} as any,
|
|
51
|
+
included: [],
|
|
52
|
+
});
|
|
53
|
+
expect(chunk.content).toBe("hello");
|
|
54
|
+
expect(chunk.nodeId).toBe("doc-1");
|
|
55
|
+
expect(chunk.nodeType).toBe("documents");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("resolves the source relationship polymorphically when matching module is registered", () => {
|
|
59
|
+
(resolveReferenceableModules as any).mockReturnValue([testDocumentModule]);
|
|
60
|
+
|
|
61
|
+
const chunk = new Chunk();
|
|
62
|
+
chunk.rehydrate({
|
|
63
|
+
jsonApi: {
|
|
64
|
+
id: "c1",
|
|
65
|
+
type: "chunks",
|
|
66
|
+
attributes: { content: "hello" },
|
|
67
|
+
relationships: { source: { data: { type: "documents", id: "doc-1" } } },
|
|
68
|
+
} as any,
|
|
69
|
+
included: [{ id: "doc-1", type: "documents", attributes: { name: "Manual v1" } }],
|
|
70
|
+
});
|
|
71
|
+
expect(chunk.source).toBeDefined();
|
|
72
|
+
expect((chunk.source as any).type).toBe("documents");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("returns undefined source when relationship is absent", () => {
|
|
76
|
+
const chunk = new Chunk();
|
|
77
|
+
chunk.rehydrate({
|
|
78
|
+
jsonApi: { id: "c1", type: "chunks", attributes: { content: "x" } } as any,
|
|
79
|
+
included: [],
|
|
80
|
+
});
|
|
81
|
+
expect(chunk.source).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { RoundPageContainer } from "@/components";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { Loader2Icon } from "lucide-react";
|
|
6
|
+
import { useTranslations } from "next-intl";
|
|
7
|
+
import { Fragment, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { useRbacContext } from "../contexts/RbacContext";
|
|
9
|
+
import { ACTION_TYPES, ActionType, PermissionValue, type PermToken } from "../data/RbacTypes";
|
|
10
|
+
import RbacPermissionCell from "./RbacPermissionCell";
|
|
11
|
+
import { RbacPermissionPicker } from "./RbacPermissionPicker";
|
|
12
|
+
|
|
13
|
+
function findToken(tokens: PermToken[] | undefined, action: ActionType): PermToken | undefined {
|
|
14
|
+
if (!tokens) return undefined;
|
|
15
|
+
return tokens.find((t) => t.action === action);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function cellValue(tokens: PermToken[] | undefined, action: ActionType): PermissionValue | undefined {
|
|
19
|
+
const tok = findToken(tokens, action);
|
|
20
|
+
if (!tok) return undefined;
|
|
21
|
+
return tok.scope;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ActivePicker {
|
|
25
|
+
moduleId: string;
|
|
26
|
+
rowKey: string;
|
|
27
|
+
action: ActionType;
|
|
28
|
+
isRoleColumn: boolean;
|
|
29
|
+
anchor: HTMLElement;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface CellButtonProps {
|
|
33
|
+
moduleId: string;
|
|
34
|
+
rowKey: string;
|
|
35
|
+
action: ActionType;
|
|
36
|
+
tokens: PermToken[] | undefined;
|
|
37
|
+
isRoleColumn: boolean;
|
|
38
|
+
onOpen: (picker: ActivePicker) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const CellButton = memo(function CellButton({
|
|
42
|
+
moduleId,
|
|
43
|
+
rowKey,
|
|
44
|
+
action,
|
|
45
|
+
tokens,
|
|
46
|
+
isRoleColumn,
|
|
47
|
+
onOpen,
|
|
48
|
+
}: CellButtonProps) {
|
|
49
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
50
|
+
const value = cellValue(tokens, action);
|
|
51
|
+
|
|
52
|
+
const handleClick = useCallback(() => {
|
|
53
|
+
if (!ref.current) return;
|
|
54
|
+
onOpen({ moduleId, rowKey, action, isRoleColumn, anchor: ref.current });
|
|
55
|
+
}, [onOpen, moduleId, rowKey, action, isRoleColumn]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div ref={ref}>
|
|
59
|
+
<RbacPermissionCell value={value} isRoleColumn={isRoleColumn} onClick={handleClick} />
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const ACTION_LABELS: Record<ActionType, string> = {
|
|
65
|
+
read: "Read",
|
|
66
|
+
create: "Create",
|
|
67
|
+
update: "Update",
|
|
68
|
+
delete: "Delete",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default function RbacByRoleContainer() {
|
|
72
|
+
const t = useTranslations();
|
|
73
|
+
const { matrix, modulePaths, loading, error, roleNames, moduleNames, updateCell, clearCell } = useRbacContext();
|
|
74
|
+
|
|
75
|
+
const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
|
|
76
|
+
const [activePicker, setActivePicker] = useState<ActivePicker | null>(null);
|
|
77
|
+
|
|
78
|
+
const openPicker = useCallback((picker: ActivePicker) => {
|
|
79
|
+
setActivePicker(picker);
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
const closePicker = useCallback(() => {
|
|
83
|
+
setActivePicker(null);
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
const handleSelectRole = useCallback((id: string) => {
|
|
87
|
+
setSelectedRoleId(id);
|
|
88
|
+
setActivePicker(null);
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
const sortedRoleIds = useMemo(() => {
|
|
92
|
+
if (!roleNames) return [];
|
|
93
|
+
return Object.keys(roleNames).sort((a, b) => (roleNames[a] ?? a).localeCompare(roleNames[b] ?? b));
|
|
94
|
+
}, [roleNames]);
|
|
95
|
+
|
|
96
|
+
const sortedModuleIds = useMemo(() => {
|
|
97
|
+
if (!matrix) return [];
|
|
98
|
+
return Object.keys(matrix).sort((a, b) => (moduleNames?.[a] ?? a).localeCompare(moduleNames?.[b] ?? b));
|
|
99
|
+
}, [matrix, moduleNames]);
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!selectedRoleId && sortedRoleIds.length > 0) {
|
|
103
|
+
setSelectedRoleId(sortedRoleIds[0]);
|
|
104
|
+
}
|
|
105
|
+
}, [selectedRoleId, sortedRoleIds]);
|
|
106
|
+
|
|
107
|
+
const activeValue = useMemo<PermissionValue | undefined>(() => {
|
|
108
|
+
if (!activePicker || !matrix) return undefined;
|
|
109
|
+
const block = matrix[activePicker.moduleId];
|
|
110
|
+
if (!block) return undefined;
|
|
111
|
+
const tokens =
|
|
112
|
+
activePicker.rowKey === "default" ? block.default : (block as Record<string, PermToken[]>)[activePicker.rowKey];
|
|
113
|
+
return cellValue(tokens, activePicker.action);
|
|
114
|
+
}, [activePicker, matrix]);
|
|
115
|
+
|
|
116
|
+
const activeSegments = useMemo<string[]>(() => {
|
|
117
|
+
if (!activePicker) return [];
|
|
118
|
+
return (modulePaths[activePicker.moduleId] as string[] | undefined) ?? [];
|
|
119
|
+
}, [activePicker, modulePaths]);
|
|
120
|
+
|
|
121
|
+
const handleSetValue = useCallback(
|
|
122
|
+
(value: PermissionValue) => {
|
|
123
|
+
if (!activePicker) return;
|
|
124
|
+
updateCell(activePicker.moduleId, activePicker.rowKey, activePicker.action, value);
|
|
125
|
+
},
|
|
126
|
+
[activePicker, updateCell],
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const handleClear = useCallback(() => {
|
|
130
|
+
if (!activePicker || !activePicker.isRoleColumn) return;
|
|
131
|
+
clearCell(activePicker.moduleId, activePicker.rowKey, activePicker.action);
|
|
132
|
+
}, [activePicker, clearCell]);
|
|
133
|
+
|
|
134
|
+
if (loading) {
|
|
135
|
+
return (
|
|
136
|
+
<RoundPageContainer fullWidth>
|
|
137
|
+
<div className="flex h-full items-center justify-center">
|
|
138
|
+
<Loader2Icon className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
139
|
+
</div>
|
|
140
|
+
</RoundPageContainer>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (error) {
|
|
145
|
+
return (
|
|
146
|
+
<RoundPageContainer fullWidth>
|
|
147
|
+
<div className="flex h-full items-center justify-center">
|
|
148
|
+
<p className="text-destructive">{error}</p>
|
|
149
|
+
</div>
|
|
150
|
+
</RoundPageContainer>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!matrix || !selectedRoleId) return null;
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<RoundPageContainer fullWidth forceHeader>
|
|
158
|
+
<div className="flex h-full w-full">
|
|
159
|
+
<aside className="w-60 shrink-0 overflow-y-auto border-r bg-muted/20">
|
|
160
|
+
<ul className="py-1">
|
|
161
|
+
{sortedRoleIds.map((id) => (
|
|
162
|
+
<li key={id}>
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
onClick={() => handleSelectRole(id)}
|
|
166
|
+
aria-current={id === selectedRoleId ? "true" : undefined}
|
|
167
|
+
className={cn(
|
|
168
|
+
"block w-full px-4 py-1.5 text-left text-sm hover:bg-muted",
|
|
169
|
+
id === selectedRoleId && "bg-muted font-medium text-foreground",
|
|
170
|
+
id !== selectedRoleId && "text-muted-foreground",
|
|
171
|
+
)}
|
|
172
|
+
>
|
|
173
|
+
{roleNames?.[id] ?? id}
|
|
174
|
+
</button>
|
|
175
|
+
</li>
|
|
176
|
+
))}
|
|
177
|
+
</ul>
|
|
178
|
+
</aside>
|
|
179
|
+
|
|
180
|
+
<section className="flex-1 overflow-y-auto p-4">
|
|
181
|
+
{sortedModuleIds.length > 0 ? (
|
|
182
|
+
<div className="rounded-lg border border-accent bg-card">
|
|
183
|
+
<div className="overflow-x-auto">
|
|
184
|
+
<table className="w-full text-sm">
|
|
185
|
+
<thead className="sticky top-0 z-10">
|
|
186
|
+
<tr className="border-b bg-muted/80 backdrop-blur-sm">
|
|
187
|
+
<th className="w-40 px-4 py-2 text-left text-xs font-medium text-muted-foreground">
|
|
188
|
+
{t("rbac.module")}
|
|
189
|
+
</th>
|
|
190
|
+
{ACTION_TYPES.map((action) => (
|
|
191
|
+
<th
|
|
192
|
+
key={action}
|
|
193
|
+
className="min-w-28 px-2 py-2 text-center text-xs font-medium text-muted-foreground"
|
|
194
|
+
>
|
|
195
|
+
{ACTION_LABELS[action]}
|
|
196
|
+
</th>
|
|
197
|
+
))}
|
|
198
|
+
</tr>
|
|
199
|
+
</thead>
|
|
200
|
+
<tbody>
|
|
201
|
+
{sortedModuleIds.map((moduleId) => {
|
|
202
|
+
const block = matrix[moduleId];
|
|
203
|
+
if (!block) return null;
|
|
204
|
+
const defaultTokens = block.default ?? [];
|
|
205
|
+
const roleTokens = (block as Record<string, PermToken[]>)[selectedRoleId];
|
|
206
|
+
const moduleLabel = moduleNames?.[moduleId] ?? moduleId;
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<Fragment key={moduleId}>
|
|
210
|
+
<tr className="border-b bg-muted/40">
|
|
211
|
+
<td
|
|
212
|
+
colSpan={ACTION_TYPES.length + 1}
|
|
213
|
+
className="px-4 py-1.5 text-xs font-bold text-muted-foreground"
|
|
214
|
+
>
|
|
215
|
+
{moduleLabel}
|
|
216
|
+
</td>
|
|
217
|
+
</tr>
|
|
218
|
+
<tr className="border-b bg-muted/20">
|
|
219
|
+
<td className="px-4 py-1 text-xs text-muted-foreground">{t("rbac.defaults")}</td>
|
|
220
|
+
{ACTION_TYPES.map((action) => (
|
|
221
|
+
<td key={action} className="px-2 py-1">
|
|
222
|
+
<RbacPermissionCell value={cellValue(defaultTokens, action)} />
|
|
223
|
+
</td>
|
|
224
|
+
))}
|
|
225
|
+
</tr>
|
|
226
|
+
<tr className="border-b last:border-b-0">
|
|
227
|
+
<td className="px-4 py-1 text-xs font-medium text-muted-foreground">
|
|
228
|
+
{roleNames?.[selectedRoleId] ?? selectedRoleId}
|
|
229
|
+
</td>
|
|
230
|
+
{ACTION_TYPES.map((action) => (
|
|
231
|
+
<td key={action} className="px-2 py-1">
|
|
232
|
+
<CellButton
|
|
233
|
+
moduleId={moduleId}
|
|
234
|
+
rowKey={selectedRoleId}
|
|
235
|
+
action={action}
|
|
236
|
+
tokens={roleTokens}
|
|
237
|
+
isRoleColumn={true}
|
|
238
|
+
onOpen={openPicker}
|
|
239
|
+
/>
|
|
240
|
+
</td>
|
|
241
|
+
))}
|
|
242
|
+
</tr>
|
|
243
|
+
</Fragment>
|
|
244
|
+
);
|
|
245
|
+
})}
|
|
246
|
+
</tbody>
|
|
247
|
+
</table>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
) : (
|
|
251
|
+
<p className="text-muted-foreground text-sm">{t("rbac.select_role_prompt")}</p>
|
|
252
|
+
)}
|
|
253
|
+
</section>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<RbacPermissionPicker
|
|
257
|
+
open={!!activePicker}
|
|
258
|
+
anchor={activePicker?.anchor ?? null}
|
|
259
|
+
value={activeValue}
|
|
260
|
+
isRoleColumn={activePicker?.isRoleColumn ?? false}
|
|
261
|
+
knownSegments={activeSegments}
|
|
262
|
+
onSetValue={handleSetValue}
|
|
263
|
+
onClear={activePicker?.isRoleColumn ? handleClear : undefined}
|
|
264
|
+
onClose={closePicker}
|
|
265
|
+
/>
|
|
266
|
+
</RoundPageContainer>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export { RbacByRoleContainer };
|
|
@@ -3,6 +3,7 @@ export * from "./data";
|
|
|
3
3
|
|
|
4
4
|
// Components
|
|
5
5
|
export { RbacContainer } from "./components/RbacContainer";
|
|
6
|
+
export { RbacByRoleContainer } from "./components/RbacByRoleContainer";
|
|
6
7
|
export { RbacPermissionCell } from "./components/RbacPermissionCell";
|
|
7
8
|
export { RbacPermissionPicker } from "./components/RbacPermissionPicker";
|
|
8
9
|
|