172 lines
6.8 KiB
TypeScript
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;
|