2026-03-07 17:32:08 +00:00

172 lines
6.8 KiB
TypeScript

import FormContainer from "@/components/FormContainer";
import Table from "@/components/Table";
import { getSupabaseClient } from "@/lib/supabase";
import { Tables } from "@/types/supabase";
import { auth } from "@clerk/nextjs/server";
import { notFound } from "next/navigation";
import GenerateLessonsForm from "@/components/forms/GenerateLessonsForm";
type EntryList = Tables<"TeacherTimetableEntry"> & {
class: Tables<"Class"> | null;
subject: Tables<"Subject"> | null;
slot: Tables<"SchoolTimetableSlot"> | null;
};
const SingleTemplatePage = async ({
params: { id },
}: {
params: { id: string };
}) => {
const { sessionClaims, userId } = auth();
const role = (sessionClaims?.metadata as { role?: string })?.role;
const schoolId = (sessionClaims?.metadata as { schoolId?: string })?.schoolId;
const teacherType = (sessionClaims?.metadata as { teacherType?: string })?.teacherType;
const canManageSchool =
role === "admin" ||
(role === "teacher" && (teacherType === "INDEPENDENT" || teacherType === "AGENCY"));
const isTemplateOwner = (tid: string) => tid === userId;
const supabase = await getSupabaseClient();
// Fetch the template
const { data: template, error: templateError } = await supabase
.from("TeacherTimetableTemplate")
.select("*, teacher:Teacher(*)")
.eq("id", parseInt(id))
.single();
if (templateError || !template) {
return notFound();
}
// Ensure it belongs to the current school
if (schoolId && template.schoolId !== schoolId) {
return notFound();
}
// Fetch the entries
const { data: rawEntries } = await supabase
.from("TeacherTimetableEntry")
.select("*, class:Class(*), subject:Subject(*), slot:SchoolTimetableSlot(*)")
.eq("teacherTimetableTemplateId", template.id)
.order("dayOfWeek", { ascending: true });
const entries = (rawEntries || []) as unknown as EntryList[];
// Sort entries by day then by slot position
entries.sort((a, b) => {
if (a.dayOfWeek !== b.dayOfWeek) return a.dayOfWeek - b.dayOfWeek;
const posA = (a.slot as { position?: number })?.position ?? 0;
const posB = (b.slot as { position?: number })?.position ?? 0;
return posA - posB;
});
const schoolForTerms = schoolId || template.schoolId;
const terms = schoolForTerms
? (await supabase
.from("Term")
.select("id, name, startDate, endDate")
.eq("schoolId", schoolForTerms)
.order("startDate", { ascending: true })).data ?? []
: [];
const columns = [
{ header: "Day", accessor: "dayOfWeek" },
{ header: "Time Slot", accessor: "slot" },
{ header: "Class", accessor: "class", className: "hidden md:table-cell" },
{ header: "Subject", accessor: "subject", className: "hidden md:table-cell" },
...(role === "admin"
? [{ header: "Actions", accessor: "action" }]
: []),
];
const getDayName = (day: number) => {
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
// JS days: 0 = Sunday, 1 = Monday. We'll map assuming 1 = Monday like our form (1-7).
if (day >= 1 && day <= 7) {
// Just map standard ISO 1=Monday... 7=Sunday
const isoDays = ["", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
return isoDays[day];
}
return "Unknown";
}
const renderRow = (item: EntryList) => (
<tr
key={item.id}
className="border-b border-gray-200 even:bg-slate-50 text-sm hover:bg-lamaPurpleLight"
>
<td className="p-4">{getDayName(item.dayOfWeek)}</td>
<td>
{item.slot ? `${item.slot.name} (${item.slot.startTime}-${item.slot.endTime})` : "-"}
</td>
<td className="hidden md:table-cell">{item.class?.name || "-"}</td>
<td className="hidden md:table-cell">{item.subject?.name || "-"}</td>
<td>
<div className="flex items-center gap-2">
{role === "admin" && (
<>
<FormContainer
table="timetableEntry"
type="update"
data={item}
/>
<FormContainer
table="timetableEntry"
type="delete"
id={item.id}
/>
</>
)}
</div>
</td>
</tr>
);
return (
<div className="flex-1 p-4 flex flex-col gap-4 xl:flex-row">
{/* LEFT */}
<div className="w-full xl:w-2/3 flex flex-col gap-8">
{/* TOP INFO */}
<div className="bg-lamaSky rounded-md p-4 flex gap-4">
<div className="w-2/3 flex flex-col justify-between gap-4">
<h1 className="text-xl font-semibold">{template.name}</h1>
<p className="text-sm text-gray-500">
Teacher Owner: {template.teacher ? `${template.teacher.name} ${template.teacher.surname}` : "Unknown"}
</p>
</div>
</div>
{/* GENERATE LESSONS: template owner or admin; use template.schoolId when session school not set (e.g. admin) */}
{canManageSchool && (role === "admin" || isTemplateOwner(template.teacherId)) && (schoolId || template.schoolId) && (
<GenerateLessonsForm
templateId={template.id}
schoolId={schoolId || template.schoolId}
terms={terms}
/>
)}
{/* ENTRIES LIST */}
<div className="bg-white rounded-md p-4">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold">Timetable Entries</h2>
{role === "admin" && (
<FormContainer
table="timetableEntry"
type="create"
// Pre-fill the template ID
data={{ timetableTemplateId: template.id, teacherTimetableTemplateId: template.id }}
/>
)}
</div>
<Table columns={columns} renderRow={renderRow} data={entries} />
</div>
</div>
</div>
);
};
export default SingleTemplatePage;