Files
pwa/src/components/admin/AdminLayout.tsx
T

107 lines
4.3 KiB
TypeScript
Raw Normal View History

import { useState } from 'react';
import { Link, Outlet, useNavigate } from 'react-router-dom';
import { LogOut, Moon, Sun } from 'lucide-react';
import { useAuth } from '@/hooks/useAuth';
import { useTheme } from '@/hooks/useTheme';
import { APP_NAME } from '@/utils/constants';
import { cn } from '@/lib/utils';
/**
* Full-viewport admin shell — not the mobile PWA frame.
* Journey AppHeader / app-mobile-shell are intentionally absent.
*/
export function AdminLayout() {
const { user, signOut } = useAuth();
const { toggleTheme, isDark } = useTheme();
const navigate = useNavigate();
const [confirmLogout, setConfirmLogout] = useState(false);
const handleSignOut = async () => {
await signOut();
setConfirmLogout(false);
navigate('/login', { replace: true });
};
return (
<div className="admin-app-shell flex h-dvh min-h-0 flex-col bg-slate-100 text-slate-900 dark:bg-slate-950 dark:text-slate-50">
<header className="shrink-0 border-b border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900">
<div className="mx-auto flex h-14 w-full max-w-6xl items-center justify-between gap-3 px-4">
<div className="min-w-0">
<p className="truncate text-sm font-semibold tracking-tight">
{APP_NAME}
<span className="mx-2 text-slate-300 dark:text-slate-600">|</span>
<span className="text-slate-600 dark:text-slate-300">پنل ادمین</span>
</p>
{user?.email ? (
<p className="truncate text-[11px] text-slate-500" dir="ltr">
{user.email}
</p>
) : null}
</div>
<div className="flex shrink-0 items-center gap-2">
<Link
to="/"
className={cn(
'rounded-lg border border-slate-200 px-3 py-1.5 text-xs text-slate-700',
'hover:bg-slate-50 dark:border-slate-600 dark:text-slate-200 dark:hover:bg-slate-800',
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
)}
>
بازگشت به اپ
</Link>
<button
type="button"
onClick={toggleTheme}
className="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-slate-200 text-slate-600 dark:border-slate-600 dark:text-slate-300"
aria-label={isDark ? 'حالت روشن' : 'حالت تاریک'}
>
{isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
</button>
<button
type="button"
onClick={() => setConfirmLogout(true)}
className="inline-flex h-9 items-center gap-1.5 rounded-lg border border-slate-200 px-3 text-xs text-slate-700 dark:border-slate-600 dark:text-slate-200"
aria-label="خروج از حساب"
>
<LogOut className="h-3.5 w-3.5" />
خروج
</button>
</div>
</div>
</header>
<main className="min-h-0 flex-1 overflow-hidden">
<Outlet />
</main>
{confirmLogout ? (
<div className="fixed inset-0 z-[60] flex items-end justify-center bg-slate-900/40 p-4 sm:items-center">
<div className="w-full max-w-sm space-y-4 rounded-xl border border-slate-200 bg-white p-5 shadow-lg dark:border-slate-700 dark:bg-slate-900">
<h2 className="text-base font-semibold">خروج از پنل ادمین</h2>
<p className="text-sm text-slate-600 dark:text-slate-300">
از حساب کاربری خارج می‌شوید؟
</p>
<div className="flex gap-2">
<button
type="button"
onClick={() => setConfirmLogout(false)}
className="flex-1 rounded-lg border border-slate-200 px-3 py-2 text-sm dark:border-slate-600"
>
انصراف
</button>
<button
type="button"
onClick={() => void handleSignOut()}
className="flex-1 rounded-lg bg-slate-900 px-3 py-2 text-sm text-white dark:bg-slate-100 dark:text-slate-900"
>
خروج
</button>
</div>
</div>
</div>
) : null}
</div>
);
}