Files
lms-sb/src/app/(student)/layout.tsx
T
admins 39e2102a18 Gate Obsidian Toolbox behind TOOLBOX_VISIBLE flag
Toolbox is still being refined; hide it from clients on prod while keeping it
usable on staging. Entry points (header link, dashboard card) render only when
TOOLBOX_VISIBLE=true; a tools/layout redirects /tools/* to /dashboard otherwise.
Routes and ToolUsage table are untouched (no destructive migration).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:15:42 +05:00

107 lines
4.5 KiB
TypeScript

import { headers } from "next/headers";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
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");
const isImpersonating = !!(session.session as { impersonatedBy?: string }).impersonatedBy;
// Force password change for auto-provisioned buyers (temp password).
// Skip while an admin is impersonating — they only view the client's cabinet;
// the client sets their own password on a real first login.
const dbUser = await prisma.user.findUnique({
where: { id: session.user.id },
select: { mustChangePassword: true },
});
if (dbUser?.mustChangePassword && !isImpersonating) redirect("/change-password");
// 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"),
]);
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="/wrapped" className="text-sm hover:underline" style={{ color: "var(--muted-foreground)" }}>
Wrapped
</Link>
{process.env.TOOLBOX_VISIBLE === "true" && (
<Link href="/tools" className="text-sm hover:underline" style={{ color: "var(--muted-foreground)" }}>
Инструменты
</Link>
)}
<Link href="/questions" className="text-sm hover:underline" style={{ color: "var(--muted-foreground)" }}>
Вопросы
</Link>
<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>
);
}