hazo_auth 3.0.4 → 4.1.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/README.md +228 -8
- package/SETUP_CHECKLIST.md +370 -0
- package/dist/app/api/hazo_auth/me/route.d.ts +3 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +9 -1
- package/dist/components/layouts/my_settings/components/profile_picture_library_tab.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +2 -2
- package/dist/components/layouts/profile_stamp_test/index.d.ts +10 -0
- package/dist/components/layouts/profile_stamp_test/index.d.ts.map +1 -0
- package/dist/components/layouts/profile_stamp_test/index.js +51 -0
- package/dist/components/layouts/rbac_test/index.d.ts +15 -0
- package/dist/components/layouts/rbac_test/index.d.ts.map +1 -0
- package/dist/components/layouts/rbac_test/index.js +378 -0
- package/dist/components/layouts/shared/components/password_field.js +1 -1
- package/dist/components/layouts/shared/components/profile_stamp.d.ts +58 -0
- package/dist/components/layouts/shared/components/profile_stamp.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/profile_stamp.js +72 -0
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +2 -2
- package/dist/components/layouts/shared/components/two_column_auth_layout.js +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts +3 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.js +4 -0
- package/dist/components/layouts/shared/index.d.ts +2 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +1 -0
- package/dist/components/layouts/user_management/components/roles_matrix.d.ts +2 -3
- package/dist/components/layouts/user_management/components/roles_matrix.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/roles_matrix.js +133 -8
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts +12 -0
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts.map +1 -0
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +291 -0
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts +13 -0
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts.map +1 -0
- package/dist/components/layouts/user_management/components/scope_labels_tab.js +158 -0
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts +11 -0
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -0
- package/dist/components/layouts/user_management/components/user_scopes_tab.js +267 -0
- package/dist/components/layouts/user_management/index.d.ts +9 -2
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +22 -6
- package/dist/components/ui/hover-card.d.ts +7 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/hover-card.js +29 -0
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +1 -0
- package/dist/components/ui/select.d.ts +14 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +59 -0
- package/dist/components/ui/tree-view.d.ts +108 -0
- package/dist/components/ui/tree-view.d.ts.map +1 -0
- package/dist/components/ui/tree-view.js +194 -0
- package/dist/lib/auth/auth_types.d.ts +45 -0
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +13 -0
- package/dist/lib/auth/hazo_get_auth.server.d.ts +4 -2
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +107 -3
- package/dist/lib/auth/scope_cache.d.ts +92 -0
- package/dist/lib/auth/scope_cache.d.ts.map +1 -0
- package/dist/lib/auth/scope_cache.js +171 -0
- package/dist/lib/scope_hierarchy_config.server.d.ts +39 -0
- package/dist/lib/scope_hierarchy_config.server.d.ts.map +1 -0
- package/dist/lib/scope_hierarchy_config.server.js +96 -0
- package/dist/lib/services/email_service.d.ts.map +1 -1
- package/dist/lib/services/email_service.js +7 -2
- package/dist/lib/services/profile_picture_service.d.ts +1 -7
- package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
- package/dist/lib/services/profile_picture_service.js +77 -32
- package/dist/lib/services/registration_service.js +1 -1
- package/dist/lib/services/scope_labels_service.d.ts +48 -0
- package/dist/lib/services/scope_labels_service.d.ts.map +1 -0
- package/dist/lib/services/scope_labels_service.js +277 -0
- package/dist/lib/services/scope_service.d.ts +114 -0
- package/dist/lib/services/scope_service.d.ts.map +1 -0
- package/dist/lib/services/scope_service.js +582 -0
- package/dist/lib/services/user_scope_service.d.ts +74 -0
- package/dist/lib/services/user_scope_service.d.ts.map +1 -0
- package/dist/lib/services/user_scope_service.js +415 -0
- package/hazo_auth_config.example.ini +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile_stamp.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/profile_stamp.tsx"],"names":[],"mappings":"AAcA;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/B;;OAEG;IACH,aAAa,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAC1C;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAgB,EAChB,aAAkB,EAClB,SAAS,EACT,SAAgB,EAChB,UAAiB,GAClB,EAAE,iBAAiB,2CAwInB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// file_description: ProfileStamp component - circular profile picture with hover card showing user details
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
// section: imports
|
|
6
|
+
import { Avatar, AvatarImage, AvatarFallback } from "../../../ui/avatar";
|
|
7
|
+
import { HoverCard, HoverCardTrigger, HoverCardContent, } from "../../../ui/hover-card";
|
|
8
|
+
import { use_auth_status } from "../hooks/use_auth_status";
|
|
9
|
+
// section: component
|
|
10
|
+
/**
|
|
11
|
+
* ProfileStamp component - displays a circular profile picture with a hover card
|
|
12
|
+
* showing the user's name, email, and any custom fields.
|
|
13
|
+
*
|
|
14
|
+
* Use this component to add profile attribution to notes, comments, or any
|
|
15
|
+
* user-generated content in your application.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Basic usage
|
|
19
|
+
* <ProfileStamp />
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // With custom fields
|
|
23
|
+
* <ProfileStamp
|
|
24
|
+
* size="lg"
|
|
25
|
+
* custom_fields={[
|
|
26
|
+
* { label: "Role", value: "Admin" },
|
|
27
|
+
* { label: "Department", value: "Engineering" }
|
|
28
|
+
* ]}
|
|
29
|
+
* />
|
|
30
|
+
*/
|
|
31
|
+
export function ProfileStamp({ size = "default", custom_fields = [], className, show_name = true, show_email = true, }) {
|
|
32
|
+
const authStatus = use_auth_status();
|
|
33
|
+
// Avatar size classes
|
|
34
|
+
const avatarSizeClasses = {
|
|
35
|
+
sm: "h-6 w-6",
|
|
36
|
+
default: "h-8 w-8",
|
|
37
|
+
lg: "h-10 w-10",
|
|
38
|
+
};
|
|
39
|
+
// Get initials from name or email
|
|
40
|
+
const getInitials = () => {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
if (authStatus.name) {
|
|
43
|
+
const parts = authStatus.name.trim().split(" ");
|
|
44
|
+
if (parts.length >= 2) {
|
|
45
|
+
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
46
|
+
}
|
|
47
|
+
return ((_a = authStatus.name[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || "";
|
|
48
|
+
}
|
|
49
|
+
if (authStatus.email) {
|
|
50
|
+
return ((_b = authStatus.email[0]) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || "";
|
|
51
|
+
}
|
|
52
|
+
return "?";
|
|
53
|
+
};
|
|
54
|
+
// Show loading skeleton
|
|
55
|
+
if (authStatus.loading) {
|
|
56
|
+
return (_jsx("div", { className: `cls_profile_stamp inline-block ${className || ""}`, children: _jsx("div", { className: `${avatarSizeClasses[size]} rounded-full bg-[var(--hazo-bg-emphasis)] animate-pulse`, "aria-label": "Loading profile" }) }));
|
|
57
|
+
}
|
|
58
|
+
// Not authenticated - show placeholder
|
|
59
|
+
if (!authStatus.authenticated) {
|
|
60
|
+
return (_jsx("div", { className: `cls_profile_stamp inline-block ${className || ""}`, children: _jsx(Avatar, { className: `cls_profile_stamp_avatar ${avatarSizeClasses[size]}`, children: _jsx(AvatarFallback, { className: "cls_profile_stamp_fallback bg-[var(--hazo-bg-emphasis)] text-[var(--hazo-text-muted)]", children: "?" }) }) }));
|
|
61
|
+
}
|
|
62
|
+
// Check if there's any content to show in the hover card
|
|
63
|
+
const hasHoverContent = (show_name && authStatus.name) ||
|
|
64
|
+
(show_email && authStatus.email) ||
|
|
65
|
+
custom_fields.length > 0;
|
|
66
|
+
// If no hover content, just show the avatar without hover card
|
|
67
|
+
if (!hasHoverContent) {
|
|
68
|
+
return (_jsx("div", { className: `cls_profile_stamp inline-block ${className || ""}`, children: _jsxs(Avatar, { className: `cls_profile_stamp_avatar ${avatarSizeClasses[size]}`, children: [_jsx(AvatarImage, { src: authStatus.profile_picture_url, alt: authStatus.name ? `Profile picture of ${authStatus.name}` : "Profile picture", className: "cls_profile_stamp_image" }), _jsx(AvatarFallback, { className: "cls_profile_stamp_fallback bg-[var(--hazo-bg-emphasis)] text-[var(--hazo-text-muted)]", children: getInitials() })] }) }));
|
|
69
|
+
}
|
|
70
|
+
// Authenticated with hover content - show avatar with hover card
|
|
71
|
+
return (_jsx("div", { className: `cls_profile_stamp inline-block ${className || ""}`, children: _jsxs(HoverCard, { children: [_jsx(HoverCardTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "cls_profile_stamp_trigger cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary rounded-full", "aria-label": `View profile information for ${authStatus.name || authStatus.email || "user"}`, children: _jsxs(Avatar, { className: `cls_profile_stamp_avatar ${avatarSizeClasses[size]}`, children: [_jsx(AvatarImage, { src: authStatus.profile_picture_url, alt: authStatus.name ? `Profile picture of ${authStatus.name}` : "Profile picture", className: "cls_profile_stamp_image" }), _jsx(AvatarFallback, { className: "cls_profile_stamp_fallback bg-[var(--hazo-bg-emphasis)] text-[var(--hazo-text-muted)]", children: getInitials() })] }) }) }), _jsx(HoverCardContent, { align: "start", className: "cls_profile_stamp_hover_card w-auto min-w-[200px]", children: _jsxs("div", { className: "cls_profile_stamp_hover_content flex flex-col gap-2", children: [show_name && authStatus.name && (_jsx("div", { className: "cls_profile_stamp_name text-sm font-semibold text-foreground", children: authStatus.name })), show_email && authStatus.email && (_jsx("div", { className: "cls_profile_stamp_email text-sm text-muted-foreground", children: authStatus.email })), custom_fields.length > 0 && (_jsx("div", { className: "cls_profile_stamp_custom_fields flex flex-col gap-1 pt-2 border-t", children: custom_fields.map((field, index) => (_jsxs("div", { className: "cls_profile_stamp_custom_field flex justify-between gap-4 text-sm", children: [_jsxs("span", { className: "cls_profile_stamp_field_label text-muted-foreground", children: [field.label, ":"] }), _jsx("span", { className: "cls_profile_stamp_field_value text-foreground font-medium", children: field.value })] }, `${field.label}-${index}`))) }))] }) })] }) }));
|
|
72
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/sidebar_layout_wrapper.tsx"],"names":[],"mappings":"AAwBA,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,
|
|
1
|
+
{"version":3,"file":"sidebar_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/sidebar_layout_wrapper.tsx"],"names":[],"mappings":"AAwBA,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,2CAiM3E"}
|
|
@@ -5,11 +5,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
// section: imports
|
|
6
6
|
import Link from "next/link";
|
|
7
7
|
import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, SidebarInset, } from "../../../ui/sidebar";
|
|
8
|
-
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User } from "lucide-react";
|
|
8
|
+
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User, ShieldCheck, CircleUserRound } from "lucide-react";
|
|
9
9
|
import { use_auth_status } from "../hooks/use_auth_status";
|
|
10
10
|
import { ProfilePicMenu } from "./profile_pic_menu";
|
|
11
11
|
// section: component
|
|
12
12
|
export function SidebarLayoutWrapper({ children }) {
|
|
13
13
|
const authStatus = use_auth_status();
|
|
14
|
-
return (_jsx(SidebarProvider, { children: _jsxs("div", { className: "cls_sidebar_layout_wrapper flex min-h-screen w-full", children: [_jsxs(Sidebar, { children: [_jsx(SidebarHeader, { className: "cls_sidebar_layout_header", children: _jsx("div", { className: "cls_sidebar_layout_title flex items-center gap-2 px-2 py-4", children: _jsx("h1", { className: "cls_sidebar_layout_title_text text-lg font-semibold text-sidebar-foreground", children: "hazo auth" }) }) }), _jsxs(SidebarContent, { className: "cls_sidebar_layout_content", children: [_jsxs(SidebarGroup, { className: "cls_sidebar_layout_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Test components" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_test_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_login_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/login", className: "cls_sidebar_layout_test_login_link flex items-center gap-2", "aria-label": "Test login layout component", children: [_jsx(LogIn, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test login" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_register_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/register", className: "cls_sidebar_layout_test_register_link flex items-center gap-2", "aria-label": "Test register layout component", children: [_jsx(UserPlus, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test register" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_forgot_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/forgot_password", className: "cls_sidebar_layout_test_forgot_password_link flex items-center gap-2", "aria-label": "Test forgot password layout component", children: [_jsx(KeyRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test forgot password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_reset_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/reset_password", className: "cls_sidebar_layout_test_reset_password_link flex items-center gap-2", "aria-label": "Test reset password layout component", children: [_jsx(Key, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test reset password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_email_verification_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/verify_email", className: "cls_sidebar_layout_test_email_verification_link flex items-center gap-2", "aria-label": "Test email verification layout component", children: [_jsx(MailCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test email verification" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_sqlite_admin_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_connect/sqlite_admin", className: "cls_sidebar_layout_sqlite_admin_link flex items-center gap-2", "aria-label": "Open SQLite admin UI to browse and edit database", children: [_jsx(Database, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "SQLite Admin" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_user_management_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/user_management", className: "cls_sidebar_layout_user_management_link flex items-center gap-2", "aria-label": "Open User Management to manage users, roles, and permissions", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "User Management" })] }) }) })] })] }), _jsx(ProfilePicMenu, { variant: "sidebar", avatar_size: "sm", className: "cls_sidebar_layout_profile_menu", sidebar_group_label: "Account" }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_resources_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Resources" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_resources_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_storybook_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "http://localhost:6006", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_storybook_link flex items-center gap-2", "aria-label": "Open Storybook preview for reusable components", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Storybook" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_docs_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "https://ui.shadcn.com/docs", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_docs_link flex items-center gap-2", "aria-label": "Review shadcn documentation for styling guidance", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Shadcn docs" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) })] })] })] })] }), _jsxs(SidebarInset, { className: "cls_sidebar_layout_inset", children: [_jsxs("header", { className: "cls_sidebar_layout_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4", children: [_jsx(SidebarTrigger, { className: "cls_sidebar_layout_trigger" }), _jsx("div", { className: "cls_sidebar_layout_main_header_content flex flex-1 items-center gap-2", children: _jsx("h2", { className: "cls_sidebar_layout_main_title text-lg font-semibold text-foreground", children: "hazo reusable ui library workspace" }) }), _jsx(ProfilePicMenu, { className: "cls_sidebar_layout_auth_status", avatar_size: "sm" })] }), _jsx("main", { className: "cls_sidebar_layout_main_content flex flex-1 items-center justify-center p-6", children: children })] })] }) }));
|
|
14
|
+
return (_jsx(SidebarProvider, { children: _jsxs("div", { className: "cls_sidebar_layout_wrapper flex min-h-screen w-full", children: [_jsxs(Sidebar, { children: [_jsx(SidebarHeader, { className: "cls_sidebar_layout_header", children: _jsx("div", { className: "cls_sidebar_layout_title flex items-center gap-2 px-2 py-4", children: _jsx("h1", { className: "cls_sidebar_layout_title_text text-lg font-semibold text-sidebar-foreground", children: "hazo auth" }) }) }), _jsxs(SidebarContent, { className: "cls_sidebar_layout_content", children: [_jsxs(SidebarGroup, { className: "cls_sidebar_layout_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Test components" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_test_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_login_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/login", className: "cls_sidebar_layout_test_login_link flex items-center gap-2", "aria-label": "Test login layout component", children: [_jsx(LogIn, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test login" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_register_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/register", className: "cls_sidebar_layout_test_register_link flex items-center gap-2", "aria-label": "Test register layout component", children: [_jsx(UserPlus, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test register" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_forgot_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/forgot_password", className: "cls_sidebar_layout_test_forgot_password_link flex items-center gap-2", "aria-label": "Test forgot password layout component", children: [_jsx(KeyRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test forgot password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_reset_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/reset_password", className: "cls_sidebar_layout_test_reset_password_link flex items-center gap-2", "aria-label": "Test reset password layout component", children: [_jsx(Key, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test reset password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_email_verification_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/verify_email", className: "cls_sidebar_layout_test_email_verification_link flex items-center gap-2", "aria-label": "Test email verification layout component", children: [_jsx(MailCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test email verification" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_sqlite_admin_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_connect/sqlite_admin", className: "cls_sidebar_layout_sqlite_admin_link flex items-center gap-2", "aria-label": "Open SQLite admin UI to browse and edit database", children: [_jsx(Database, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "SQLite Admin" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_user_management_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/user_management", className: "cls_sidebar_layout_user_management_link flex items-center gap-2", "aria-label": "Open User Management to manage users, roles, and permissions", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "User Management" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_rbac_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/rbac_test", className: "cls_sidebar_layout_rbac_test_link flex items-center gap-2", "aria-label": "Test RBAC and HRBAC access control", children: [_jsx(ShieldCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "RBAC/HRBAC Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_profile_stamp_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/profile_stamp_test", className: "cls_sidebar_layout_profile_stamp_test_link flex items-center gap-2", "aria-label": "Test ProfileStamp component", children: [_jsx(CircleUserRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "ProfileStamp Test" })] }) }) })] })] }), _jsx(ProfilePicMenu, { variant: "sidebar", avatar_size: "sm", className: "cls_sidebar_layout_profile_menu", sidebar_group_label: "Account" }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_resources_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Resources" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_resources_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_storybook_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "http://localhost:6006", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_storybook_link flex items-center gap-2", "aria-label": "Open Storybook preview for reusable components", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Storybook" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_docs_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "https://ui.shadcn.com/docs", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_docs_link flex items-center gap-2", "aria-label": "Review shadcn documentation for styling guidance", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Shadcn docs" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) })] })] })] })] }), _jsxs(SidebarInset, { className: "cls_sidebar_layout_inset", children: [_jsxs("header", { className: "cls_sidebar_layout_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4", children: [_jsx(SidebarTrigger, { className: "cls_sidebar_layout_trigger" }), _jsx("div", { className: "cls_sidebar_layout_main_header_content flex flex-1 items-center gap-2", children: _jsx("h2", { className: "cls_sidebar_layout_main_title text-lg font-semibold text-foreground", children: "hazo reusable ui library workspace" }) }), _jsx(ProfilePicMenu, { className: "cls_sidebar_layout_auth_status", avatar_size: "sm" })] }), _jsx("main", { className: "cls_sidebar_layout_main_content flex flex-1 items-center justify-center p-6", children: children })] })] }) }));
|
|
15
15
|
}
|
|
@@ -4,5 +4,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
import { VisualPanel } from "./visual_panel";
|
|
5
5
|
// section: component
|
|
6
6
|
export function TwoColumnAuthLayout({ imageSrc, imageAlt, imageBackgroundColor, formContent, className, visualPanelClassName, formContainerClassName, }) {
|
|
7
|
-
return (_jsxs("div", { className: `cls_two_column_auth_layout mx-
|
|
7
|
+
return (_jsxs("div", { className: `cls_two_column_auth_layout mx-4 my-8 grid w-full max-w-5xl grid-cols-1 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm md:mx-auto md:my-12 md:grid-cols-2 md:min-h-[520px] ${className !== null && className !== void 0 ? className : ""}`, children: [_jsx(VisualPanel, { imageSrc: imageSrc, imageAlt: imageAlt, backgroundColor: imageBackgroundColor, className: visualPanelClassName }), _jsx("div", { className: `cls_two_column_auth_layout_form_container flex flex-col gap-6 p-8 ${formContainerClassName !== null && formContainerClassName !== void 0 ? formContainerClassName : ""}`, children: formContent })] }));
|
|
8
8
|
}
|
|
@@ -6,6 +6,9 @@ export type AuthStatusData = {
|
|
|
6
6
|
email_verified?: boolean;
|
|
7
7
|
last_logon?: string;
|
|
8
8
|
profile_picture_url?: string;
|
|
9
|
+
profile_image?: string;
|
|
10
|
+
avatar_url?: string;
|
|
11
|
+
image?: string;
|
|
9
12
|
profile_source?: "upload" | "library" | "gravatar" | "custom";
|
|
10
13
|
permissions?: string[];
|
|
11
14
|
permission_ok?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_auth_status.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/hooks/use_auth_status.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"use_auth_status.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/hooks/use_auth_status.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC9D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG;IACxC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAMF;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAIlD;AAGD,wBAAgB,eAAe,IAAI,UAAU,CAuE5C"}
|
|
@@ -40,6 +40,10 @@ export function use_auth_status() {
|
|
|
40
40
|
email_verified: data.email_verified,
|
|
41
41
|
last_logon: data.last_logon,
|
|
42
42
|
profile_picture_url: data.profile_picture_url,
|
|
43
|
+
// Populate aliases for profile_picture_url
|
|
44
|
+
profile_image: data.profile_image,
|
|
45
|
+
avatar_url: data.avatar_url,
|
|
46
|
+
image: data.image,
|
|
43
47
|
profile_source: data.profile_source,
|
|
44
48
|
permissions: data.permissions || [],
|
|
45
49
|
permission_ok: (_a = data.permission_ok) !== null && _a !== void 0 ? _a : true,
|
|
@@ -6,6 +6,8 @@ export { FormHeader } from "./components/form_header";
|
|
|
6
6
|
export { LogoutButton } from "./components/logout_button";
|
|
7
7
|
export { PasswordField } from "./components/password_field";
|
|
8
8
|
export { ProfilePicMenu } from "./components/profile_pic_menu";
|
|
9
|
+
export { ProfileStamp } from "./components/profile_stamp";
|
|
10
|
+
export type { ProfileStampProps, ProfileStampCustomField } from "./components/profile_stamp";
|
|
9
11
|
export { StandaloneLayoutWrapper } from "./components/standalone_layout_wrapper";
|
|
10
12
|
export { TwoColumnAuthLayout } from "./components/two_column_auth_layout";
|
|
11
13
|
export { UnauthorizedGuard } from "./components/unauthorized_guard";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/shared/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/shared/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACjF,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,cAAc,+BAA+B,CAAC;AAG9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,cAAc,oBAAoB,CAAC"}
|
|
@@ -10,6 +10,7 @@ export { LogoutButton } from "./components/logout_button";
|
|
|
10
10
|
export { PasswordField } from "./components/password_field";
|
|
11
11
|
// ProfilePicMenuWrapper - NOT exported (server component - imports .server files)
|
|
12
12
|
export { ProfilePicMenu } from "./components/profile_pic_menu";
|
|
13
|
+
export { ProfileStamp } from "./components/profile_stamp";
|
|
13
14
|
// SidebarLayoutWrapper - NOT exported (test workspace component only)
|
|
14
15
|
export { StandaloneLayoutWrapper } from "./components/standalone_layout_wrapper";
|
|
15
16
|
export { TwoColumnAuthLayout } from "./components/two_column_auth_layout";
|
|
@@ -18,9 +18,8 @@ export type RolesMatrixProps = {
|
|
|
18
18
|
className?: string;
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
-
* Roles matrix component - reusable internal component for roles-permissions
|
|
22
|
-
* Shows
|
|
23
|
-
* Checkboxes in cells indicate role-permission mappings
|
|
21
|
+
* Roles matrix component - reusable internal component for roles-permissions management
|
|
22
|
+
* Shows roles with permission tags and an edit button to modify permissions via dialog
|
|
24
23
|
* Changes are stored locally and only saved when Save button is pressed
|
|
25
24
|
* @param props - Component props including button enable flags and save callback
|
|
26
25
|
* @returns Roles matrix component
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roles_matrix.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/roles_matrix.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;QACX,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"roles_matrix.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/roles_matrix.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;QACX,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAQF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,kBAAyB,EACzB,2BAAkC,EAClC,qBAA6B,EAC7B,gBAAuB,EACvB,OAAO,EACP,MAAM,EACN,QAAQ,EACR,eAAe,EACf,SAAS,GACV,EAAE,gBAAgB,2CA4xBlB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// file_description: internal reusable component for roles-permissions
|
|
1
|
+
// file_description: internal reusable component for roles-permissions management with tag-based UI
|
|
2
2
|
// section: client_directive
|
|
3
3
|
"use client";
|
|
4
4
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -10,30 +10,36 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "
|
|
|
10
10
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../../../ui/dialog";
|
|
11
11
|
import { Input } from "../../../ui/input";
|
|
12
12
|
import { Label } from "../../../ui/label";
|
|
13
|
-
import { Plus, Loader2, CircleCheck, CircleX } from "lucide-react";
|
|
13
|
+
import { Plus, Loader2, CircleCheck, CircleX, Pencil } from "lucide-react";
|
|
14
14
|
import { toast } from "sonner";
|
|
15
15
|
import { Avatar, AvatarImage, AvatarFallback } from "../../../ui/avatar";
|
|
16
16
|
import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider";
|
|
17
17
|
// section: component
|
|
18
18
|
/**
|
|
19
|
-
* Roles matrix component - reusable internal component for roles-permissions
|
|
20
|
-
* Shows
|
|
21
|
-
* Checkboxes in cells indicate role-permission mappings
|
|
19
|
+
* Roles matrix component - reusable internal component for roles-permissions management
|
|
20
|
+
* Shows roles with permission tags and an edit button to modify permissions via dialog
|
|
22
21
|
* Changes are stored locally and only saved when Save button is pressed
|
|
23
22
|
* @param props - Component props including button enable flags and save callback
|
|
24
23
|
* @returns Roles matrix component
|
|
25
24
|
*/
|
|
26
25
|
export function RolesMatrix({ add_button_enabled = true, role_name_selection_enabled = true, permissions_read_only = false, show_save_cancel = true, user_id, onSave, onCancel, onRoleSelection, className, }) {
|
|
26
|
+
var _a;
|
|
27
27
|
const { apiBasePath } = useHazoAuthConfig();
|
|
28
28
|
const [roles, setRoles] = useState([]);
|
|
29
29
|
const [originalRoles, setOriginalRoles] = useState([]);
|
|
30
30
|
const [permissions, setPermissions] = useState([]);
|
|
31
|
+
const [permissionsWithDescriptions, setPermissionsWithDescriptions] = useState([]);
|
|
31
32
|
const [loading, setLoading] = useState(true);
|
|
32
33
|
const [saving, setSaving] = useState(false);
|
|
33
34
|
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
|
|
34
35
|
const [newRoleName, setNewRoleName] = useState("");
|
|
35
36
|
const [userInfo, setUserInfo] = useState(null);
|
|
36
37
|
const [userRoleIds, setUserRoleIds] = useState([]);
|
|
38
|
+
// Edit permissions dialog state
|
|
39
|
+
const [editPermissionsDialogOpen, setEditPermissionsDialogOpen] = useState(false);
|
|
40
|
+
const [editingRoleIndex, setEditingRoleIndex] = useState(null);
|
|
41
|
+
// Track which roles have expanded permission tags (for read-only mode)
|
|
42
|
+
const [expandedRoles, setExpandedRoles] = useState(new Set());
|
|
37
43
|
// Load roles and permissions on mount
|
|
38
44
|
useEffect(() => {
|
|
39
45
|
const loadData = async () => {
|
|
@@ -48,6 +54,44 @@ export function RolesMatrix({ add_button_enabled = true, role_name_selection_ena
|
|
|
48
54
|
return;
|
|
49
55
|
}
|
|
50
56
|
setPermissions(roles_data.permissions.map((p) => p.permission_name));
|
|
57
|
+
// Also fetch permission descriptions from the permissions endpoint
|
|
58
|
+
try {
|
|
59
|
+
const perms_response = await fetch(`${apiBasePath}/user_management/permissions`);
|
|
60
|
+
const perms_data = await perms_response.json();
|
|
61
|
+
if (perms_data.success) {
|
|
62
|
+
// Combine DB permissions and config permissions with descriptions
|
|
63
|
+
const all_perms_with_desc = [];
|
|
64
|
+
// DB permissions have descriptions
|
|
65
|
+
if (Array.isArray(perms_data.db_permissions)) {
|
|
66
|
+
perms_data.db_permissions.forEach((p) => {
|
|
67
|
+
all_perms_with_desc.push({
|
|
68
|
+
permission_name: p.permission_name,
|
|
69
|
+
description: p.description || "",
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Config permissions don't have descriptions in the API
|
|
74
|
+
if (Array.isArray(perms_data.config_permissions)) {
|
|
75
|
+
perms_data.config_permissions.forEach((name) => {
|
|
76
|
+
// Only add if not already in db_permissions
|
|
77
|
+
if (!all_perms_with_desc.some(p => p.permission_name === name)) {
|
|
78
|
+
all_perms_with_desc.push({
|
|
79
|
+
permission_name: name,
|
|
80
|
+
description: "",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
setPermissionsWithDescriptions(all_perms_with_desc);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (_a) {
|
|
89
|
+
// If we can't get descriptions, use permissions without descriptions
|
|
90
|
+
setPermissionsWithDescriptions(roles_data.permissions.map((p) => ({
|
|
91
|
+
permission_name: p.permission_name,
|
|
92
|
+
description: "",
|
|
93
|
+
})));
|
|
94
|
+
}
|
|
51
95
|
// Initialize roles with permissions as Sets
|
|
52
96
|
const roles_with_permissions = roles_data.roles.map((role) => ({
|
|
53
97
|
role_id: role.role_id,
|
|
@@ -105,7 +149,7 @@ export function RolesMatrix({ add_button_enabled = true, role_name_selection_ena
|
|
|
105
149
|
}
|
|
106
150
|
};
|
|
107
151
|
void loadData();
|
|
108
|
-
}, [user_id]);
|
|
152
|
+
}, [user_id, apiBasePath]);
|
|
109
153
|
// Handle checkbox change for role-permission mapping
|
|
110
154
|
const handlePermissionToggle = (role_index, permission_name) => {
|
|
111
155
|
setRoles((prev) => {
|
|
@@ -264,6 +308,53 @@ export function RolesMatrix({ add_button_enabled = true, role_name_selection_ena
|
|
|
264
308
|
setSaving(false);
|
|
265
309
|
}
|
|
266
310
|
};
|
|
311
|
+
// Handle opening the edit permissions dialog
|
|
312
|
+
const handleOpenEditPermissions = (role_index) => {
|
|
313
|
+
setEditingRoleIndex(role_index);
|
|
314
|
+
setEditPermissionsDialogOpen(true);
|
|
315
|
+
};
|
|
316
|
+
// Handle toggling expanded state for a role's permission tags
|
|
317
|
+
const handleToggleExpandedPermissions = (role_index) => {
|
|
318
|
+
setExpandedRoles((prev) => {
|
|
319
|
+
const updated = new Set(prev);
|
|
320
|
+
if (updated.has(role_index)) {
|
|
321
|
+
updated.delete(role_index);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
updated.add(role_index);
|
|
325
|
+
}
|
|
326
|
+
return updated;
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
// Handle select all permissions for the editing role
|
|
330
|
+
const handleSelectAllPermissions = () => {
|
|
331
|
+
if (editingRoleIndex === null)
|
|
332
|
+
return;
|
|
333
|
+
setRoles((prev) => {
|
|
334
|
+
const updated = [...prev];
|
|
335
|
+
const role = Object.assign({}, updated[editingRoleIndex]);
|
|
336
|
+
role.permissions = new Set(permissions);
|
|
337
|
+
updated[editingRoleIndex] = role;
|
|
338
|
+
return updated;
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
// Handle unselect all permissions for the editing role
|
|
342
|
+
const handleUnselectAllPermissions = () => {
|
|
343
|
+
if (editingRoleIndex === null)
|
|
344
|
+
return;
|
|
345
|
+
setRoles((prev) => {
|
|
346
|
+
const updated = [...prev];
|
|
347
|
+
const role = Object.assign({}, updated[editingRoleIndex]);
|
|
348
|
+
role.permissions = new Set();
|
|
349
|
+
updated[editingRoleIndex] = role;
|
|
350
|
+
return updated;
|
|
351
|
+
});
|
|
352
|
+
};
|
|
353
|
+
// Get description for a permission
|
|
354
|
+
const getPermissionDescription = (permission_name) => {
|
|
355
|
+
const perm = permissionsWithDescriptions.find(p => p.permission_name === permission_name);
|
|
356
|
+
return (perm === null || perm === void 0 ? void 0 : perm.description) || "";
|
|
357
|
+
};
|
|
267
358
|
if (loading) {
|
|
268
359
|
return (_jsx("div", { className: `cls_roles_matrix flex items-center justify-center p-8 ${className || ""}`, children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) }));
|
|
269
360
|
}
|
|
@@ -278,12 +369,46 @@ export function RolesMatrix({ add_button_enabled = true, role_name_selection_ena
|
|
|
278
369
|
}
|
|
279
370
|
return email.substring(0, 2).toUpperCase();
|
|
280
371
|
};
|
|
281
|
-
|
|
372
|
+
// Number of permission tags to show before truncating
|
|
373
|
+
const MAX_VISIBLE_TAGS = 4;
|
|
374
|
+
return (_jsxs("div", { className: `cls_roles_matrix flex flex-col gap-4 w-full ${className || ""}`, children: [user_id && userInfo && (_jsxs("div", { className: "cls_roles_matrix_user_info flex items-center gap-4 p-4 border rounded-lg bg-muted/50", children: [_jsxs(Avatar, { className: "cls_roles_matrix_user_avatar h-12 w-12", children: [_jsx(AvatarImage, { src: userInfo.profile_picture_url || undefined, alt: userInfo.name ? `Profile picture of ${userInfo.name}` : "Profile picture", className: "cls_roles_matrix_user_avatar_image" }), _jsx(AvatarFallback, { className: "cls_roles_matrix_user_avatar_fallback bg-slate-200 text-slate-600", children: getUserInitials(userInfo.name, userInfo.email_address) })] }), _jsxs("div", { className: "cls_roles_matrix_user_info_details flex flex-col", children: [_jsx("span", { className: "cls_roles_matrix_user_name font-semibold text-lg", children: userInfo.name || userInfo.email_address }), userInfo.name && (_jsx("span", { className: "cls_roles_matrix_user_email text-sm text-muted-foreground", children: userInfo.email_address }))] })] })), _jsx("div", { className: "cls_roles_matrix_header flex items-center justify-between", children: _jsx("div", { className: "cls_roles_matrix_header_left", children: add_button_enabled && (_jsxs(Button, { onClick: () => setIsAddDialogOpen(true), variant: "default", size: "sm", className: "cls_roles_matrix_add_button", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Add Role"] })) }) }), _jsx("div", { className: "cls_roles_matrix_table_container border rounded-lg overflow-auto w-full", children: _jsxs(Table, { className: "cls_roles_matrix_table w-full", children: [_jsx(TableHeader, { className: "cls_roles_matrix_table_header", children: _jsxs(TableRow, { className: "cls_roles_matrix_table_header_row", children: [role_name_selection_enabled && (_jsx(TableHead, { className: "cls_roles_matrix_table_header_role_checkbox w-12" })), _jsx(TableHead, { className: "cls_roles_matrix_table_header_role_name w-48", children: "Role Name" }), _jsx(TableHead, { className: "cls_roles_matrix_table_header_permissions", children: "Permissions" }), !permissions_read_only && (_jsx(TableHead, { className: "cls_roles_matrix_table_header_actions w-24 text-center", children: "Actions" }))] }) }), _jsx(TableBody, { className: "cls_roles_matrix_table_body", children: roles.length === 0 ? (_jsx(TableRow, { className: "cls_roles_matrix_table_row_empty", children: _jsx(TableCell, { colSpan: role_name_selection_enabled ? 4 : 3, className: "text-center text-muted-foreground py-8", children: "No roles found. Add a role to get started." }) })) : (roles.map((role, role_index) => {
|
|
375
|
+
const permission_array = Array.from(role.permissions);
|
|
376
|
+
const is_expanded = expandedRoles.has(role_index);
|
|
377
|
+
const visible_permissions = is_expanded
|
|
378
|
+
? permission_array
|
|
379
|
+
: permission_array.slice(0, MAX_VISIBLE_TAGS);
|
|
380
|
+
const remaining_count = permission_array.length - MAX_VISIBLE_TAGS;
|
|
381
|
+
return (_jsxs(TableRow, { className: "cls_roles_matrix_table_row", children: [role_name_selection_enabled && (_jsx(TableCell, { className: "cls_roles_matrix_table_cell_role_checkbox text-center", children: _jsx("div", { className: "cls_roles_matrix_role_checkbox_wrapper flex items-center justify-center", children: _jsx(Checkbox, { checked: role.selected, onCheckedChange: () => handleRoleSelectionToggle(role_index), className: "cls_roles_matrix_role_checkbox" }) }) })), _jsx(TableCell, { className: "cls_roles_matrix_table_cell_role_name font-medium", children: role.role_name }), _jsx(TableCell, { className: "cls_roles_matrix_table_cell_permissions", children: _jsx("div", { className: "cls_roles_matrix_permission_tags flex flex-wrap items-center gap-1.5", children: permission_array.length === 0 ? (_jsx("span", { className: "text-muted-foreground text-sm italic", children: "No permissions assigned" })) : (_jsxs(_Fragment, { children: [visible_permissions.map((perm_name) => (_jsx("span", { className: "cls_roles_matrix_permission_tag bg-blue-100 text-blue-700 px-2 py-0.5 rounded text-xs font-medium", title: getPermissionDescription(perm_name) || perm_name, children: perm_name }, perm_name))), remaining_count > 0 && !is_expanded && (_jsxs("span", { className: "cls_roles_matrix_permission_tag_more bg-slate-100 text-slate-600 px-2 py-0.5 rounded text-xs font-medium cursor-pointer hover:bg-slate-200", onClick: () => {
|
|
382
|
+
if (permissions_read_only) {
|
|
383
|
+
handleToggleExpandedPermissions(role_index);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
handleOpenEditPermissions(role_index);
|
|
387
|
+
}
|
|
388
|
+
}, title: permissions_read_only ? `Click to expand all ${permission_array.length} permissions` : `Click to edit all ${permission_array.length} permissions`, children: ["+", remaining_count, " more"] })), is_expanded && remaining_count > 0 && (_jsx("span", { className: "cls_roles_matrix_permission_tag_collapse bg-slate-100 text-slate-600 px-2 py-0.5 rounded text-xs font-medium cursor-pointer hover:bg-slate-200", onClick: () => handleToggleExpandedPermissions(role_index), title: "Click to collapse", children: "Show less" }))] })) }) }), !permissions_read_only && (_jsx(TableCell, { className: "cls_roles_matrix_table_cell_actions text-center", children: _jsx(Button, { onClick: () => handleOpenEditPermissions(role_index), variant: "ghost", size: "sm", className: "cls_roles_matrix_edit_button h-8 w-8 p-0", title: "Edit permissions", children: _jsx(Pencil, { className: "h-4 w-4" }) }) }))] }, role_index));
|
|
389
|
+
})) })] }) }), show_save_cancel && (_jsxs("div", { className: "cls_roles_matrix_footer flex items-center justify-end gap-2", children: [_jsx(Button, { onClick: handleSave, disabled: saving, variant: "default", size: "sm", className: "cls_roles_matrix_save_button", children: saving ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Saving..."] })) : (_jsxs(_Fragment, { children: [_jsx(CircleCheck, { className: "h-4 w-4 mr-2" }), "Save"] })) }), _jsxs(Button, { onClick: handleCancel, variant: "outline", size: "sm", className: "cls_roles_matrix_cancel_button", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })), _jsx(Dialog, { open: isAddDialogOpen, onOpenChange: setIsAddDialogOpen, children: _jsxs(DialogContent, { className: "cls_roles_matrix_add_dialog", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add New Role" }), _jsx(DialogDescription, { children: "Enter a name for the new role. You can assign permissions after creating the role." })] }), _jsx("div", { className: "cls_roles_matrix_add_dialog_content flex flex-col gap-4 py-4", children: _jsxs("div", { className: "cls_roles_matrix_add_dialog_field flex flex-col gap-2", children: [_jsx(Label, { htmlFor: "role_name", className: "cls_roles_matrix_add_dialog_label", children: "Role Name" }), _jsx(Input, { id: "role_name", value: newRoleName, onChange: (e) => setNewRoleName(e.target.value), placeholder: "Enter role name", className: "cls_roles_matrix_add_dialog_input", onKeyDown: (e) => {
|
|
282
390
|
if (e.key === "Enter") {
|
|
283
391
|
handleAddRole();
|
|
284
392
|
}
|
|
285
393
|
} })] }) }), _jsxs(DialogFooter, { className: "cls_roles_matrix_add_dialog_footer", children: [_jsx(Button, { onClick: handleAddRole, variant: "default", className: "cls_roles_matrix_add_dialog_save", children: "Add Role" }), _jsx(Button, { onClick: () => {
|
|
286
394
|
setIsAddDialogOpen(false);
|
|
287
395
|
setNewRoleName("");
|
|
288
|
-
}, variant: "outline", className: "cls_roles_matrix_add_dialog_cancel", children: "Cancel" })] })] }) })] }))
|
|
396
|
+
}, variant: "outline", className: "cls_roles_matrix_add_dialog_cancel", children: "Cancel" })] })] }) }), _jsx(Dialog, { open: editPermissionsDialogOpen, onOpenChange: setEditPermissionsDialogOpen, children: _jsxs(DialogContent, { className: "cls_roles_matrix_edit_permissions_dialog max-w-lg max-h-[80vh] flex flex-col", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Edit Permissions for: ", editingRoleIndex !== null ? (_a = roles[editingRoleIndex]) === null || _a === void 0 ? void 0 : _a.role_name : ""] }), _jsx(DialogDescription, { children: "Select which permissions to assign to this role." })] }), _jsxs("div", { className: "cls_roles_matrix_edit_permissions_actions flex items-center gap-2 py-2 border-b", children: [_jsx(Button, { onClick: handleSelectAllPermissions, variant: "outline", size: "sm", className: "cls_roles_matrix_select_all_button", children: "Select All" }), _jsx(Button, { onClick: handleUnselectAllPermissions, variant: "outline", size: "sm", className: "cls_roles_matrix_unselect_all_button", children: "Unselect All" })] }), _jsx("div", { className: "cls_roles_matrix_edit_permissions_list flex-1 overflow-y-auto py-2 min-h-0", children: _jsx("div", { className: "flex flex-col gap-3", children: permissionsWithDescriptions.map((perm) => {
|
|
397
|
+
var _a;
|
|
398
|
+
const is_checked = editingRoleIndex !== null && ((_a = roles[editingRoleIndex]) === null || _a === void 0 ? void 0 : _a.permissions.has(perm.permission_name));
|
|
399
|
+
return (_jsxs("div", { className: "cls_roles_matrix_permission_item flex items-start gap-3 p-2 rounded hover:bg-muted/50 cursor-pointer", onClick: () => editingRoleIndex !== null && handlePermissionToggle(editingRoleIndex, perm.permission_name), children: [_jsx(Checkbox, { checked: is_checked, onCheckedChange: () => editingRoleIndex !== null && handlePermissionToggle(editingRoleIndex, perm.permission_name), className: "cls_roles_matrix_permission_checkbox mt-0.5" }), _jsxs("div", { className: "cls_roles_matrix_permission_info flex flex-col gap-0.5", children: [_jsx("span", { className: "cls_roles_matrix_permission_name font-medium text-sm", children: perm.permission_name }), perm.description && (_jsx("span", { className: "cls_roles_matrix_permission_description text-xs text-muted-foreground italic", children: perm.description }))] })] }, perm.permission_name));
|
|
400
|
+
}) }) }), _jsxs(DialogFooter, { className: "cls_roles_matrix_edit_permissions_footer border-t pt-4", children: [_jsxs(Button, { onClick: () => setEditPermissionsDialogOpen(false), variant: "default", className: "cls_roles_matrix_edit_permissions_done", children: [_jsx(CircleCheck, { className: "h-4 w-4 mr-2" }), "Done"] }), _jsxs(Button, { onClick: () => {
|
|
401
|
+
// Reset this role's permissions to original state
|
|
402
|
+
if (editingRoleIndex !== null) {
|
|
403
|
+
const original_role = originalRoles.find(r => r.role_name === roles[editingRoleIndex].role_name);
|
|
404
|
+
if (original_role) {
|
|
405
|
+
setRoles((prev) => {
|
|
406
|
+
const updated = [...prev];
|
|
407
|
+
updated[editingRoleIndex] = Object.assign(Object.assign({}, updated[editingRoleIndex]), { permissions: new Set(original_role.permissions) });
|
|
408
|
+
return updated;
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
setEditPermissionsDialogOpen(false);
|
|
413
|
+
}, variant: "outline", className: "cls_roles_matrix_edit_permissions_cancel", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })] }) })] }));
|
|
289
414
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ScopeHierarchyTabProps = {
|
|
2
|
+
className?: string;
|
|
3
|
+
defaultOrg?: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Scope Hierarchy tab component for managing HRBAC scopes
|
|
7
|
+
* Displays scopes in a tree view for intuitive hierarchy configuration
|
|
8
|
+
* @param props - Component props
|
|
9
|
+
* @returns Scope Hierarchy tab component
|
|
10
|
+
*/
|
|
11
|
+
export declare function ScopeHierarchyTab({ className, defaultOrg, }: ScopeHierarchyTabProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=scope_hierarchy_tab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope_hierarchy_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/scope_hierarchy_tab.tsx"],"names":[],"mappings":"AA2CA,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA8HF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,UAAe,GAChB,EAAE,sBAAsB,2CA2fxB"}
|