hazo_auth 4.4.1 → 4.5.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 +207 -5
- package/SETUP_CHECKLIST.md +1 -1
- package/cli-src/lib/auth/auth_types.ts +22 -0
- package/cli-src/lib/auth/hazo_get_auth.server.ts +25 -1
- package/cli-src/lib/auth/session_token_validator.edge.ts +1 -0
- package/cli-src/lib/config/default_config.ts +36 -0
- package/cli-src/lib/navbar_config.server.ts +129 -0
- package/cli-src/lib/scope_hierarchy_config.server.ts +3 -14
- package/cli-src/lib/services/registration_service.ts +12 -0
- package/cli-src/lib/services/scope_labels_service.ts +21 -21
- package/cli-src/lib/services/scope_service.ts +15 -11
- package/cli-src/lib/services/session_token_service.ts +1 -0
- package/cli-src/lib/ui_shell_config.server.ts +15 -0
- package/cli-src/lib/user_types_config.server.ts +178 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +17 -0
- package/dist/app/api/hazo_auth/org_management/orgs/route.d.ts +26 -0
- package/dist/app/api/hazo_auth/org_management/orgs/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/org_management/orgs/route.js +315 -0
- package/dist/app/api/hazo_auth/user_management/users/route.d.ts +7 -0
- package/dist/app/api/hazo_auth/user_management/users/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/user_management/users/route.js +48 -10
- 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 +8 -14
- package/dist/components/layouts/rbac_test/index.d.ts +1 -3
- package/dist/components/layouts/rbac_test/index.d.ts.map +1 -1
- package/dist/components/layouts/rbac_test/index.js +2 -2
- package/dist/components/layouts/shared/components/auth_navbar.d.ts +26 -0
- package/dist/components/layouts/shared/components/auth_navbar.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/auth_navbar.js +14 -0
- package/dist/components/layouts/shared/components/auth_page_shell.d.ts +3 -1
- package/dist/components/layouts/shared/components/auth_page_shell.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/auth_page_shell.js +17 -2
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.d.ts +6 -1
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.js +7 -2
- 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/scope_hierarchy_tab.d.ts +3 -2
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +45 -18
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts +3 -2
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/scope_labels_tab.js +48 -20
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/user_scopes_tab.js +1 -1
- package/dist/components/layouts/user_management/index.d.ts +11 -3
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +52 -5
- package/dist/components/ui/user-type-badge.d.ts +23 -0
- package/dist/components/ui/user-type-badge.d.ts.map +1 -0
- package/dist/components/ui/user-type-badge.js +42 -0
- package/dist/lib/auth/auth_types.d.ts +17 -0
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +11 -0
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +21 -1
- package/dist/lib/config/default_config.d.ts +60 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +34 -0
- package/dist/lib/navbar_config.server.d.ts +36 -0
- package/dist/lib/navbar_config.server.d.ts.map +1 -0
- package/dist/lib/navbar_config.server.js +45 -0
- package/dist/lib/scope_hierarchy_config.server.d.ts +3 -7
- package/dist/lib/scope_hierarchy_config.server.d.ts.map +1 -1
- package/dist/lib/scope_hierarchy_config.server.js +1 -10
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +8 -0
- package/dist/lib/services/scope_labels_service.d.ts +7 -7
- package/dist/lib/services/scope_labels_service.d.ts.map +1 -1
- package/dist/lib/services/scope_labels_service.js +20 -20
- package/dist/lib/services/scope_service.d.ts +8 -5
- package/dist/lib/services/scope_service.d.ts.map +1 -1
- package/dist/lib/services/scope_service.js +9 -8
- package/dist/lib/ui_shell_config.server.d.ts +5 -0
- package/dist/lib/ui_shell_config.server.d.ts.map +1 -1
- package/dist/lib/ui_shell_config.server.js +5 -0
- package/dist/lib/user_types_config.server.d.ts +56 -0
- package/dist/lib/user_types_config.server.d.ts.map +1 -0
- package/dist/lib/user_types_config.server.js +100 -0
- package/dist/server/routes/index.d.ts +1 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +2 -0
- package/dist/server/routes/org_management_orgs.d.ts +2 -0
- package/dist/server/routes/org_management_orgs.d.ts.map +1 -0
- package/dist/server/routes/org_management_orgs.js +2 -0
- package/hazo_auth_config.example.ini +9 -0
- package/package.json +1 -1
- package/cli-src/server/logging/logger_service.ts +0 -56
- /package/public/profile_pictures/library/Cars/{050 - citroe/314/210n_c3.jpeg" → 050 - citro/303/253n_c3.jpeg"} +0 -0
- /package/public/profile_pictures/library/Cars/{064 - lamborghini_huraca/314/201n.jpeg" → 064 - lamborghini_hurac/303/241n.jpeg"} +0 -0
- /package/public/profile_pictures/library/Cars/{099 - citroe/314/210n_2cv_(classic).jpeg" → 099 - citro/303/253n_2cv_(classic).jpeg"} +0 -0
- /package/public/profile_pictures/library/Cars/{131 - lamborghini_huraca/314/201n_sto.jpeg" → 131 - lamborghini_hurac/303/241n_sto.jpeg"} +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
type AuthPageShellProps = {
|
|
3
3
|
children: ReactNode;
|
|
4
|
+
/** Force disable navbar for this page (e.g., dev_lock) */
|
|
5
|
+
disableNavbar?: boolean;
|
|
4
6
|
};
|
|
5
|
-
export declare function AuthPageShell({ children }: AuthPageShellProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function AuthPageShell({ children, disableNavbar }: AuthPageShellProps): import("react/jsx-runtime").JSX.Element;
|
|
6
8
|
export {};
|
|
7
9
|
//# sourceMappingURL=auth_page_shell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_page_shell.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/auth_page_shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"auth_page_shell.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/auth_page_shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAGF,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,aAAqB,EAAE,EAAE,kBAAkB,2CAsCpF"}
|
|
@@ -3,10 +3,25 @@ import { SidebarLayoutWrapper } from "./sidebar_layout_wrapper";
|
|
|
3
3
|
import { StandaloneLayoutWrapper } from "./standalone_layout_wrapper";
|
|
4
4
|
import { get_ui_shell_config } from "../../../../lib/ui_shell_config.server";
|
|
5
5
|
// section: component
|
|
6
|
-
export function AuthPageShell({ children }) {
|
|
6
|
+
export function AuthPageShell({ children, disableNavbar = false }) {
|
|
7
7
|
const uiShellConfig = get_ui_shell_config();
|
|
8
8
|
if (uiShellConfig.layout_mode === "standalone") {
|
|
9
|
-
|
|
9
|
+
// Build navbar props (or null if disabled)
|
|
10
|
+
const navbarProps = !disableNavbar && uiShellConfig.navbar.enable_navbar
|
|
11
|
+
? {
|
|
12
|
+
logo_path: uiShellConfig.navbar.logo_path,
|
|
13
|
+
logo_width: uiShellConfig.navbar.logo_width,
|
|
14
|
+
logo_height: uiShellConfig.navbar.logo_height,
|
|
15
|
+
company_name: uiShellConfig.navbar.company_name,
|
|
16
|
+
home_path: uiShellConfig.navbar.home_path,
|
|
17
|
+
home_label: uiShellConfig.navbar.home_label,
|
|
18
|
+
show_home_link: uiShellConfig.navbar.show_home_link,
|
|
19
|
+
background_color: uiShellConfig.navbar.background_color,
|
|
20
|
+
text_color: uiShellConfig.navbar.text_color,
|
|
21
|
+
height: uiShellConfig.navbar.height,
|
|
22
|
+
}
|
|
23
|
+
: null;
|
|
24
|
+
return (_jsx(StandaloneLayoutWrapper, { heading: uiShellConfig.standalone_heading, description: uiShellConfig.standalone_description, wrapperClassName: uiShellConfig.standalone_wrapper_class, contentClassName: uiShellConfig.standalone_content_class, showHeading: uiShellConfig.standalone_show_heading, showDescription: uiShellConfig.standalone_show_description, navbar: navbarProps, verticalCenter: uiShellConfig.vertical_center, children: children }));
|
|
10
25
|
}
|
|
11
26
|
return _jsx(SidebarLayoutWrapper, { children: children });
|
|
12
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AuthNavbarProps } from "./auth_navbar";
|
|
1
2
|
export type StandaloneLayoutWrapperProps = {
|
|
2
3
|
children: React.ReactNode;
|
|
3
4
|
heading?: string;
|
|
@@ -6,6 +7,10 @@ export type StandaloneLayoutWrapperProps = {
|
|
|
6
7
|
contentClassName?: string;
|
|
7
8
|
showHeading?: boolean;
|
|
8
9
|
showDescription?: boolean;
|
|
10
|
+
/** Navbar configuration (pass null to disable navbar) */
|
|
11
|
+
navbar?: AuthNavbarProps | null;
|
|
12
|
+
/** Enable vertical centering of content (default: true) */
|
|
13
|
+
verticalCenter?: boolean;
|
|
9
14
|
};
|
|
10
|
-
export declare function StandaloneLayoutWrapper({ children, heading, description, wrapperClassName, contentClassName, showHeading, showDescription, }: StandaloneLayoutWrapperProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function StandaloneLayoutWrapper({ children, heading, description, wrapperClassName, contentClassName, showHeading, showDescription, navbar, verticalCenter, }: StandaloneLayoutWrapperProps): import("react/jsx-runtime").JSX.Element;
|
|
11
16
|
//# sourceMappingURL=standalone_layout_wrapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/standalone_layout_wrapper.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"standalone_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/standalone_layout_wrapper.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGjE,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yDAAyD;IACzD,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAGF,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,OAAqB,EACrB,WAA8E,EAC9E,gBAAgB,EAChB,gBAAgB,EAChB,WAAkB,EAClB,eAAsB,EACtB,MAAM,EACN,cAAqB,GACtB,EAAE,4BAA4B,2CAmD9B"}
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
5
|
// section: imports
|
|
6
6
|
import { cn } from "../../../../lib/utils";
|
|
7
|
+
import { AuthNavbar } from "./auth_navbar";
|
|
7
8
|
// section: component
|
|
8
|
-
export function StandaloneLayoutWrapper({ children, heading = "hazo auth", description = "Drop-in authentication flows that inherit your existing theme.", wrapperClassName, contentClassName, showHeading = true, showDescription = true, }) {
|
|
9
|
-
|
|
9
|
+
export function StandaloneLayoutWrapper({ children, heading = "hazo auth", description = "Drop-in authentication flows that inherit your existing theme.", wrapperClassName, contentClassName, showHeading = true, showDescription = true, navbar, verticalCenter = true, }) {
|
|
10
|
+
var _a;
|
|
11
|
+
const hasNavbar = navbar !== null && navbar !== undefined;
|
|
12
|
+
// Calculate navbar height for vertical centering offset
|
|
13
|
+
const navbarHeight = hasNavbar ? ((_a = navbar === null || navbar === void 0 ? void 0 : navbar.height) !== null && _a !== void 0 ? _a : 64) : 0;
|
|
14
|
+
return (_jsxs("div", { className: cn("cls_standalone_layout_wrapper flex min-h-screen w-full flex-col bg-background", wrapperClassName), children: [hasNavbar && _jsx(AuthNavbar, Object.assign({}, navbar)), _jsx("div", { className: cn("cls_standalone_layout_content_area flex-1", verticalCenter && "flex items-center justify-center"), style: verticalCenter ? { minHeight: `calc(100vh - ${navbarHeight}px)` } : undefined, children: _jsxs("div", { className: cn("cls_standalone_layout_content mx-auto flex w-full max-w-5xl flex-col gap-8 p-6", contentClassName), children: [(showHeading || showDescription) && (_jsxs("div", { className: "cls_standalone_layout_header text-center", children: [showHeading && (_jsx("h1", { className: "cls_standalone_layout_title text-2xl font-semibold tracking-tight text-foreground", children: heading })), showDescription && (_jsx("p", { className: "cls_standalone_layout_description mt-2 text-sm text-muted-foreground", children: description }))] })), _jsx("div", { className: "cls_standalone_layout_body", children: children })] }) })] }));
|
|
10
15
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { AlreadyLoggedInGuard } from "./components/already_logged_in_guard";
|
|
2
|
+
export { AuthNavbar } from "./components/auth_navbar";
|
|
3
|
+
export type { AuthNavbarProps } from "./components/auth_navbar";
|
|
2
4
|
export { FieldErrorMessage } from "./components/field_error_message";
|
|
3
5
|
export { FormActionButtons } from "./components/form_action_buttons";
|
|
4
6
|
export { FormFieldWrapper } from "./components/form_field_wrapper";
|
|
@@ -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;
|
|
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;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,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;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// file_description: barrel export for shared layout components, hooks, and utilities
|
|
2
2
|
// section: component_exports
|
|
3
3
|
export { AlreadyLoggedInGuard } from "./components/already_logged_in_guard";
|
|
4
|
+
export { AuthNavbar } from "./components/auth_navbar";
|
|
4
5
|
// AuthPageShell - NOT exported (test workspace component only)
|
|
5
6
|
export { FieldErrorMessage } from "./components/field_error_message";
|
|
6
7
|
export { FormActionButtons } from "./components/form_action_buttons";
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export type ScopeHierarchyTabProps = {
|
|
2
2
|
className?: string;
|
|
3
|
-
defaultOrg?: string;
|
|
4
3
|
};
|
|
5
4
|
/**
|
|
6
5
|
* Scope Hierarchy tab component for managing HRBAC scopes
|
|
7
6
|
* Displays scopes in a tree view for intuitive hierarchy configuration
|
|
7
|
+
* Non-global admins see only their org's scopes (auto-filtered by API)
|
|
8
|
+
* Global admins can view/manage any org's scopes by providing org_id
|
|
8
9
|
* @param props - Component props
|
|
9
10
|
* @returns Scope Hierarchy tab component
|
|
10
11
|
*/
|
|
11
|
-
export declare function ScopeHierarchyTab({ className,
|
|
12
|
+
export declare function ScopeHierarchyTab({ className, }: ScopeHierarchyTabProps): import("react/jsx-runtime").JSX.Element;
|
|
12
13
|
//# sourceMappingURL=scope_hierarchy_tab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope_hierarchy_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/scope_hierarchy_tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scope_hierarchy_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/scope_hierarchy_tab.tsx"],"names":[],"mappings":"AA4CA,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA+HF;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,GACV,EAAE,sBAAsB,2CA2gBxB"}
|
|
@@ -13,6 +13,7 @@ import { Label } from "../../../ui/label";
|
|
|
13
13
|
import { Loader2, Plus, Edit, Trash2, CircleCheck, CircleX, Building2, FolderTree, RefreshCw, } from "lucide-react";
|
|
14
14
|
import { toast } from "sonner";
|
|
15
15
|
import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider";
|
|
16
|
+
import { use_hazo_auth } from "../../shared/hooks/use_hazo_auth";
|
|
16
17
|
const SCOPE_LEVEL_LABELS = {
|
|
17
18
|
hazo_scopes_l1: "Level 1",
|
|
18
19
|
hazo_scopes_l2: "Level 2",
|
|
@@ -64,16 +65,26 @@ function convertToTreeData(nodes, onEdit, onDelete, onAddChild) {
|
|
|
64
65
|
/**
|
|
65
66
|
* Scope Hierarchy tab component for managing HRBAC scopes
|
|
66
67
|
* Displays scopes in a tree view for intuitive hierarchy configuration
|
|
68
|
+
* Non-global admins see only their org's scopes (auto-filtered by API)
|
|
69
|
+
* Global admins can view/manage any org's scopes by providing org_id
|
|
67
70
|
* @param props - Component props
|
|
68
71
|
* @returns Scope Hierarchy tab component
|
|
69
72
|
*/
|
|
70
|
-
export function ScopeHierarchyTab({ className,
|
|
73
|
+
export function ScopeHierarchyTab({ className, }) {
|
|
74
|
+
var _a;
|
|
71
75
|
const { apiBasePath } = useHazoAuthConfig();
|
|
76
|
+
const authResult = use_hazo_auth();
|
|
77
|
+
// Determine if user is global admin (can manage any org)
|
|
78
|
+
const isGlobalAdmin = authResult.authenticated && authResult.permissions.includes("hazo_org_global_admin");
|
|
79
|
+
// Get user's org_id (for non-global admins, this is the only org they can manage)
|
|
80
|
+
const userOrgId = ((_a = authResult.user) === null || _a === void 0 ? void 0 : _a.org_id) || "";
|
|
72
81
|
// State
|
|
73
82
|
const [tree, setTree] = useState([]);
|
|
74
83
|
const [loading, setLoading] = useState(true);
|
|
75
84
|
const [actionLoading, setActionLoading] = useState(false);
|
|
76
|
-
|
|
85
|
+
// For non-global admins, org_id is fixed to their own org
|
|
86
|
+
// For global admins, they can enter any org_id to view that org's scopes
|
|
87
|
+
const [orgId, setOrgId] = useState("");
|
|
77
88
|
const [selectedItem, setSelectedItem] = useState();
|
|
78
89
|
// Dialog state
|
|
79
90
|
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
|
@@ -83,18 +94,29 @@ export function ScopeHierarchyTab({ className, defaultOrg = "", }) {
|
|
|
83
94
|
const [addParentScope, setAddParentScope] = useState(null);
|
|
84
95
|
// Form state
|
|
85
96
|
const [newName, setNewName] = useState("");
|
|
86
|
-
const [newOrg, setNewOrg] = useState(defaultOrg);
|
|
87
97
|
const [editName, setEditName] = useState("");
|
|
98
|
+
// Initialize orgId from user's org when auth loads
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!authResult.loading && userOrgId && !orgId) {
|
|
101
|
+
setOrgId(userOrgId);
|
|
102
|
+
}
|
|
103
|
+
}, [authResult.loading, userOrgId, orgId]);
|
|
88
104
|
// Load tree data
|
|
89
105
|
const loadTree = useCallback(async () => {
|
|
90
|
-
|
|
106
|
+
// For non-global admins, API will auto-filter to their org
|
|
107
|
+
// For global admins, org_id is required to specify which org to view
|
|
108
|
+
if (isGlobalAdmin && !orgId) {
|
|
91
109
|
setTree([]);
|
|
92
110
|
setLoading(false);
|
|
93
111
|
return;
|
|
94
112
|
}
|
|
95
113
|
setLoading(true);
|
|
96
114
|
try {
|
|
97
|
-
const params = new URLSearchParams({ action: "tree"
|
|
115
|
+
const params = new URLSearchParams({ action: "tree" });
|
|
116
|
+
// Only pass org_id for global admins (non-global admins are auto-filtered by API)
|
|
117
|
+
if (isGlobalAdmin && orgId) {
|
|
118
|
+
params.set("org_id", orgId);
|
|
119
|
+
}
|
|
98
120
|
const response = await fetch(`${apiBasePath}/scope_management/scopes?${params}`);
|
|
99
121
|
const data = await response.json();
|
|
100
122
|
if (data.success) {
|
|
@@ -112,22 +134,22 @@ export function ScopeHierarchyTab({ className, defaultOrg = "", }) {
|
|
|
112
134
|
finally {
|
|
113
135
|
setLoading(false);
|
|
114
136
|
}
|
|
115
|
-
}, [apiBasePath,
|
|
116
|
-
// Load data when
|
|
137
|
+
}, [apiBasePath, orgId, isGlobalAdmin]);
|
|
138
|
+
// Load data when orgId changes or auth finishes loading
|
|
117
139
|
useEffect(() => {
|
|
118
|
-
|
|
119
|
-
|
|
140
|
+
if (!authResult.loading) {
|
|
141
|
+
void loadTree();
|
|
142
|
+
}
|
|
143
|
+
}, [loadTree, authResult.loading]);
|
|
120
144
|
// Handle add scope (root level)
|
|
121
145
|
const handleAddRootScope = () => {
|
|
122
146
|
setAddParentScope(null);
|
|
123
|
-
setNewOrg(org || defaultOrg);
|
|
124
147
|
setNewName("");
|
|
125
148
|
setAddDialogOpen(true);
|
|
126
149
|
};
|
|
127
150
|
// Handle add child scope
|
|
128
151
|
const handleAddChildScope = (parent) => {
|
|
129
152
|
setAddParentScope(parent);
|
|
130
|
-
setNewOrg(parent.org);
|
|
131
153
|
setNewName("");
|
|
132
154
|
setAddDialogOpen(true);
|
|
133
155
|
};
|
|
@@ -148,10 +170,6 @@ export function ScopeHierarchyTab({ className, defaultOrg = "", }) {
|
|
|
148
170
|
toast.error("Name is required");
|
|
149
171
|
return;
|
|
150
172
|
}
|
|
151
|
-
if (!newOrg.trim()) {
|
|
152
|
-
toast.error("Organization is required");
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
173
|
setActionLoading(true);
|
|
156
174
|
try {
|
|
157
175
|
const level = addParentScope
|
|
@@ -161,13 +179,22 @@ export function ScopeHierarchyTab({ className, defaultOrg = "", }) {
|
|
|
161
179
|
toast.error("Cannot add children to Level 7 scopes");
|
|
162
180
|
return;
|
|
163
181
|
}
|
|
182
|
+
// Build request body - API will use user's org_id for non-global admins
|
|
183
|
+
// or the provided org_id for global admins
|
|
164
184
|
const body = {
|
|
165
185
|
level,
|
|
166
|
-
org: newOrg.trim(),
|
|
167
186
|
name: newName.trim(),
|
|
168
187
|
};
|
|
188
|
+
// For global admins, pass the selected org_id
|
|
189
|
+
if (isGlobalAdmin && orgId) {
|
|
190
|
+
body.org_id = orgId;
|
|
191
|
+
body.root_org_id = orgId; // Default root_org_id to same as org_id
|
|
192
|
+
}
|
|
169
193
|
if (addParentScope) {
|
|
170
194
|
body.parent_scope_id = addParentScope.id;
|
|
195
|
+
// Inherit org_id from parent scope
|
|
196
|
+
body.org_id = addParentScope.org_id;
|
|
197
|
+
body.root_org_id = addParentScope.root_org_id;
|
|
171
198
|
}
|
|
172
199
|
const response = await fetch(`${apiBasePath}/scope_management/scopes`, {
|
|
173
200
|
method: "POST",
|
|
@@ -278,10 +305,10 @@ export function ScopeHierarchyTab({ className, defaultOrg = "", }) {
|
|
|
278
305
|
const childLevel = getChildLevel(addParentScope.level);
|
|
279
306
|
return childLevel ? SCOPE_LEVEL_LABELS[childLevel] : "Unknown";
|
|
280
307
|
};
|
|
281
|
-
return (_jsxs("div", { className: `cls_scope_hierarchy_tab flex flex-col gap-4 w-full ${className || ""}`, children: [_jsxs("div", { className: "cls_scope_hierarchy_header flex items-center justify-between gap-4 flex-wrap", children: [_jsxs("div", { className: "cls_scope_hierarchy_header_left flex items-center gap-4", children: [_jsxs("div", { className: "cls_scope_hierarchy_org_filter flex items-center gap-2", children: [_jsx(Label, { htmlFor: "
|
|
308
|
+
return (_jsxs("div", { className: `cls_scope_hierarchy_tab flex flex-col gap-4 w-full ${className || ""}`, children: [_jsxs("div", { className: "cls_scope_hierarchy_header flex items-center justify-between gap-4 flex-wrap", children: [_jsxs("div", { className: "cls_scope_hierarchy_header_left flex items-center gap-4", children: [isGlobalAdmin && (_jsxs("div", { className: "cls_scope_hierarchy_org_filter flex items-center gap-2", children: [_jsx(Label, { htmlFor: "scope_org_id", className: "text-sm font-medium", children: "Organization ID:" }), _jsx(Input, { id: "scope_org_id", value: orgId, onChange: (e) => setOrgId(e.target.value), placeholder: "Enter org UUID", className: "w-[280px] font-mono text-sm" })] })), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void loadTree(), disabled: loading || (isGlobalAdmin && !orgId), children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}` }), "Refresh"] })] }), _jsx("div", { className: "cls_scope_hierarchy_header_right", children: _jsxs(Button, { onClick: handleAddRootScope, variant: "default", size: "sm", disabled: isGlobalAdmin && !orgId, children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Add Root Scope"] }) })] }), loading || authResult.loading ? (_jsx("div", { className: "cls_scope_hierarchy_loading flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : isGlobalAdmin && !orgId ? (_jsxs("div", { className: "cls_scope_hierarchy_empty flex flex-col items-center justify-center p-8 border rounded-lg border-dashed", children: [_jsx(Building2, { className: "h-12 w-12 text-muted-foreground mb-4" }), _jsx("p", { className: "text-muted-foreground text-center", children: "Enter an organization ID to view scope hierarchy" })] })) : tree.length === 0 ? (_jsxs("div", { className: "cls_scope_hierarchy_empty flex flex-col items-center justify-center p-8 border rounded-lg border-dashed", children: [_jsx(FolderTree, { className: "h-12 w-12 text-muted-foreground mb-4" }), _jsx("p", { className: "text-muted-foreground text-center mb-4", children: "No scopes found for this organization" }), _jsxs(Button, { onClick: handleAddRootScope, variant: "outline", size: "sm", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Create First Scope"] })] })) : (_jsx("div", { className: "cls_scope_hierarchy_tree_container border rounded-lg overflow-auto w-full min-h-[300px]", children: _jsx(TreeView, { data: treeData, expandAll: true, defaultNodeIcon: Building2, defaultLeafIcon: Building2, onSelectChange: handleSelectChange, className: "w-full" }) })), (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.scopeData) && (_jsxs("div", { className: "cls_scope_hierarchy_selected_info p-4 border rounded-lg bg-muted/50", children: [_jsx("h4", { className: "font-medium mb-2", children: "Selected Scope" }), _jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Name:" }), " ", selectedItem.scopeData.name] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Seq:" }), " ", selectedItem.scopeData.seq] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Level:" }), " ", SCOPE_LEVEL_LABELS[selectedItem.scopeData.level]] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Org ID:" }), " ", _jsx("span", { className: "font-mono text-xs", children: selectedItem.scopeData.org_id })] })] })] })), _jsx(Dialog, { open: addDialogOpen, onOpenChange: setAddDialogOpen, children: _jsxs(DialogContent, { className: "cls_scope_hierarchy_add_dialog", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: addParentScope
|
|
282
309
|
? `Add Child Scope to "${addParentScope.name}"`
|
|
283
310
|
: "Add Root Scope" }), _jsxs(DialogDescription, { children: ["Create a new scope at ", getAddDialogLevelLabel(), ".", addParentScope &&
|
|
284
|
-
` This will be a child of "${addParentScope.name}".`] })] }),
|
|
311
|
+
` This will be a child of "${addParentScope.name}".`] })] }), _jsx("div", { className: "flex flex-col gap-4 py-4", children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { htmlFor: "new_scope_name", children: "Name *" }), _jsx(Input, { id: "new_scope_name", value: newName, onChange: (e) => setNewName(e.target.value), placeholder: "Enter scope name" })] }) }), _jsxs(DialogFooter, { children: [_jsx(Button, { onClick: handleCreateScope, disabled: actionLoading, variant: "default", children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Creating..."] })) : (_jsxs(_Fragment, { children: [_jsx(CircleCheck, { className: "h-4 w-4 mr-2" }), "Create"] })) }), _jsxs(Button, { onClick: () => setAddDialogOpen(false), variant: "outline", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })] }) }), _jsx(Dialog, { open: editDialogOpen, onOpenChange: setEditDialogOpen, children: _jsxs(DialogContent, { className: "cls_scope_hierarchy_edit_dialog", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Edit Scope" }), _jsxs(DialogDescription, { children: ["Update scope: ", selectedScope === null || selectedScope === void 0 ? void 0 : selectedScope.name, " (", selectedScope === null || selectedScope === void 0 ? void 0 : selectedScope.seq, ")"] })] }), _jsx("div", { className: "flex flex-col gap-4 py-4", children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { htmlFor: "edit_scope_name", children: "Name *" }), _jsx(Input, { id: "edit_scope_name", value: editName, onChange: (e) => setEditName(e.target.value), placeholder: "Enter scope name" })] }) }), _jsxs(DialogFooter, { children: [_jsx(Button, { onClick: handleUpdateScope, disabled: actionLoading, variant: "default", children: actionLoading ? (_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: () => {
|
|
285
312
|
setEditDialogOpen(false);
|
|
286
313
|
setSelectedScope(null);
|
|
287
314
|
}, variant: "outline", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })] }) }), _jsx(AlertDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete Scope" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to delete \"", selectedScope === null || selectedScope === void 0 ? void 0 : selectedScope.name, "\" (", selectedScope === null || selectedScope === void 0 ? void 0 : selectedScope.seq, ")? This action cannot be undone and will also delete all child scopes."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogAction, { onClick: handleDeleteScope, disabled: actionLoading, children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Deleting..."] })) : ("Delete") }), _jsx(AlertDialogCancel, { onClick: () => {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export type ScopeLabelsTabProps = {
|
|
2
2
|
className?: string;
|
|
3
|
-
defaultOrg?: string;
|
|
4
3
|
};
|
|
5
4
|
/**
|
|
6
5
|
* Scope Labels tab component for configuring friendly names for scope levels
|
|
7
6
|
* Shows all 7 scope levels with their current labels from database
|
|
8
7
|
* Empty inputs for levels without labels - no placeholders
|
|
8
|
+
* Non-global admins see only their org's labels (auto-filtered by API)
|
|
9
|
+
* Global admins can view/manage any org's labels by providing org_id
|
|
9
10
|
* @param props - Component props
|
|
10
11
|
* @returns Scope Labels tab component
|
|
11
12
|
*/
|
|
12
|
-
export declare function ScopeLabelsTab({ className
|
|
13
|
+
export declare function ScopeLabelsTab({ className }: ScopeLabelsTabProps): import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
//# sourceMappingURL=scope_labels_tab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope_labels_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/scope_labels_tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scope_labels_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/scope_labels_tab.tsx"],"names":[],"mappings":"AAuBA,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAeF;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAwPhE"}
|
|
@@ -11,6 +11,7 @@ import { Label } from "../../../ui/label";
|
|
|
11
11
|
import { Loader2, Save } from "lucide-react";
|
|
12
12
|
import { toast } from "sonner";
|
|
13
13
|
import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider";
|
|
14
|
+
import { use_hazo_auth } from "../../shared/hooks/use_hazo_auth";
|
|
14
15
|
const SCOPE_LEVELS = [
|
|
15
16
|
"hazo_scopes_l1",
|
|
16
17
|
"hazo_scopes_l2",
|
|
@@ -25,11 +26,19 @@ const SCOPE_LEVELS = [
|
|
|
25
26
|
* Scope Labels tab component for configuring friendly names for scope levels
|
|
26
27
|
* Shows all 7 scope levels with their current labels from database
|
|
27
28
|
* Empty inputs for levels without labels - no placeholders
|
|
29
|
+
* Non-global admins see only their org's labels (auto-filtered by API)
|
|
30
|
+
* Global admins can view/manage any org's labels by providing org_id
|
|
28
31
|
* @param props - Component props
|
|
29
32
|
* @returns Scope Labels tab component
|
|
30
33
|
*/
|
|
31
|
-
export function ScopeLabelsTab({ className
|
|
34
|
+
export function ScopeLabelsTab({ className }) {
|
|
35
|
+
var _a;
|
|
32
36
|
const { apiBasePath } = useHazoAuthConfig();
|
|
37
|
+
const authResult = use_hazo_auth();
|
|
38
|
+
// Determine if user is global admin (can manage any org)
|
|
39
|
+
const isGlobalAdmin = authResult.authenticated && authResult.permissions.includes("hazo_org_global_admin");
|
|
40
|
+
// Get user's org_id (for non-global admins, this is the only org they can manage)
|
|
41
|
+
const userOrgId = ((_a = authResult.user) === null || _a === void 0 ? void 0 : _a.org_id) || "";
|
|
33
42
|
// State - simple record of scope_type to label string (empty string if not set)
|
|
34
43
|
const [labels, setLabels] = useState(() => {
|
|
35
44
|
const initial = {};
|
|
@@ -41,11 +50,21 @@ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
|
|
|
41
50
|
const [originalLabels, setOriginalLabels] = useState(null);
|
|
42
51
|
const [loading, setLoading] = useState(true);
|
|
43
52
|
const [saving, setSaving] = useState(false);
|
|
44
|
-
|
|
53
|
+
// For non-global admins, org_id is fixed to their own org
|
|
54
|
+
// For global admins, they can enter any org_id to view that org's labels
|
|
55
|
+
const [orgId, setOrgId] = useState("");
|
|
56
|
+
// Initialize orgId from user's org when auth loads
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!authResult.loading && userOrgId && !orgId) {
|
|
59
|
+
setOrgId(userOrgId);
|
|
60
|
+
}
|
|
61
|
+
}, [authResult.loading, userOrgId, orgId]);
|
|
45
62
|
// Load labels from database (only real DB records, not synthetic defaults)
|
|
46
63
|
const loadLabels = useCallback(async () => {
|
|
47
|
-
|
|
48
|
-
|
|
64
|
+
// For non-global admins, API will auto-filter to their org
|
|
65
|
+
// For global admins, org_id is required to specify which org to view
|
|
66
|
+
if (isGlobalAdmin && !orgId.trim()) {
|
|
67
|
+
// Reset to empty if global admin hasn't specified org
|
|
49
68
|
const empty = {};
|
|
50
69
|
for (const level of SCOPE_LEVELS) {
|
|
51
70
|
empty[level] = "";
|
|
@@ -58,7 +77,11 @@ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
|
|
|
58
77
|
setLoading(true);
|
|
59
78
|
try {
|
|
60
79
|
// Fetch WITHOUT defaults - only get actual DB records
|
|
61
|
-
const params = new URLSearchParams({
|
|
80
|
+
const params = new URLSearchParams({ include_defaults: "false" });
|
|
81
|
+
// Only pass org_id for global admins (non-global admins are auto-filtered by API)
|
|
82
|
+
if (isGlobalAdmin && orgId.trim()) {
|
|
83
|
+
params.set("org_id", orgId.trim());
|
|
84
|
+
}
|
|
62
85
|
const response = await fetch(`${apiBasePath}/scope_management/labels?${params}`);
|
|
63
86
|
const data = await response.json();
|
|
64
87
|
if (data.success) {
|
|
@@ -87,11 +110,13 @@ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
|
|
|
87
110
|
finally {
|
|
88
111
|
setLoading(false);
|
|
89
112
|
}
|
|
90
|
-
}, [apiBasePath,
|
|
91
|
-
// Load labels when
|
|
113
|
+
}, [apiBasePath, orgId, isGlobalAdmin]);
|
|
114
|
+
// Load labels when orgId changes or auth finishes loading
|
|
92
115
|
useEffect(() => {
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
if (!authResult.loading) {
|
|
117
|
+
void loadLabels();
|
|
118
|
+
}
|
|
119
|
+
}, [loadLabels, authResult.loading]);
|
|
95
120
|
// Handle label change
|
|
96
121
|
const handleLabelChange = (level, value) => {
|
|
97
122
|
setLabels((prev) => (Object.assign(Object.assign({}, prev), { [level]: value })));
|
|
@@ -109,10 +134,6 @@ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
|
|
|
109
134
|
};
|
|
110
135
|
// Save all labels - send all non-empty labels to be upserted
|
|
111
136
|
const handleSave = async () => {
|
|
112
|
-
if (!org.trim()) {
|
|
113
|
-
toast.error("Organization is required");
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
137
|
// Collect all non-empty labels
|
|
117
138
|
const labelsToSave = [];
|
|
118
139
|
for (const level of SCOPE_LEVELS) {
|
|
@@ -126,13 +147,18 @@ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
|
|
|
126
147
|
}
|
|
127
148
|
setSaving(true);
|
|
128
149
|
try {
|
|
150
|
+
// Build request body - API will use user's org_id for non-global admins
|
|
151
|
+
const body = {
|
|
152
|
+
labels: labelsToSave,
|
|
153
|
+
};
|
|
154
|
+
// For global admins, pass the selected org_id
|
|
155
|
+
if (isGlobalAdmin && orgId.trim()) {
|
|
156
|
+
body.org_id = orgId.trim();
|
|
157
|
+
}
|
|
129
158
|
const response = await fetch(`${apiBasePath}/scope_management/labels`, {
|
|
130
159
|
method: "PUT",
|
|
131
160
|
headers: { "Content-Type": "application/json" },
|
|
132
|
-
body: JSON.stringify(
|
|
133
|
-
org: org.trim(),
|
|
134
|
-
labels: labelsToSave,
|
|
135
|
-
}),
|
|
161
|
+
body: JSON.stringify(body),
|
|
136
162
|
});
|
|
137
163
|
const data = await response.json();
|
|
138
164
|
if (data.success) {
|
|
@@ -151,8 +177,10 @@ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
|
|
|
151
177
|
setSaving(false);
|
|
152
178
|
}
|
|
153
179
|
};
|
|
154
|
-
|
|
180
|
+
// Check if inputs should be enabled
|
|
181
|
+
const inputsEnabled = !isGlobalAdmin || orgId.trim().length > 0;
|
|
182
|
+
return (_jsxs("div", { className: `cls_scope_labels_tab flex flex-col gap-4 w-full ${className || ""}`, children: [_jsxs("div", { className: "cls_scope_labels_header flex items-center justify-between gap-4 flex-wrap", children: [_jsx("div", { className: "cls_scope_labels_header_left flex items-center gap-4", children: isGlobalAdmin && (_jsxs("div", { className: "cls_scope_labels_org_input flex items-center gap-2", children: [_jsx(Label, { htmlFor: "labels_org_id", className: "text-sm font-medium", children: "Organization ID:" }), _jsx(Input, { id: "labels_org_id", value: orgId, onChange: (e) => setOrgId(e.target.value), placeholder: "Enter org UUID", className: "w-[280px] font-mono text-sm" })] })) }), _jsx("div", { className: "cls_scope_labels_header_right", children: _jsx(Button, { onClick: handleSave, disabled: saving || !hasChanges() || (isGlobalAdmin && !orgId.trim()), variant: "default", size: "sm", children: saving ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Saving..."] })) : (_jsxs(_Fragment, { children: [_jsx(Save, { className: "h-4 w-4 mr-2" }), "Save Changes"] })) }) })] }), loading || authResult.loading ? (_jsx("div", { className: "cls_scope_labels_loading flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : isGlobalAdmin && !orgId.trim() ? (_jsx("div", { className: "cls_scope_labels_info text-sm text-muted-foreground text-center p-4 bg-muted/50 rounded-lg", children: "Enter an organization ID to customize scope labels." })) : (_jsx("div", { className: "cls_scope_labels_table_container border rounded-lg overflow-auto w-full", children: _jsxs(Table, { className: "cls_scope_labels_table w-full", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[180px]", children: "Scope Level" }), _jsx(TableHead, { children: "Label" })] }) }), _jsx(TableBody, { children: SCOPE_LEVELS.map((level) => {
|
|
155
183
|
const label = labels[level];
|
|
156
|
-
return (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono text-sm", children: level }), _jsx(TableCell, { children: _jsx(Input, { value: label, onChange: (e) => handleLabelChange(level, e.target.value), className: "max-w-[400px]", disabled: !
|
|
157
|
-
}) })] }) }))
|
|
184
|
+
return (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono text-sm", children: level }), _jsx(TableCell, { children: _jsx(Input, { value: label, onChange: (e) => handleLabelChange(level, e.target.value), className: "max-w-[400px]", disabled: !inputsEnabled }) })] }, level));
|
|
185
|
+
}) })] }) }))] }));
|
|
158
186
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user_scopes_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/user_scopes_tab.tsx"],"names":[],"mappings":"AAoDA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"user_scopes_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/user_scopes_tab.tsx"],"names":[],"mappings":"AAoDA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA8EF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,kBAAkB,2CA8f9D"}
|
|
@@ -260,7 +260,7 @@ export function UserScopesTab({ className }) {
|
|
|
260
260
|
}, variant: "outline", size: "sm", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Assign First Scope"] })] })) : (_jsxs(Table, { className: "w-full", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Level" }), _jsx(TableHead, { children: "Scope Seq" }), _jsx(TableHead, { children: "Scope ID" }), _jsx(TableHead, { children: "Assigned" }), _jsx(TableHead, { className: "text-right w-[80px]", children: "Actions" })] }) }), _jsx(TableBody, { children: userScopes.map((scope) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: getLevelLabel(scope.scope_type) }), _jsx(TableCell, { className: "font-mono text-sm", children: scope.scope_seq }), _jsxs(TableCell, { className: "font-mono text-xs text-muted-foreground", children: [scope.scope_id.substring(0, 8), "..."] }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: new Date(scope.created_at).toLocaleDateString() }), _jsx(TableCell, { className: "text-right", children: _jsx(Button, { onClick: () => {
|
|
261
261
|
setScopeToDelete(scope);
|
|
262
262
|
setDeleteDialogOpen(true);
|
|
263
|
-
}, variant: "outline", size: "sm", className: "text-destructive", children: _jsx(Trash2, { className: "h-4 w-4" }) }) })] }, `${scope.scope_type}-${scope.scope_id}`))) })] })) })] }), _jsx(Dialog, { open: addDialogOpen, onOpenChange: setAddDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_scopes_add_dialog sm:max-w-[500px]", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add Scope Assignment" }), _jsxs(DialogDescription, { children: ["Select a scope from the tree to assign to", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "."] })] }), _jsxs("div", { className: "flex flex-col gap-4 py-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Select Scope" }), treeLoading ? (_jsx("div", { className: "flex items-center justify-center p-8 border rounded-lg", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : scopeTree.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-6 border rounded-lg border-dashed", children: [_jsx(FolderTree, { className: "h-8 w-8 text-muted-foreground mb-2" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "No scopes available. Create scopes in the Scope Hierarchy tab first." })] })) : (_jsx("div", { className: "border rounded-lg max-h-[300px] overflow-auto", children: _jsx(TreeView, { data: treeData, expandAll: true, defaultNodeIcon: Building2, defaultLeafIcon: Building2, onSelectChange: handleTreeSelectChange, initialSelectedItemId: selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.id, className: "w-full" }) }))] }), (selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData) && (_jsxs("div", { className: "p-3 border rounded-lg bg-muted/50", children: [_jsxs("p", { className: "text-sm", children: [_jsx("span", { className: "font-medium", children: "Selected:" }), " ", selectedTreeItem.scopeData.name] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [SCOPE_LEVEL_LABELS[selectedTreeItem.scopeData.level], " -", " ", selectedTreeItem.scopeData.seq
|
|
263
|
+
}, variant: "outline", size: "sm", className: "text-destructive", children: _jsx(Trash2, { className: "h-4 w-4" }) }) })] }, `${scope.scope_type}-${scope.scope_id}`))) })] })) })] }), _jsx(Dialog, { open: addDialogOpen, onOpenChange: setAddDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_scopes_add_dialog sm:max-w-[500px]", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add Scope Assignment" }), _jsxs(DialogDescription, { children: ["Select a scope from the tree to assign to", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "."] })] }), _jsxs("div", { className: "flex flex-col gap-4 py-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Select Scope" }), treeLoading ? (_jsx("div", { className: "flex items-center justify-center p-8 border rounded-lg", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : scopeTree.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-6 border rounded-lg border-dashed", children: [_jsx(FolderTree, { className: "h-8 w-8 text-muted-foreground mb-2" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "No scopes available. Create scopes in the Scope Hierarchy tab first." })] })) : (_jsx("div", { className: "border rounded-lg max-h-[300px] overflow-auto", children: _jsx(TreeView, { data: treeData, expandAll: true, defaultNodeIcon: Building2, defaultLeafIcon: Building2, onSelectChange: handleTreeSelectChange, initialSelectedItemId: selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.id, className: "w-full" }) }))] }), (selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData) && (_jsxs("div", { className: "p-3 border rounded-lg bg-muted/50", children: [_jsxs("p", { className: "text-sm", children: [_jsx("span", { className: "font-medium", children: "Selected:" }), " ", selectedTreeItem.scopeData.name] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [SCOPE_LEVEL_LABELS[selectedTreeItem.scopeData.level], " -", " ", selectedTreeItem.scopeData.seq] })] }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { onClick: handleAddScope, disabled: actionLoading || !(selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData), variant: "default", children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Assigning..."] })) : (_jsxs(_Fragment, { children: [_jsx(CircleCheck, { className: "h-4 w-4 mr-2" }), "Assign Scope"] })) }), _jsxs(Button, { onClick: () => setAddDialogOpen(false), variant: "outline", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })] }) }), _jsx(AlertDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Remove Scope Assignment" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to remove the scope \"", scopeToDelete === null || scopeToDelete === void 0 ? void 0 : scopeToDelete.scope_seq, "\" from", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "? This will also revoke access to any inherited scopes."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogAction, { onClick: handleRemoveScope, disabled: actionLoading, children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Removing..."] })) : ("Remove") }), _jsx(AlertDialogCancel, { onClick: () => {
|
|
264
264
|
setDeleteDialogOpen(false);
|
|
265
265
|
setScopeToDelete(null);
|
|
266
266
|
}, children: "Cancel" })] })] }) })] }));
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
/** User type definition for the dropdown */
|
|
2
|
+
export type UserTypeOption = {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
badge_color: string;
|
|
6
|
+
};
|
|
1
7
|
export type UserManagementLayoutProps = {
|
|
2
8
|
className?: string;
|
|
3
9
|
/** Whether HRBAC is enabled (passed from server) */
|
|
4
10
|
hrbacEnabled?: boolean;
|
|
5
11
|
/** Whether multi-tenancy is enabled (passed from server) */
|
|
6
12
|
multiTenancyEnabled?: boolean;
|
|
7
|
-
/**
|
|
8
|
-
|
|
13
|
+
/** Whether user types feature is enabled (passed from server) */
|
|
14
|
+
userTypesEnabled?: boolean;
|
|
15
|
+
/** Available user types for dropdown (passed from server) */
|
|
16
|
+
availableUserTypes?: UserTypeOption[];
|
|
9
17
|
};
|
|
10
18
|
/**
|
|
11
19
|
* User Management layout component with tabs for managing users, roles, permissions, and HRBAC scopes
|
|
@@ -18,5 +26,5 @@ export type UserManagementLayoutProps = {
|
|
|
18
26
|
* @param props - Component props
|
|
19
27
|
* @returns User Management layout component
|
|
20
28
|
*/
|
|
21
|
-
export declare function UserManagementLayout({ className, hrbacEnabled, multiTenancyEnabled,
|
|
29
|
+
export declare function UserManagementLayout({ className, hrbacEnabled, multiTenancyEnabled, userTypesEnabled, availableUserTypes }: UserManagementLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
22
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AA0DA,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AAuBF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,mBAA2B,EAAE,gBAAwB,EAAE,kBAAuB,EAAE,EAAE,yBAAyB,2CA81ClL"}
|