Add 8 files, update 14 files

This commit is contained in:
[dyad] committed 2026-08-02 14:00:21 +02:00
1 parent 669e029f45
commit c84b080fb8
22 files changed
+732 -101

No files matched your search

+4 -4
View File
@@ -89,8 +89,8 @@ export async function PUT(
const userId = session.userId;
const body = await request.json();
// PARENT can approve/reject pending reports
if (body.parentApprovalStatus && (role === 'PARENT' || role === 'SCHOOL_ADMIN' || role === 'VICE_PRINCIPAL')) {
// Parent approval remains parent-only. Final approval is handled by /approve.
if (body.parentApprovalStatus && role === 'PARENT') {
const newStatus = body.parentApprovalStatus;
if (newStatus !== 'approved' && newStatus !== 'rejected') {
return NextResponse.json({ error: 'Invalid approval status' }, { status: 400 });
@@ -112,8 +112,8 @@ export async function PUT(
parentApprovedBy: userId,
parentApprovedAt: new Date(),
isVisibleToTeacher: newStatus === 'approved',
isVisibleToAdmin: newStatus === 'approved',
status: newStatus === 'approved' ? 'approved' : 'rejected',
isVisibleToAdmin: false,
status: newStatus === 'approved' ? 'pending' : 'rejected',
},
include: {
student: { select: { id: true, firstName: true, lastName: true } },
+69 -17
View File
@@ -1,6 +1,7 @@
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { getSession } from '@/lib/auth';
import { canAdminApprove, canCreateCalendarEvent, canParentApprove } from '@/lib/leave-policy';
export async function POST(request: Request) {
try {
@@ -12,8 +13,8 @@ export async function POST(request: Request) {
const role = session.user?.role;
const userId = session.userId;
if (role !== 'PARENT' && role !== 'SCHOOL_ADMIN' && role !== 'VICE_PRINCIPAL') {
return NextResponse.json({ error: 'Only parents or admins can approve' }, { status: 403 });
if (!canParentApprove(role) && !canAdminApprove(role)) {
return NextResponse.json({ error: 'Only parents or administrators can approve leave requests' }, { status: 403 });
}
const body = await request.json();
@@ -32,12 +33,10 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Report not found' }, { status: 404 });
}
if (report.parentApprovalStatus !== 'pending') {
return NextResponse.json({ error: 'Report already processed' }, { status: 400 });
}
// Verify parent is linked to this student
if (role === 'PARENT') {
if (report.parentApprovalStatus !== 'pending') {
return NextResponse.json({ error: 'Parent approval has already been processed' }, { status: 400 });
}
const parentLink = await db.parentStudentLink.findFirst({
where: { parentId: userId, studentId: report.studentId },
});
@@ -46,25 +45,78 @@ export async function POST(request: Request) {
}
}
const newStatus = action === 'approve' ? 'approved' : 'rejected';
const newStatus = action === 'approve' ? 'approved' : 'rejected';
const updated = await db.illnessReport.update({
where: { id: reportId },
data: {
parentApprovalStatus: newStatus,
parentApprovedBy: userId,
parentApprovedAt: new Date(),
isVisibleToTeacher: newStatus === 'approved',
status: newStatus === 'approved' ? 'pending' : 'rejected',
},
include: { student: { include: { enrollments: { where: { endDate: null }, include: { classGroup: { select: { responsibleTeacherId: true } } }, take: 1 } } } },
});
if (newStatus === 'approved') {
const teacherId = updated.student.enrollments[0]?.classGroup.responsibleTeacherId;
if (teacherId) {
await db.notification.create({
data: {
schoolId: updated.schoolId,
userId: teacherId,
type: 'LEAVE_PARENT_APPROVED',
category: 'administrative',
title: 'Student leave request approved by parent',
message: `${updated.student.firstName} ${updated.student.lastName} has an approved leave request awaiting administration approval.`,
relatedId: updated.id,
},
});
await db.illnessReport.update({ where: { id: updated.id }, data: { teacherNotifiedAt: new Date() } });
}
}
return NextResponse.json(updated);
}
if (report.parentApprovalStatus !== 'approved' || report.adminApprovalStatus !== 'pending') {
return NextResponse.json({ error: 'This request is not ready for administrator approval' }, { status: 400 });
}
if (session.user?.schoolId && report.schoolId !== session.user.schoolId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const adminStatus = action === 'approve' ? 'approved' : 'rejected';
const updated = await db.illnessReport.update({
where: { id: reportId },
data: {
parentApprovalStatus: newStatus,
parentApprovedBy: userId,
parentApprovedAt: new Date(),
isVisibleToTeacher: newStatus === 'approved',
isVisibleToAdmin: newStatus === 'approved',
status: newStatus === 'approved' ? 'approved' : 'rejected',
adminApprovalStatus: adminStatus,
adminApprovedBy: userId,
adminApprovedAt: new Date(),
isVisibleToAdmin: true,
status: adminStatus,
},
include: {
student: { select: { id: true, firstName: true, lastName: true } },
reporter: { select: { id: true, firstName: true, lastName: true } },
approver: { select: { id: true, firstName: true, lastName: true } },
student: { include: { enrollments: { where: { endDate: null }, include: { classGroup: true }, take: 1 } } },
},
});
if (canCreateCalendarEvent(updated)) {
const classGroup = updated.student.enrollments[0]?.classGroup;
const event = await db.calendarEvent.create({
data: {
schoolId: updated.schoolId,
teacherId: userId,
title: `${updated.student.firstName} ${updated.student.lastName}: approved leave`,
date: updated.startDate,
eventType: 'leave',
classGroupId: classGroup?.id ?? null,
notes: `Leave type: ${updated.leaveType}`,
allDay: true,
},
});
await db.illnessReport.update({ where: { id: updated.id }, data: { calendarEventId: event.id } });
}
return NextResponse.json(updated);
} catch (error) {
console.error('IllnessReport approve error:', error);
+7 -5
View File
@@ -99,7 +99,7 @@ export async function POST(request: Request) {
}
const body = await request.json();
const { studentId, reason, description, startDate, endDate, documentUrl } = body;
const { studentId, reason, description, startDate, endDate, documentUrl, leaveType = 'illness' } = body;
if (!studentId || !reason || !startDate) {
return NextResponse.json(
@@ -141,8 +141,8 @@ export async function POST(request: Request) {
const reporterType = role === 'STUDENT' ? 'student' : 'parent';
const isParentReport = reporterType === 'parent';
// When student reports: needs parent approval, not visible to teacher/admin
// When parent reports: auto-approved, visible to teacher/admin
// Both student and parent requests require the final administrator approval.
// Parent-submitted requests satisfy the parent approval step immediately.
const report = await db.illnessReport.create({
data: {
schoolId: student.schoolId,
@@ -154,12 +154,14 @@ export async function POST(request: Request) {
startDate: new Date(startDate),
endDate: endDate ? new Date(endDate) : null,
documentUrl: documentUrl || null,
leaveType,
parentApprovalStatus: isParentReport ? 'approved' : 'pending',
parentApprovedBy: isParentReport ? userId : null,
parentApprovedAt: isParentReport ? new Date() : null,
isVisibleToTeacher: isParentReport,
isVisibleToAdmin: isParentReport,
status: isParentReport ? 'approved' : 'pending',
isVisibleToAdmin: false,
adminApprovalStatus: 'pending',
status: 'pending',
},
include: {
student: { select: { id: true, firstName: true, lastName: true } },