theme draft 1

This commit is contained in:
2026-03-22 13:05:00 -04:00
parent 63ad73755d
commit d38392ac9a
18 changed files with 678 additions and 209 deletions

View File

@@ -3,11 +3,16 @@ import { Link, useLocation, Outlet, Navigate } from 'react-router-dom';
import { useGetMe } from '../generated/anthoLumeAPIV1';
import { useAuth } from '../auth/AuthContext';
import { UserIcon, DropdownIcon } from '../icons';
import { useTheme } from '../theme/ThemeProvider';
import type { ThemeMode } from '../utils/localSettings';
import HamburgerMenu from './HamburgerMenu';
const themeModes: ThemeMode[] = ['light', 'dark', 'system'];
export default function Layout() {
const location = useLocation();
const { isAuthenticated, user, logout, isCheckingAuth } = useAuth();
const { themeMode, setThemeMode } = useTheme();
const { data } = useGetMe(isAuthenticated ? {} : undefined);
const fetchedUser =
data?.status === 200 && data.data && 'username' in data.data ? data.data : null;
@@ -20,7 +25,6 @@ export default function Layout() {
setIsUserDropdownOpen(false);
};
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
@@ -34,7 +38,6 @@ export default function Layout() {
};
}, []);
// Get current page title
const navItems = [
{ path: '/admin/import-results', title: 'Admin - Import' },
{ path: '/admin/import', title: 'Admin - Import' },
@@ -57,43 +60,62 @@ export default function Layout() {
document.title = `AnthoLume - ${currentPageTitle}`;
}, [currentPageTitle]);
// Show loading while checking authentication status
if (isCheckingAuth) {
return <div className="text-gray-500 dark:text-white">Loading...</div>;
return <div className="text-content-muted">Loading...</div>;
}
// Redirect to login if not authenticated
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return (
<div className="min-h-screen bg-gray-100 dark:bg-gray-800">
{/* Header */}
<div className="min-h-screen bg-canvas">
<div className="flex h-16 w-full items-center justify-between">
{/* Mobile Navigation Button with CSS animations */}
<HamburgerMenu />
{/* Header Title */}
<h1 className="whitespace-nowrap px-6 text-xl font-bold lg:ml-44 dark:text-white">
<h1 className="whitespace-nowrap px-6 text-xl font-bold text-content lg:ml-44">
{currentPageTitle}
</h1>
{/* User Dropdown */}
<div
className="relative flex w-full items-center justify-end space-x-4 p-4"
ref={dropdownRef}
>
<button
onClick={() => setIsUserDropdownOpen(!isUserDropdownOpen)}
className="relative block text-gray-800 dark:text-gray-200"
className="relative block text-content"
>
<UserIcon size={20} />
</button>
{isUserDropdownOpen && (
<div className="absolute right-4 top-16 z-20 pt-4 transition duration-200">
<div className="w-40 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black/5 dark:bg-gray-700 dark:shadow-gray-800">
<div className="w-64 origin-top-right rounded-md bg-surface shadow-lg ring-1 ring-black/5 dark:shadow-gray-800">
<div
className="border-b border-border px-4 py-3"
role="group"
aria-label="Theme mode"
>
<p className="mb-2 text-xs font-semibold uppercase tracking-wide text-content-subtle">
Theme
</p>
<div className="inline-flex w-full rounded border border-border bg-surface-muted p-1">
{themeModes.map(mode => (
<button
key={mode}
type="button"
onClick={() => setThemeMode(mode)}
className={`flex-1 rounded px-2 py-1 text-xs font-medium capitalize transition-colors ${
themeMode === mode
? 'bg-content text-content-inverse'
: 'text-content-muted hover:bg-surface hover:text-content'
}`}
>
{mode}
</button>
))}
</div>
</div>
<div
className="py-1"
role="menu"
@@ -103,7 +125,7 @@ export default function Layout() {
<Link
to="/settings"
onClick={() => setIsUserDropdownOpen(false)}
className="block px-4 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
className="block px-4 py-2 text-content-muted hover:bg-surface-muted hover:text-content"
role="menuitem"
>
<span className="flex flex-col">
@@ -112,7 +134,7 @@ export default function Layout() {
</Link>
<button
onClick={handleLogout}
className="block w-full px-4 py-2 text-left text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
className="block w-full px-4 py-2 text-left text-content-muted hover:bg-surface-muted hover:text-content"
role="menuitem"
>
<span className="flex flex-col">
@@ -126,11 +148,11 @@ export default function Layout() {
<button
onClick={() => setIsUserDropdownOpen(!isUserDropdownOpen)}
className="flex cursor-pointer items-center gap-2 py-4 text-gray-500 dark:text-white"
className="flex cursor-pointer items-center gap-2 py-4 text-content-muted"
>
<span>{userData ? ('username' in userData ? userData.username : 'User') : 'User'}</span>
<span
className="text-gray-800 transition-transform duration-200 dark:text-gray-200"
className="text-content transition-transform duration-200"
style={{ transform: isUserDropdownOpen ? 'rotate(180deg)' : 'rotate(0deg)' }}
>
<DropdownIcon size={20} />
@@ -139,7 +161,6 @@ export default function Layout() {
</div>
</div>
{/* Main Content */}
<main
className="relative overflow-hidden"
style={{ height: 'calc(100dvh - 4rem - env(safe-area-inset-top))' }}