Spaces:
Sleeping
Sleeping
Update AppLayout.tsx
Browse files
client/src/components/Layout/AppLayout.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { ReactNode } from 'react';
|
| 2 |
import { useAuthStore } from '../../store/authStore';
|
| 3 |
import { useNavigate, useLocation } from 'react-router-dom';
|
| 4 |
import {
|
|
@@ -17,6 +17,18 @@ export default function AppLayout({ children, sidebarContent, showSidebar }: App
|
|
| 17 |
const navigate = useNavigate();
|
| 18 |
const location = useLocation();
|
| 19 |
const isEditor = location.pathname.startsWith('/project/');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
return (
|
| 22 |
<div className="min-h-screen bg-surface-950 flex flex-col">
|
|
@@ -46,16 +58,33 @@ export default function AppLayout({ children, sidebarContent, showSidebar }: App
|
|
| 46 |
<span className="text-surface-300 text-sm hidden md:block">
|
| 47 |
{user.username}
|
| 48 |
</span>
|
| 49 |
-
<div className="
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
</div>
|
| 52 |
-
<button
|
| 53 |
-
onClick={() => { logout(); navigate('/login'); }}
|
| 54 |
-
className="btn-ghost p-2"
|
| 55 |
-
title="Logout"
|
| 56 |
-
>
|
| 57 |
-
<LogOut className="w-4 h-4" />
|
| 58 |
-
</button>
|
| 59 |
</>
|
| 60 |
)}
|
| 61 |
</div>
|
|
|
|
| 1 |
+
import { ReactNode, useState, useRef, useEffect } from 'react';
|
| 2 |
import { useAuthStore } from '../../store/authStore';
|
| 3 |
import { useNavigate, useLocation } from 'react-router-dom';
|
| 4 |
import {
|
|
|
|
| 17 |
const navigate = useNavigate();
|
| 18 |
const location = useLocation();
|
| 19 |
const isEditor = location.pathname.startsWith('/project/');
|
| 20 |
+
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
| 21 |
+
const profileRef = useRef<HTMLDivElement>(null);
|
| 22 |
+
|
| 23 |
+
useEffect(() => {
|
| 24 |
+
const handleClickOutside = (e: MouseEvent) => {
|
| 25 |
+
if (profileRef.current && !profileRef.current.contains(e.target as Node)) {
|
| 26 |
+
setShowProfileMenu(false);
|
| 27 |
+
}
|
| 28 |
+
};
|
| 29 |
+
document.addEventListener('mousedown', handleClickOutside);
|
| 30 |
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
| 31 |
+
}, []);
|
| 32 |
|
| 33 |
return (
|
| 34 |
<div className="min-h-screen bg-surface-950 flex flex-col">
|
|
|
|
| 58 |
<span className="text-surface-300 text-sm hidden md:block">
|
| 59 |
{user.username}
|
| 60 |
</span>
|
| 61 |
+
<div className="relative" ref={profileRef}>
|
| 62 |
+
<button
|
| 63 |
+
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
| 64 |
+
className="w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center text-white text-sm font-medium hover:bg-primary-500 transition-colors"
|
| 65 |
+
>
|
| 66 |
+
{user.username.charAt(0).toUpperCase()}
|
| 67 |
+
</button>
|
| 68 |
+
{showProfileMenu && (
|
| 69 |
+
<div className="absolute right-0 top-full mt-2 w-48 bg-surface-800 border border-surface-700 rounded-lg shadow-xl py-1 z-50">
|
| 70 |
+
<button
|
| 71 |
+
onClick={() => { setShowProfileMenu(false); /* TODO: navigate to settings */ }}
|
| 72 |
+
className="w-full flex items-center gap-2 px-4 py-2 text-sm text-surface-200 hover:bg-surface-700 transition-colors"
|
| 73 |
+
>
|
| 74 |
+
<Settings className="w-4 h-4" />
|
| 75 |
+
Settings
|
| 76 |
+
</button>
|
| 77 |
+
<div className="border-t border-surface-700 my-1" />
|
| 78 |
+
<button
|
| 79 |
+
onClick={() => { logout(); navigate('/login'); }}
|
| 80 |
+
className="w-full flex items-center gap-2 px-4 py-2 text-sm text-red-400 hover:bg-surface-700 transition-colors"
|
| 81 |
+
>
|
| 82 |
+
<LogOut className="w-4 h-4" />
|
| 83 |
+
Logout
|
| 84 |
+
</button>
|
| 85 |
+
</div>
|
| 86 |
+
)}
|
| 87 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
</>
|
| 89 |
)}
|
| 90 |
</div>
|