c445bfacad
- New page /profile: shows name/email and password change form - Uses authClient.changePassword (current + new + confirm) - Student name in header is now a link to /profile Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
86 lines
3.5 KiB
TypeScript
86 lines
3.5 KiB
TypeScript
import { headers } from "next/headers";
|
|
import { auth } from "@/lib/auth";
|
|
import { redirect } from "next/navigation";
|
|
import Link from "next/link";
|
|
import { LogoutButton } from "@/components/layout/logout-button";
|
|
import { getSetting } from "@/lib/settings";
|
|
import { StopImpersonateBanner } from "@/components/admin/stop-impersonate-banner";
|
|
|
|
export default async function StudentLayout({ children }: { children: React.ReactNode }) {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
if (!session) redirect("/login");
|
|
|
|
// Maintenance mode: non-admin users see the maintenance page
|
|
if (session.user.role !== "admin") {
|
|
const maintenance = await getSetting("maintenanceMode");
|
|
if (maintenance === "true") redirect("/maintenance");
|
|
}
|
|
|
|
const [schoolName, logoUrl, showLogo, socialYoutube, socialVk, socialTelegram, orgRequisites] =
|
|
await Promise.all([
|
|
getSetting("schoolName"),
|
|
getSetting("logoUrl"),
|
|
getSetting("showLogo"),
|
|
getSetting("socialYoutube"),
|
|
getSetting("socialVk"),
|
|
getSetting("socialTelegram"),
|
|
getSetting("orgRequisites"),
|
|
]);
|
|
|
|
const isImpersonating = !!(session.session as { impersonatedBy?: string }).impersonatedBy;
|
|
|
|
return (
|
|
<div className="min-h-screen flex flex-col" style={{ backgroundColor: "var(--background)" }}>
|
|
{isImpersonating && <StopImpersonateBanner userName={session.user.name} />}
|
|
<header
|
|
className="sticky top-0 z-10 flex items-center justify-between px-6 py-3"
|
|
style={{ borderBottom: "2px solid var(--border)", backgroundColor: "var(--background)" }}
|
|
>
|
|
<Link href="/dashboard" className="flex items-center gap-2 font-bold tracking-wide" style={{ color: "var(--foreground)" }}>
|
|
{logoUrl && showLogo === "true" && (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img src={logoUrl} alt={schoolName} className="h-6 w-auto object-contain" />
|
|
)}
|
|
{schoolName}
|
|
</Link>
|
|
<div className="flex items-center gap-4">
|
|
<Link href="/profile" className="text-sm hover:underline" style={{ color: "var(--muted-foreground)" }}>
|
|
{session.user.name}
|
|
</Link>
|
|
<LogoutButton />
|
|
</div>
|
|
</header>
|
|
<div className="flex-1 flex flex-col">{children}</div>
|
|
{(socialYoutube || socialVk || socialTelegram || orgRequisites) && (
|
|
<footer
|
|
className="px-6 py-4 flex flex-col sm:flex-row items-center justify-between gap-3 text-xs"
|
|
style={{ borderTop: "2px solid var(--border)", color: "var(--muted-foreground)" }}
|
|
>
|
|
{orgRequisites && (
|
|
<p className="whitespace-pre-line text-center sm:text-left">{orgRequisites}</p>
|
|
)}
|
|
{(socialYoutube || socialVk || socialTelegram) && (
|
|
<div className="flex items-center gap-4 flex-shrink-0">
|
|
{socialYoutube && (
|
|
<a href={socialYoutube} target="_blank" rel="noopener noreferrer" className="hover:underline">
|
|
YouTube
|
|
</a>
|
|
)}
|
|
{socialVk && (
|
|
<a href={socialVk} target="_blank" rel="noopener noreferrer" className="hover:underline">
|
|
VK
|
|
</a>
|
|
)}
|
|
{socialTelegram && (
|
|
<a href={socialTelegram} target="_blank" rel="noopener noreferrer" className="hover:underline">
|
|
Telegram
|
|
</a>
|
|
)}
|
|
</div>
|
|
)}
|
|
</footer>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|