hazo_auth 1.6.2 → 1.6.4

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 CHANGED
@@ -680,6 +680,76 @@ layout_mode = standalone
680
680
 
681
681
  The `hazo_auth` package provides a comprehensive authentication and authorization system with role-based access control (RBAC). The main authentication utility is `hazo_get_auth`, which provides user details, permissions, and permission checking with built-in caching and rate limiting.
682
682
 
683
+ ### Client-Side API Endpoint (Recommended)
684
+
685
+ #### `/api/hazo_auth/me` (GET) - Standardized User Info Endpoint
686
+
687
+ **⚠️ IMPORTANT: Use this endpoint for all client-side authentication checks. It always returns the same standardized format with permissions.**
688
+
689
+ This is the **standardized endpoint** that ensures consistent response format across all projects. It always includes permissions and user information in a unified structure.
690
+
691
+ **Endpoint:** `GET /api/hazo_auth/me`
692
+
693
+ **Response Format (Authenticated):**
694
+ ```typescript
695
+ {
696
+ authenticated: true,
697
+ // Top-level fields (for backward compatibility)
698
+ user_id: string,
699
+ email: string,
700
+ name: string | null,
701
+ email_verified: boolean,
702
+ last_logon: string | undefined,
703
+ profile_picture_url: string | null,
704
+ profile_source: "upload" | "library" | "gravatar" | "custom" | undefined,
705
+ // Permissions (always included)
706
+ user: {
707
+ id: string,
708
+ email_address: string,
709
+ name: string | null,
710
+ is_active: boolean,
711
+ profile_picture_url: string | null,
712
+ },
713
+ permissions: string[],
714
+ permission_ok: boolean,
715
+ missing_permissions?: string[],
716
+ }
717
+ ```
718
+
719
+ **Response Format (Not Authenticated):**
720
+ ```typescript
721
+ {
722
+ authenticated: false
723
+ }
724
+ ```
725
+
726
+ **Example Usage:**
727
+
728
+ ```typescript
729
+ // Client-side (React component)
730
+ const response = await fetch("/api/hazo_auth/me", {
731
+ method: "GET",
732
+ credentials: "include",
733
+ });
734
+
735
+ const data = await response.json();
736
+
737
+ if (data.authenticated) {
738
+ console.log("User:", data.user);
739
+ console.log("Email:", data.email);
740
+ console.log("Permissions:", data.permissions);
741
+ console.log("Permission OK:", data.permission_ok);
742
+ }
743
+ ```
744
+
745
+ **Why Use `/api/hazo_auth/me`?**
746
+ - ✅ **Standardized format** - Always returns the same structure
747
+ - ✅ **Always includes permissions** - No need for separate permission checks
748
+ - ✅ **Backward compatible** - Top-level fields work with existing code
749
+ - ✅ **Single source of truth** - Prevents downstream variations
750
+
751
+ **Note:** The `use_auth_status` hook automatically uses this endpoint and includes permissions in its return value.
752
+
683
753
  ### Server-Side Functions
684
754
 
685
755
  #### `hazo_get_auth` (Recommended)
@@ -917,13 +987,22 @@ enable_friendly_error_messages = true
917
987
  ## Profile Picture Menu Widget
918
988
 
919
989
  The Profile Picture Menu is a versatile component for navbar or sidebar that automatically displays:
920
- - **When authenticated**: User's profile picture with a dropdown menu containing user info, settings link, logout, and custom menu items
990
+ - **When authenticated**: User's profile picture with a dropdown menu (navbar) or sidebar menu (sidebar) containing user info, settings link, logout, and custom menu items
921
991
  - **When not authenticated**: Sign Up and Sign In buttons (or a single button, configurable)
922
992
 
993
+ ### Variants
994
+
995
+ The component supports two rendering variants:
996
+
997
+ - **`dropdown`** (default): Renders as a clickable avatar that opens a dropdown menu. Use this for navbar/header contexts.
998
+ - **`sidebar`**: Shows profile picture and name in a sidebar group. Clicking opens a dropdown menu with account actions. Use this inside `SidebarContent` for sidebar navigation.
999
+
923
1000
  ### Basic Usage (Recommended)
924
1001
 
925
1002
  Use the `ProfilePicMenuWrapper` component which automatically loads configuration from `hazo_auth_config.ini`:
926
1003
 
1004
+ #### Dropdown Variant (Navbar/Header)
1005
+
927
1006
  ```typescript
928
1007
  // In your navbar or layout component
929
1008
  import { ProfilePicMenuWrapper } from "hazo_auth/components/layouts/shared/components/profile_pic_menu_wrapper";
@@ -935,12 +1014,65 @@ export function Navbar() {
935
1014
  <ProfilePicMenuWrapper
936
1015
  avatar_size="default" // "sm" | "default" | "lg"
937
1016
  className="ml-auto"
1017
+ // variant="dropdown" is the default
938
1018
  />
939
1019
  </nav>
940
1020
  );
941
1021
  }
942
1022
  ```
943
1023
 
1024
+ #### Sidebar Variant
1025
+
1026
+ The sidebar variant shows only the profile picture and name. Clicking opens a dropdown menu with account actions:
1027
+
1028
+ ```typescript
1029
+ // In your sidebar component
1030
+ import { ProfilePicMenu } from "hazo_auth/components/layouts/shared";
1031
+ import { SidebarContent } from "hazo_auth/components/ui/sidebar";
1032
+
1033
+ export function Sidebar() {
1034
+ return (
1035
+ <SidebarContent>
1036
+ {/* Other sidebar groups */}
1037
+
1038
+ {/* Profile menu as sidebar variant - shows avatar + name, clicking opens dropdown */}
1039
+ <ProfilePicMenu
1040
+ variant="sidebar"
1041
+ avatar_size="sm"
1042
+ sidebar_group_label="Account" // Optional: defaults to "Account"
1043
+ className="mt-auto" // Optional: push to bottom
1044
+ />
1045
+ </SidebarContent>
1046
+ );
1047
+ }
1048
+ ```
1049
+
1050
+ ### Direct Usage (Advanced)
1051
+
1052
+ If you need more control, use `ProfilePicMenu` directly:
1053
+
1054
+ ```typescript
1055
+ import { ProfilePicMenu } from "hazo_auth/components/layouts/shared";
1056
+
1057
+ // Dropdown variant (navbar)
1058
+ <ProfilePicMenu
1059
+ variant="dropdown"
1060
+ avatar_size="sm"
1061
+ settings_path="/settings"
1062
+ logout_path="/api/logout"
1063
+ />
1064
+
1065
+ // Sidebar variant
1066
+ <ProfilePicMenu
1067
+ variant="sidebar"
1068
+ avatar_size="sm"
1069
+ sidebar_group_label="My Account"
1070
+ custom_menu_items={[
1071
+ { type: "link", label: "Dashboard", href: "/dashboard", order: 1, id: "dashboard" }
1072
+ ]}
1073
+ />
1074
+ ```
1075
+
944
1076
  ### Configuration
945
1077
 
946
1078
  ```ini
@@ -616,19 +616,51 @@ export default function CustomLoginPage() {
616
616
 
617
617
  Run these tests to verify your setup is working correctly.
618
618
 
619
- ### Test 1: API Health Check
619
+ ### Test 1: API Health Check - Standardized `/api/hazo_auth/me` Endpoint
620
+
621
+ **⚠️ IMPORTANT: Use `/api/hazo_auth/me` for all client-side authentication checks. It always returns a standardized format with permissions.**
620
622
 
621
623
  ```bash
622
624
  curl -s http://localhost:3000/api/hazo_auth/me | jq
623
625
  ```
624
626
 
625
- **Expected response:**
627
+ **Expected response (not authenticated):**
626
628
  ```json
627
629
  {
628
630
  "authenticated": false
629
631
  }
630
632
  ```
631
633
 
634
+ **Expected response (authenticated - standardized format):**
635
+ ```json
636
+ {
637
+ "authenticated": true,
638
+ "user_id": "28fe0aff-29c7-407e-b92e-bf11a6a3332f",
639
+ "email": "test@example.com",
640
+ "name": "Test User",
641
+ "email_verified": false,
642
+ "last_logon": "2025-01-27T16:18:00.054Z",
643
+ "profile_picture_url": "https://gravatar.com/avatar/...",
644
+ "profile_source": "gravatar",
645
+ "user": {
646
+ "id": "28fe0aff-29c7-407e-b92e-bf11a6a3332f",
647
+ "email_address": "test@example.com",
648
+ "name": "Test User",
649
+ "is_active": true,
650
+ "profile_picture_url": "https://gravatar.com/avatar/..."
651
+ },
652
+ "permissions": [],
653
+ "permission_ok": true
654
+ }
655
+ ```
656
+
657
+ **Key Points:**
658
+ - ✅ Always returns the same standardized format
659
+ - ✅ Always includes `permissions` and `permission_ok` fields
660
+ - ✅ Top-level fields (`user_id`, `email`, `name`) for backward compatibility
661
+ - ✅ `user` object contains full user details
662
+ - ✅ Use this endpoint instead of `/api/hazo_auth/get_auth` for client-side code
663
+
632
664
  ### Test 2: Registration API
633
665
 
634
666
  ```bash
@@ -1,3 +1,32 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
- export declare function GET(request: NextRequest): Promise<NextResponse<unknown>>;
2
+ /**
3
+ * GET /api/hazo_auth/me
4
+ *
5
+ * Standardized endpoint that returns authenticated user information with permissions.
6
+ * Always returns the same format to prevent downstream variations.
7
+ *
8
+ * Response format (authenticated):
9
+ * {
10
+ * authenticated: true,
11
+ * user_id: string,
12
+ * email: string,
13
+ * name: string | null,
14
+ * email_verified: boolean,
15
+ * last_logon: string | undefined,
16
+ * profile_picture_url: string | null,
17
+ * profile_source: "upload" | "library" | "gravatar" | "custom" | undefined,
18
+ * user: { id, email_address, name, is_active, profile_picture_url },
19
+ * permissions: string[],
20
+ * permission_ok: boolean,
21
+ * missing_permissions?: string[],
22
+ * }
23
+ *
24
+ * Response format (not authenticated):
25
+ * {
26
+ * authenticated: false
27
+ * }
28
+ */
29
+ export declare function GET(request: NextRequest): Promise<NextResponse<{
30
+ authenticated: boolean;
31
+ }>>;
3
32
  //# sourceMappingURL=route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/me/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,kCAuC7C"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/me/route.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASxD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;IA0E7C"}
@@ -1,33 +1,93 @@
1
- // file_description: API route to get current authenticated user information
1
+ // file_description: API route to get current authenticated user information with permissions
2
+ // This is the standardized endpoint that always returns the same format including permissions
2
3
  // section: imports
3
4
  import { NextResponse } from "next/server";
4
- import { get_authenticated_user_with_response } from "../../../../lib/auth/auth_utils.server";
5
+ import { hazo_get_auth } from "../../../../lib/auth/hazo_get_auth.server";
6
+ import { get_hazo_connect_instance } from "../../../../lib/hazo_connect_instance.server";
7
+ import { createCrudService } from "hazo_connect/server";
8
+ import { map_db_source_to_ui } from "../../../../lib/services/profile_picture_source_mapper";
9
+ import { create_app_logger } from "../../../../lib/app_logger";
10
+ import { get_filename, get_line_number } from "../../../../lib/utils/api_route_helpers";
5
11
  // section: api_handler
12
+ /**
13
+ * GET /api/hazo_auth/me
14
+ *
15
+ * Standardized endpoint that returns authenticated user information with permissions.
16
+ * Always returns the same format to prevent downstream variations.
17
+ *
18
+ * Response format (authenticated):
19
+ * {
20
+ * authenticated: true,
21
+ * user_id: string,
22
+ * email: string,
23
+ * name: string | null,
24
+ * email_verified: boolean,
25
+ * last_logon: string | undefined,
26
+ * profile_picture_url: string | null,
27
+ * profile_source: "upload" | "library" | "gravatar" | "custom" | undefined,
28
+ * user: { id, email_address, name, is_active, profile_picture_url },
29
+ * permissions: string[],
30
+ * permission_ok: boolean,
31
+ * missing_permissions?: string[],
32
+ * }
33
+ *
34
+ * Response format (not authenticated):
35
+ * {
36
+ * authenticated: false
37
+ * }
38
+ */
6
39
  export async function GET(request) {
40
+ const logger = create_app_logger();
7
41
  try {
8
- // Use centralized auth utility
9
- const { auth_result, response } = await get_authenticated_user_with_response(request);
10
- // If response is provided, it means cookies were cleared (invalid auth)
11
- if (response) {
12
- return response;
13
- }
42
+ // Use hazo_get_auth to get user with permissions
43
+ const auth_result = await hazo_get_auth(request);
14
44
  // If not authenticated, return false
15
45
  if (!auth_result.authenticated) {
16
46
  return NextResponse.json({ authenticated: false }, { status: 200 });
17
47
  }
18
- // Return user info
48
+ // Fetch additional user fields from database (email_verified, last_logon, profile_source)
49
+ const hazoConnect = get_hazo_connect_instance();
50
+ const users_service = createCrudService(hazoConnect, "hazo_users");
51
+ const users = await users_service.findBy({ id: auth_result.user.id });
52
+ if (!Array.isArray(users) || users.length === 0) {
53
+ logger.warn("me_endpoint_user_not_found", {
54
+ filename: get_filename(),
55
+ line_number: get_line_number(),
56
+ user_id: auth_result.user.id,
57
+ message: "User found in auth but not in database",
58
+ });
59
+ return NextResponse.json({ authenticated: false }, { status: 200 });
60
+ }
61
+ const user_db = users[0];
62
+ // Map database profile_source to UI representation
63
+ const profile_source_db = user_db.profile_source;
64
+ const profile_source_ui = profile_source_db ? map_db_source_to_ui(profile_source_db) : undefined;
65
+ // Return unified format with all fields
19
66
  return NextResponse.json({
20
67
  authenticated: true,
21
- user_id: auth_result.user_id,
22
- email: auth_result.email,
23
- name: auth_result.name,
24
- email_verified: auth_result.email_verified,
25
- last_logon: auth_result.last_logon,
26
- profile_picture_url: auth_result.profile_picture_url,
27
- profile_source: auth_result.profile_source,
68
+ // Top-level fields for backward compatibility
69
+ user_id: auth_result.user.id,
70
+ email: auth_result.user.email_address,
71
+ name: auth_result.user.name,
72
+ email_verified: user_db.email_verified === true,
73
+ last_logon: user_db.last_logon || undefined,
74
+ profile_picture_url: auth_result.user.profile_picture_url,
75
+ profile_source: profile_source_ui,
76
+ // Permissions and user object (always included)
77
+ user: auth_result.user,
78
+ permissions: auth_result.permissions,
79
+ permission_ok: auth_result.permission_ok,
80
+ missing_permissions: auth_result.missing_permissions,
28
81
  }, { status: 200 });
29
82
  }
30
83
  catch (error) {
84
+ const error_message = error instanceof Error ? error.message : "Unknown error";
85
+ logger.error("me_endpoint_error", {
86
+ filename: get_filename(),
87
+ line_number: get_line_number(),
88
+ error_message,
89
+ error_stack: error instanceof Error ? error.stack : undefined,
90
+ });
31
91
  // On error, assume not authenticated
32
92
  return NextResponse.json({ authenticated: false }, { status: 200 });
33
93
  }
@@ -10,13 +10,17 @@ export type ProfilePicMenuProps = {
10
10
  custom_menu_items?: ProfilePicMenuMenuItem[];
11
11
  className?: string;
12
12
  avatar_size?: "default" | "sm" | "lg";
13
+ variant?: "dropdown" | "sidebar";
14
+ sidebar_group_label?: string;
13
15
  };
14
16
  /**
15
17
  * Profile picture menu component
16
18
  * Shows user profile picture when authenticated, or sign up/sign in buttons when not authenticated
17
- * Clicking profile picture opens dropdown menu with user info and actions
19
+ * Supports two variants:
20
+ * - "dropdown" (default): Clicking profile picture opens dropdown menu (for navbar/header)
21
+ * - "sidebar": Shows profile picture and name in sidebar, clicking opens dropdown menu (for sidebar navigation)
18
22
  * @param props - Component props including configuration options
19
23
  * @returns Profile picture menu component
20
24
  */
21
- export declare function ProfilePicMenu({ show_single_button, sign_up_label, sign_in_label, register_path, login_path, settings_path, logout_path, custom_menu_items, className, avatar_size, }: ProfilePicMenuProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function ProfilePicMenu({ show_single_button, sign_up_label, sign_in_label, register_path, login_path, settings_path, logout_path, custom_menu_items, className, avatar_size, variant, sidebar_group_label, }: ProfilePicMenuProps): import("react/jsx-runtime").JSX.Element;
22
26
  //# sourceMappingURL=profile_pic_menu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"profile_pic_menu.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/profile_pic_menu.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AAG7F,MAAM,MAAM,mBAAmB,GAAG;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;CACvC,CAAC;AAGF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,kBAA0B,EAC1B,aAAyB,EACzB,aAAyB,EACzB,aAAqC,EACrC,UAA+B,EAC/B,aAAwC,EACxC,WAAqC,EACrC,iBAAsB,EACtB,SAAS,EACT,WAAuB,GACxB,EAAE,mBAAmB,2CAuQrB"}
1
+ {"version":3,"file":"profile_pic_menu.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/profile_pic_menu.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AAG7F,MAAM,MAAM,mBAAmB,GAAG;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAGF;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAC7B,kBAA0B,EAC1B,aAAyB,EACzB,aAAyB,EACzB,aAAqC,EACrC,UAA+B,EAC/B,aAAwC,EACxC,WAAqC,EACrC,iBAAsB,EACtB,SAAS,EACT,WAAuB,EACvB,OAAoB,EACpB,mBAA+B,GAChC,EAAE,mBAAmB,2CAmarB"}
@@ -1,7 +1,8 @@
1
1
  // file_description: profile picture menu component for navbar or sidebar - shows profile picture when logged in, or sign up/sign in buttons when not logged in
2
+ // Supports both dropdown (navbar) and sidebar variants
2
3
  // section: client_directive
3
4
  "use client";
4
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
5
6
  // section: imports
6
7
  import { useState, useMemo } from "react";
7
8
  import { useRouter } from "next/navigation";
@@ -9,6 +10,7 @@ import Link from "next/link";
9
10
  import { Avatar, AvatarImage, AvatarFallback } from "../../../ui/avatar";
10
11
  import { Button } from "../../../ui/button";
11
12
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "../../../ui/dropdown-menu";
13
+ import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuItem, SidebarMenuButton, } from "../../../ui/sidebar";
12
14
  import { Settings, LogOut } from "lucide-react";
13
15
  import { toast } from "sonner";
14
16
  import { use_auth_status, trigger_auth_status_refresh } from "../hooks/use_auth_status";
@@ -16,11 +18,13 @@ import { use_auth_status, trigger_auth_status_refresh } from "../hooks/use_auth_
16
18
  /**
17
19
  * Profile picture menu component
18
20
  * Shows user profile picture when authenticated, or sign up/sign in buttons when not authenticated
19
- * Clicking profile picture opens dropdown menu with user info and actions
21
+ * Supports two variants:
22
+ * - "dropdown" (default): Clicking profile picture opens dropdown menu (for navbar/header)
23
+ * - "sidebar": Shows profile picture and name in sidebar, clicking opens dropdown menu (for sidebar navigation)
20
24
  * @param props - Component props including configuration options
21
25
  * @returns Profile picture menu component
22
26
  */
23
- export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Sign Up", sign_in_label = "Sign In", register_path = "/hazo_auth/register", login_path = "/hazo_auth/login", settings_path = "/hazo_auth/my_settings", logout_path = "/api/hazo_auth/logout", custom_menu_items = [], className, avatar_size = "default", }) {
27
+ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Sign Up", sign_in_label = "Sign In", register_path = "/hazo_auth/register", login_path = "/hazo_auth/login", settings_path = "/hazo_auth/my_settings", logout_path = "/api/hazo_auth/logout", custom_menu_items = [], className, avatar_size = "default", variant = "dropdown", sidebar_group_label = "Account", }) {
24
28
  const router = useRouter();
25
29
  const authStatus = use_auth_status();
26
30
  const [isLoggingOut, setIsLoggingOut] = useState(false);
@@ -138,13 +142,44 @@ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Si
138
142
  };
139
143
  // Show loading state
140
144
  if (authStatus.loading) {
145
+ if (variant === "sidebar") {
146
+ return (_jsxs(SidebarGroup, { className: `cls_profile_pic_menu_sidebar ${className || ""}`, children: [_jsx(SidebarGroupLabel, { children: sidebar_group_label }), _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsx("div", { className: "h-10 w-10 rounded-full bg-[var(--hazo-bg-emphasis)] animate-pulse" }) }) })] }));
147
+ }
141
148
  return (_jsx("div", { className: `cls_profile_pic_menu ${className || ""}`, children: _jsx("div", { className: "h-10 w-10 rounded-full bg-[var(--hazo-bg-emphasis)] animate-pulse" }) }));
142
149
  }
143
150
  // Not authenticated - show sign up/sign in buttons
144
151
  if (!authStatus.authenticated) {
152
+ if (variant === "sidebar") {
153
+ return (_jsxs(SidebarGroup, { className: `cls_profile_pic_menu_sidebar ${className || ""}`, children: [_jsx(SidebarGroupLabel, { children: sidebar_group_label }), _jsx(SidebarMenu, { children: show_single_button ? (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, children: _jsx(Link, { href: register_path, className: "cls_profile_pic_menu_sign_up", children: sign_up_label }) }) })) : (_jsxs(_Fragment, { children: [_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, children: _jsx(Link, { href: register_path, className: "cls_profile_pic_menu_sign_up", children: sign_up_label }) }) }), _jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, children: _jsx(Link, { href: login_path, className: "cls_profile_pic_menu_sign_in", children: sign_in_label }) }) })] })) })] }));
154
+ }
145
155
  return (_jsx("div", { className: `cls_profile_pic_menu flex items-center gap-2 ${className || ""}`, children: show_single_button ? (_jsx(Button, { asChild: true, variant: "default", size: "sm", children: _jsx(Link, { href: register_path, className: "cls_profile_pic_menu_sign_up", children: sign_up_label }) })) : (_jsxs(_Fragment, { children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: register_path, className: "cls_profile_pic_menu_sign_up", children: sign_up_label }) }), _jsx(Button, { asChild: true, variant: "default", size: "sm", children: _jsx(Link, { href: login_path, className: "cls_profile_pic_menu_sign_in", children: sign_in_label }) })] })) }));
146
156
  }
147
- // Authenticated - show profile picture with dropdown menu
157
+ // Authenticated - render based on variant
158
+ if (variant === "sidebar") {
159
+ // Sidebar variant: show profile picture and name only, clicking opens dropdown
160
+ return (_jsxs(SidebarGroup, { className: `cls_profile_pic_menu_sidebar ${className || ""}`, children: [_jsx(SidebarGroupLabel, { className: "cls_profile_pic_menu_sidebar_label", children: sidebar_group_label }), _jsx(SidebarMenu, { className: "cls_profile_pic_menu_sidebar_menu", children: _jsx(SidebarMenuItem, { className: "cls_profile_pic_menu_sidebar_user_info", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: "cls_profile_pic_menu_sidebar_trigger w-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary rounded-md", "aria-label": "Profile menu", children: _jsxs("div", { className: "cls_profile_pic_menu_sidebar_user flex items-center gap-3 px-2 py-2 hover:bg-sidebar-accent rounded-md transition-colors", children: [_jsxs(Avatar, { className: `cls_profile_pic_menu_avatar ${avatarSizeClasses[avatar_size]} cursor-pointer`, children: [_jsx(AvatarImage, { src: authStatus.profile_picture_url, alt: authStatus.name ? `Profile picture of ${authStatus.name}` : "Profile picture", className: "cls_profile_pic_menu_image" }), _jsx(AvatarFallback, { className: "cls_profile_pic_menu_fallback bg-[var(--hazo-bg-emphasis)] text-[var(--hazo-text-muted)]", children: getInitials() })] }), authStatus.name && (_jsx("span", { className: "cls_profile_pic_menu_sidebar_user_name text-sm font-medium text-sidebar-foreground truncate", children: authStatus.name }))] }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "cls_profile_pic_menu_dropdown w-56", children: menuItems.map((item) => {
161
+ if (item.type === "separator") {
162
+ return _jsx(DropdownMenuSeparator, { className: "cls_profile_pic_menu_separator" }, item.id);
163
+ }
164
+ if (item.type === "info") {
165
+ return (_jsx("div", { className: "cls_profile_pic_menu_info", children: item.value && (_jsx("div", { className: "cls_profile_pic_menu_info_value px-2 py-1.5 text-sm text-foreground", children: item.value })) }, item.id));
166
+ }
167
+ if (item.type === "link") {
168
+ // Special handling for logout
169
+ if (item.id === "default_logout") {
170
+ return (_jsxs(DropdownMenuItem, { onClick: handleLogout, disabled: isLoggingOut, className: "cls_profile_pic_menu_logout cursor-pointer text-destructive focus:text-destructive", children: [_jsx(LogOut, { className: "mr-2 h-4 w-4" }), isLoggingOut ? "Logging out..." : item.label] }, item.id));
171
+ }
172
+ // Special handling for settings
173
+ if (item.id === "default_settings") {
174
+ return (_jsx(DropdownMenuItem, { asChild: true, className: "cls_profile_pic_menu_settings cursor-pointer", children: _jsxs(Link, { href: item.href || settings_path, children: [_jsx(Settings, { className: "mr-2 h-4 w-4" }), item.label] }) }, item.id));
175
+ }
176
+ // Generic link handling
177
+ return (_jsx(DropdownMenuItem, { asChild: true, className: "cls_profile_pic_menu_link cursor-pointer", children: _jsx(Link, { href: item.href || "#", children: item.label }) }, item.id));
178
+ }
179
+ return null;
180
+ }) })] }) }) })] }));
181
+ }
182
+ // Default dropdown variant: show profile picture with dropdown menu
148
183
  return (_jsx("div", { className: `cls_profile_pic_menu ${className || ""}`, children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: "cls_profile_pic_menu_trigger focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary rounded-full", "aria-label": "Profile menu", children: _jsxs(Avatar, { className: `cls_profile_pic_menu_avatar ${avatarSizeClasses[avatar_size]} cursor-pointer`, children: [_jsx(AvatarImage, { src: authStatus.profile_picture_url, alt: authStatus.name ? `Profile picture of ${authStatus.name}` : "Profile picture", className: "cls_profile_pic_menu_image" }), _jsx(AvatarFallback, { className: "cls_profile_pic_menu_fallback bg-[var(--hazo-bg-emphasis)] text-[var(--hazo-text-muted)]", children: getInitials() })] }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "cls_profile_pic_menu_dropdown w-56", children: menuItems.map((item) => {
149
184
  if (item.type === "separator") {
150
185
  return _jsx(DropdownMenuSeparator, { className: "cls_profile_pic_menu_separator" }, item.id);
@@ -1,12 +1,14 @@
1
1
  export type ProfilePicMenuWrapperProps = {
2
2
  className?: string;
3
3
  avatar_size?: "default" | "sm" | "lg";
4
+ variant?: "dropdown" | "sidebar";
5
+ sidebar_group_label?: string;
4
6
  };
5
7
  /**
6
8
  * Server wrapper component that loads profile picture menu configuration from hazo_auth_config.ini
7
9
  * and passes it to the client ProfilePicMenu component
8
- * @param props - Component props including className and avatar_size
10
+ * @param props - Component props including className, avatar_size, variant, and sidebar_group_label
9
11
  * @returns ProfilePicMenu component with loaded configuration
10
12
  */
11
- export declare function ProfilePicMenuWrapper({ className, avatar_size, }: ProfilePicMenuWrapperProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function ProfilePicMenuWrapper({ className, avatar_size, variant, sidebar_group_label, }: ProfilePicMenuWrapperProps): import("react/jsx-runtime").JSX.Element;
12
14
  //# sourceMappingURL=profile_pic_menu_wrapper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"profile_pic_menu_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/profile_pic_menu_wrapper.tsx"],"names":[],"mappings":"AAMA,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;CACvC,CAAC;AAGF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,WAAW,GACZ,EAAE,0BAA0B,2CAiB5B"}
1
+ {"version":3,"file":"profile_pic_menu_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/profile_pic_menu_wrapper.tsx"],"names":[],"mappings":"AAMA,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAGF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,WAAW,EACX,OAAoB,EACpB,mBAA+B,GAChC,EAAE,0BAA0B,2CAmB5B"}
@@ -7,10 +7,10 @@ import { get_profile_pic_menu_config } from "../../../../lib/profile_pic_menu_co
7
7
  /**
8
8
  * Server wrapper component that loads profile picture menu configuration from hazo_auth_config.ini
9
9
  * and passes it to the client ProfilePicMenu component
10
- * @param props - Component props including className and avatar_size
10
+ * @param props - Component props including className, avatar_size, variant, and sidebar_group_label
11
11
  * @returns ProfilePicMenu component with loaded configuration
12
12
  */
13
- export function ProfilePicMenuWrapper({ className, avatar_size, }) {
13
+ export function ProfilePicMenuWrapper({ className, avatar_size, variant = "dropdown", sidebar_group_label = "Account", }) {
14
14
  const config = get_profile_pic_menu_config();
15
- return (_jsx(ProfilePicMenu, { show_single_button: config.show_single_button, sign_up_label: config.sign_up_label, sign_in_label: config.sign_in_label, register_path: config.register_path, login_path: config.login_path, settings_path: config.settings_path, logout_path: config.logout_path, custom_menu_items: config.custom_menu_items, className: className, avatar_size: avatar_size }));
15
+ return (_jsx(ProfilePicMenu, { show_single_button: config.show_single_button, sign_up_label: config.sign_up_label, sign_in_label: config.sign_in_label, register_path: config.register_path, login_path: config.login_path, settings_path: config.settings_path, logout_path: config.logout_path, custom_menu_items: config.custom_menu_items, className: className, avatar_size: avatar_size, variant: variant, sidebar_group_label: sidebar_group_label }));
16
16
  }
@@ -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,2CAuL3E"}
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,2CAyK3E"}
@@ -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, Settings, User } from "lucide-react";
8
+ import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User } 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" })] }) }) })] })] }), authStatus.authenticated && (_jsxs(SidebarGroup, { className: "cls_sidebar_layout_account_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Account" }), _jsx(SidebarMenu, { className: "cls_sidebar_layout_account_menu", children: _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_my_settings_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/my_settings", className: "cls_sidebar_layout_my_settings_link flex items-center gap-2", "aria-label": "Open my settings page", children: [_jsx(Settings, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "My Settings" })] }) }) }) })] })), _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(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
  }
@@ -7,6 +7,9 @@ export type AuthStatusData = {
7
7
  last_logon?: string;
8
8
  profile_picture_url?: string;
9
9
  profile_source?: "upload" | "library" | "gravatar" | "custom";
10
+ permissions?: string[];
11
+ permission_ok?: boolean;
12
+ missing_permissions?: string[];
10
13
  loading: boolean;
11
14
  };
12
15
  export type AuthStatus = AuthStatusData & {
@@ -1 +1 @@
1
- {"version":3,"file":"use_auth_status.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/hooks/use_auth_status.ts"],"names":[],"mappings":"AAQA,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;IAC7B,cAAc,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC9D,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,CA+D5C"}
1
+ {"version":3,"file":"use_auth_status.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/hooks/use_auth_status.ts"],"names":[],"mappings":"AAQA,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;IAC7B,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,CAkE5C"}
@@ -21,6 +21,7 @@ export function use_auth_status() {
21
21
  loading: true,
22
22
  });
23
23
  const checkAuth = useCallback(async () => {
24
+ var _a;
24
25
  setAuthStatus((prev) => (Object.assign(Object.assign({}, prev), { loading: true })));
25
26
  try {
26
27
  const response = await fetch("/api/hazo_auth/me", {
@@ -38,6 +39,9 @@ export function use_auth_status() {
38
39
  last_logon: data.last_logon,
39
40
  profile_picture_url: data.profile_picture_url,
40
41
  profile_source: data.profile_source,
42
+ permissions: data.permissions || [],
43
+ permission_ok: (_a = data.permission_ok) !== null && _a !== void 0 ? _a : true,
44
+ missing_permissions: data.missing_permissions,
41
45
  loading: false,
42
46
  });
43
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",