feat: Round 21 - Major feature additions
- Schlaukopf.de clone: Subject Content Management System (7 categories, 416 topics) - AI Integration: Pollination AI, OpenAI, Anthropic (chat, image/video generation) - Virtual Character System: 5 animated characters (Owl, Dragon, Robot, Cat, Wizard) - Illness Reporting with Parent Approval: privacy-first, student reports need parent approval - Student-Teacher Communication Rooms: real-time chat, voice, file sharing - Counseling/Guidance System: 4 types, calendar integration - Disciplinary Committee System: case management, severity levels - AI Test Generation: auto-generate practice tests before exams - Teacher Grading Review: AI-powered grading audit - VICE_PRINCIPAL role with same privileges as SCHOOL_ADMIN - Class responsible teacher (Klassenlehrer) field - 15 new Prisma models (80+ total) - 30+ new API route files - 8 new frontend components - 400+ i18n keys - Professional styling with gradient headers, animated counters, skeleton loading
This commit is contained in:
1 parent
04ae7aa80c
commit
f1776932d7
48 files changed
+13559
-4
No files matched your search
@@ -0,0 +1,77 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { db } from '@/lib/db';
|
||||
import { getSession } from '@/lib/auth';
|
||||
import { withRateLimit } from '@/lib/rate-limit';
|
||||
|
||||
// ── PUT: Approve or reject a change request ──
|
||||
const updateChangeRequestSchema = z.object({
|
||||
status: z.enum(['approved', 'rejected']),
|
||||
reviewNote: z.string().max(2000).optional().nullable(),
|
||||
});
|
||||
|
||||
async function updateChangeRequest(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
|
||||
}
|
||||
|
||||
if (
|
||||
session.user.role !== 'SCHOOL_ADMIN' &&
|
||||
session.user.role !== 'VICE_PRINCIPAL' &&
|
||||
session.user.role !== 'SUPER_ADMIN'
|
||||
) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
const body = await request.json();
|
||||
const parsed = updateChangeRequestSchema.safeParse(body);
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Validation failed', details: parsed.error.issues },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const changeRequest = await db.contentChangeRequest.update({
|
||||
where: { id },
|
||||
data: {
|
||||
status: parsed.data.status,
|
||||
reviewedBy: session.userId,
|
||||
reviewNote: parsed.data.reviewNote,
|
||||
},
|
||||
include: {
|
||||
content: { select: { id: true, title: true } },
|
||||
requester: { select: { id: true, firstName: true, lastName: true } },
|
||||
reviewer: { select: { id: true, firstName: true, lastName: true } },
|
||||
},
|
||||
});
|
||||
|
||||
// If approved and it's an edit request, apply the proposed changes to the content
|
||||
if (parsed.data.status === 'approved' && changeRequest.proposedChanges) {
|
||||
try {
|
||||
const proposed = JSON.parse(changeRequest.proposedChanges);
|
||||
if (changeRequest.content?.id && proposed) {
|
||||
await db.subjectContent.update({
|
||||
where: { id: changeRequest.contentId },
|
||||
data: proposed,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// If proposed changes are not valid JSON, skip auto-apply
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(changeRequest);
|
||||
} catch (error) {
|
||||
console.error('ContentChangeRequest PUT [id] error:', error);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export const PUT = withRateLimit(updateChangeRequest, 'dataWrite');
|
||||
Reference in new issue
Block a user