Round 23: Report Card Generator, Seating Chart, School Library, Parent Portal Enhancement

- Report Card Generator: Professional report cards with PDF export
  - Subject grades table with competency levels
  - Competency radar chart, teacher comments
  - Template management, batch generation
  - Status workflow: draft → review → published → archived

- Seating Chart View: Visual classroom layout
  - Grid-based classroom with desk positions
  - Multiple layout templates (rows, groups, U-shape, circle)
  - Click-to-assign student placement
  - Randomize and smart arrange features
  - Student info popover

- School Library View: Book lending system
  - Book catalog with search and filter
  - Checkout system with due dates and renewals
  - Overdue tracking with notifications
  - Reservation queue system
  - Statistics dashboard with charts

- Enhanced Parent Portal: Comprehensive dashboard
  - Child overview with multiple children selector
  - Academic progress with competency radar chart
  - Attendance & illness with approval
  - Communication, schedule, and reports tabs

- Bug fixes: i18n breadcrumb keys for new views
- Styling: Gradient headers, animations, hover effects across all new views
- 200+ i18n keys added, 15+ API routes, 3+ Prisma models
This commit is contained in:
Mustafa Cetin committed 2026-07-29 23:42:26 +00:00
1 parent 490bd0650f
commit 6e5af13778
28 files changed
+10252 -494

No files matched your search

@@ -0,0 +1,59 @@
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { getSession } from '@/lib/auth';
// ── DELETE: Cancel a reservation ──────────────────────────────────
export async function DELETE(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getSession();
if (!session) {
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
}
const { id } = await params;
const reservation = await db.bookReservation.findUnique({
where: { id },
});
if (!reservation) {
return NextResponse.json({ error: 'Reservation not found' }, { status: 404 });
}
// Only the user who made the reservation, or admin, can cancel
const userRole = session.user?.role;
const isOwner = reservation.userId === session.userId;
const isAdmin = userRole === 'SCHOOL_ADMIN' || userRole === 'VICE_PRINCIPAL' || userRole === 'SUPER_ADMIN';
if (!isOwner && !isAdmin) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
await db.bookReservation.update({
where: { id },
data: { status: 'cancelled' },
});
// Re-calculate queue positions for remaining reservations
const remaining = await db.bookReservation.findMany({
where: { bookId: reservation.bookId, status: 'waiting' },
orderBy: { createdAt: 'asc' },
});
await Promise.all(
remaining.map((r, index) =>
db.bookReservation.update({
where: { id: r.id },
data: { queuePosition: index + 1 },
})
)
);
return NextResponse.json({ success: true });
} catch (error) {
console.error('BookReservation DELETE error:', error);
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
}
}