51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import { useNavigate } from "react-router-dom";
|
|
import { useState, type ReactNode } from "react";
|
|
import { useAuth } from "../auth/AuthContext";
|
|
|
|
type AppShellProps = {
|
|
children: ReactNode;
|
|
variant?: "admin" | "mobile";
|
|
hasPrimaryAction?: boolean;
|
|
projectName?: string;
|
|
};
|
|
|
|
export function AppShell({ children, variant = "admin", hasPrimaryAction = false, projectName }: AppShellProps) {
|
|
const { user, logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
const [loggingOut, setLoggingOut] = useState(false);
|
|
|
|
const handleLogout = async () => {
|
|
setLoggingOut(true);
|
|
try {
|
|
await logout();
|
|
navigate(variant === "admin" ? "/admin/login" : "/staff/login", { replace: true });
|
|
} finally {
|
|
setLoggingOut(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={`app-shell app-shell--${variant}${hasPrimaryAction ? "" : " app-shell--no-primary-action"}`}>
|
|
<a className="skip-link" href="#main-content">跳到主要内容</a>
|
|
<header className="app-header">
|
|
<div className="brand-lockup" aria-label="景区排队叫号系统">
|
|
<span className="brand-logo-frame" aria-hidden="true"><img className="brand-logo" src="/xiaoqikong-logo.jpg" alt="" /></span>
|
|
<span className="brand-lockup__text">
|
|
<strong>{projectName || "景区排队叫号系统"}</strong>
|
|
{projectName ? <small>景区排队叫号系统</small> : null}
|
|
</span>
|
|
</div>
|
|
<div className="account-menu">
|
|
<span>
|
|
<strong>{user?.display_name || user?.username}</strong>
|
|
</span>
|
|
<button className="button button--ghost button--small" onClick={handleLogout} disabled={loggingOut}>
|
|
{loggingOut ? "正在退出" : "退出"}
|
|
</button>
|
|
</div>
|
|
</header>
|
|
<main id="main-content" className="app-main">{children}</main>
|
|
</div>
|
|
);
|
|
}
|