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 +133 -1
- package/SETUP_CHECKLIST.md +34 -2
- package/dist/app/api/hazo_auth/me/route.d.ts +30 -1
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +76 -16
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts +6 -2
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu.js +39 -4
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.d.ts +4 -2
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.js +3 -3
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +2 -2
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts +3 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.js +4 -0
- package/package.json +1 -1
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
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -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
|
-
|
|
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":"
|
|
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 {
|
|
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
|
|
9
|
-
const
|
|
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
|
-
//
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
*
|
|
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":"
|
|
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,
|
|
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
|
-
*
|
|
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 -
|
|
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
|
|
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;
|
|
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
|
|
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,
|
|
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,
|
|
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" })] }) }) })] })] }),
|
|
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,
|
|
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
|
}
|