@djangocfg/layouts 2.1.21 → 2.1.23
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/package.json +5 -5
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +2 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +2 -2
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +0 -2
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +0 -2
- package/src/layouts/_components/UserMenu.tsx +15 -31
- package/src/layouts/shared/types.ts +0 -4
- package/src/snippets/McpChat/components/ChatMessages.tsx +12 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.23",
|
|
4
4
|
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
"check": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
|
-
"@djangocfg/api": "^2.1.
|
|
96
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
97
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
95
|
+
"@djangocfg/api": "^2.1.23",
|
|
96
|
+
"@djangocfg/centrifugo": "^2.1.23",
|
|
97
|
+
"@djangocfg/ui-nextjs": "^2.1.23",
|
|
98
98
|
"@hookform/resolvers": "^5.2.0",
|
|
99
99
|
"consola": "^3.4.2",
|
|
100
100
|
"lucide-react": "^0.545.0",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"uuid": "^11.1.0"
|
|
115
115
|
},
|
|
116
116
|
"devDependencies": {
|
|
117
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
117
|
+
"@djangocfg/typescript-config": "^2.1.23",
|
|
118
118
|
"@types/node": "^24.7.2",
|
|
119
119
|
"@types/react": "^19.1.0",
|
|
120
120
|
"@types/react-dom": "^19.1.0",
|
|
@@ -42,6 +42,7 @@ import { SidebarProvider, SidebarInset, Preloader, ButtonLink } from '@djangocfg
|
|
|
42
42
|
import { useAuth } from '@djangocfg/api/auth';
|
|
43
43
|
import { PrivateSidebar, PrivateHeader, PrivateContent } from './components';
|
|
44
44
|
import type { LucideIcon as LucideIconType } from 'lucide-react';
|
|
45
|
+
import { UserMenuConfig } from '../shared/types';
|
|
45
46
|
|
|
46
47
|
export interface SidebarItem {
|
|
47
48
|
label: string;
|
|
@@ -59,6 +60,7 @@ export interface HeaderConfig {
|
|
|
59
60
|
title?: string;
|
|
60
61
|
/** Profile path (optional, defaults to '/profile') */
|
|
61
62
|
profilePath?: string;
|
|
63
|
+
groups?: UserMenuConfig['groups'];
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
export interface PrivateLayoutProps {
|
|
@@ -24,7 +24,6 @@ interface PrivateHeaderProps {
|
|
|
24
24
|
|
|
25
25
|
export function PrivateHeader({ header }: PrivateHeaderProps) {
|
|
26
26
|
const { user, logout } = useAuth();
|
|
27
|
-
const profilePath = header?.profilePath || '/private/profile';
|
|
28
27
|
|
|
29
28
|
return (
|
|
30
29
|
<header
|
|
@@ -51,7 +50,8 @@ export function PrivateHeader({ header }: PrivateHeaderProps) {
|
|
|
51
50
|
{/* User Menu */}
|
|
52
51
|
<UserMenu
|
|
53
52
|
variant="desktop"
|
|
54
|
-
|
|
53
|
+
groups={header?.groups}
|
|
54
|
+
authPath={header?.profilePath}
|
|
55
55
|
/>
|
|
56
56
|
</div>
|
|
57
57
|
</header>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
import React from 'react';
|
|
34
34
|
import Link from 'next/link';
|
|
35
|
-
import { LogOut
|
|
35
|
+
import { LogOut } from 'lucide-react';
|
|
36
36
|
import {
|
|
37
37
|
DropdownMenu,
|
|
38
38
|
DropdownMenuContent,
|
|
@@ -56,17 +56,11 @@ export interface UserMenuProps {
|
|
|
56
56
|
groups?: UserMenuGroup[];
|
|
57
57
|
/** Auth page path (for sign in button) */
|
|
58
58
|
authPath?: string;
|
|
59
|
-
/** @deprecated Use groups instead - Profile page path (backward compatibility) */
|
|
60
|
-
profilePath?: string;
|
|
61
|
-
/** @deprecated Use groups instead - Dashboard page path (backward compatibility) */
|
|
62
|
-
dashboardPath?: string;
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
export function UserMenu({
|
|
66
62
|
variant = 'desktop',
|
|
67
63
|
groups,
|
|
68
|
-
profilePath = '/private/profile',
|
|
69
|
-
dashboardPath = '/private',
|
|
70
64
|
authPath = '/auth',
|
|
71
65
|
}: UserMenuProps) {
|
|
72
66
|
const { user, isAuthenticated, logout } = useAuth();
|
|
@@ -76,32 +70,18 @@ export function UserMenu({
|
|
|
76
70
|
setMounted(true);
|
|
77
71
|
}, []);
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Prepare menu groups (new groups prop or fallback to legacy props)
|
|
73
|
+
// Prepare menu groups
|
|
74
|
+
// Must be before early return to maintain hook order
|
|
84
75
|
const menuGroups: UserMenuGroup[] = React.useMemo(() => {
|
|
85
|
-
|
|
86
|
-
return groups;
|
|
87
|
-
}
|
|
76
|
+
const allGroups: UserMenuGroup[] = [];
|
|
88
77
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (profilePath) {
|
|
93
|
-
legacyGroups.push({
|
|
94
|
-
items: [
|
|
95
|
-
{
|
|
96
|
-
label: 'Profile',
|
|
97
|
-
href: profilePath,
|
|
98
|
-
icon: Settings,
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
});
|
|
78
|
+
// Add custom groups if provided
|
|
79
|
+
if (groups && groups.length > 0) {
|
|
80
|
+
allGroups.push(...groups);
|
|
102
81
|
}
|
|
103
82
|
|
|
104
|
-
|
|
83
|
+
// Always add Sign Out at the end
|
|
84
|
+
allGroups.push({
|
|
105
85
|
items: [
|
|
106
86
|
{
|
|
107
87
|
label: 'Sign Out',
|
|
@@ -112,8 +92,12 @@ export function UserMenu({
|
|
|
112
92
|
],
|
|
113
93
|
});
|
|
114
94
|
|
|
115
|
-
return
|
|
116
|
-
}, [groups,
|
|
95
|
+
return allGroups;
|
|
96
|
+
}, [groups, logout]);
|
|
97
|
+
|
|
98
|
+
if (!mounted) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
117
101
|
|
|
118
102
|
if (!isAuthenticated || !user) {
|
|
119
103
|
// Guest user - show sign in button
|
|
@@ -206,10 +206,6 @@ export interface UserMenuGroup {
|
|
|
206
206
|
export interface UserMenuConfig {
|
|
207
207
|
/** Menu groups for authenticated users */
|
|
208
208
|
groups?: UserMenuGroup[];
|
|
209
|
-
/** Profile page path (used when no groups provided - backward compatibility) */
|
|
210
|
-
profilePath?: string;
|
|
211
|
-
/** Dashboard page path (used when no groups provided - backward compatibility) */
|
|
212
|
-
dashboardPath?: string;
|
|
213
209
|
/** Auth page path (for sign in button) */
|
|
214
210
|
authPath?: string;
|
|
215
211
|
}
|
|
@@ -93,12 +93,21 @@ export const ChatMessages = forwardRef<ChatMessagesHandle, ChatMessagesProps>(
|
|
|
93
93
|
// Initial scroll when history loads (instant, no animation)
|
|
94
94
|
useEffect(() => {
|
|
95
95
|
if (!isLoading && messages.length > 0 && !hasScrolledOnLoad.current) {
|
|
96
|
+
// Double RAF to ensure DOM is fully updated
|
|
96
97
|
requestAnimationFrame(() => {
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
requestAnimationFrame(() => {
|
|
99
|
+
// Verify messages are actually in DOM before scrolling
|
|
100
|
+
if (messagesContainerRef.current) {
|
|
101
|
+
const messageElements = messagesContainerRef.current.querySelectorAll('[data-message-bubble]');
|
|
102
|
+
if (messageElements.length === messages.length) {
|
|
103
|
+
scrollToLastMessage(true);
|
|
104
|
+
hasScrolledOnLoad.current = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
99
108
|
});
|
|
100
109
|
}
|
|
101
|
-
}, [isLoading, messages.length, scrollToLastMessage]);
|
|
110
|
+
}, [isLoading, messages.length, scrollToLastMessage, messages]);
|
|
102
111
|
|
|
103
112
|
// Scroll to last message on new messages (smooth animation, shows top of message)
|
|
104
113
|
useEffect(() => {
|