- Exam Calendar View: Teacher exam planning with calendar integration - Teachers create/modify exams, admin full CRUD, students read-only - CalendarEvent integration, 2-week warning, AI test availability badge - Monthly/weekly calendar view, list view, statistics - Tablet Grading View: Stylus annotation and AI grading audit - HTML5 Canvas with PointerEvent pressure sensitivity - Tool palette: pen, highlighter, eraser, text, stamp - Quick grade buttons (German 1-6 scale) - AI grading audit with detailed feedback and suggestions - Student Achievements View: Badges, XP, leaderboard - Badge collection grid with category filters - Level/XP system with progress bars - Leaderboard with opt-in - Daily/weekly challenges - Student Study Planner View: Pomodoro timer, weekly schedule - Pomodoro timer with focus score - Weekly study schedule with drag-and-drop - AI study recommendations - Session history and statistics - Notification Center View: Categories, filters, bulk actions - 6 categories, 4 priority levels - Real-time polling (10 seconds) - Preferences with quiet hours - Statistics charts - School Announcements View: CRUD, read receipts - 6 announcement types with gradient headers - Pin-to-top, read receipts, target audience - Admin/teacher create, students/parents view - Bug fixes: AnnouncementRead model user relation, User API schoolId field - i18n: 200+ new keys, breadcrumb navigation fixes - Styling: Gradient headers, animations, hover effects across all new views
70 lines
1.7 KiB
TypeScript
70 lines
1.7 KiB
TypeScript
// CompetenceTrack — Announcement Read Receipt API
|
|
import { NextResponse } from 'next/server';
|
|
import { db } from '@/lib/db';
|
|
import { getSession } from '@/lib/auth';
|
|
|
|
export async function POST(
|
|
_request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
const session = await getSession();
|
|
if (!session?.user) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const { id } = await params;
|
|
|
|
// Verify announcement exists
|
|
const announcement = await db.announcement.findUnique({
|
|
where: { id, deletedAt: null },
|
|
});
|
|
|
|
if (!announcement) {
|
|
return NextResponse.json({ error: 'Announcement not found' }, { status: 404 });
|
|
}
|
|
|
|
// Upsert read receipt
|
|
const readReceipt = await db.announcementRead.upsert({
|
|
where: {
|
|
announcementId_userId: {
|
|
announcementId: id,
|
|
userId: session.user.id,
|
|
},
|
|
},
|
|
update: {
|
|
readAt: new Date(),
|
|
},
|
|
create: {
|
|
announcementId: id,
|
|
userId: session.user.id,
|
|
},
|
|
});
|
|
|
|
return NextResponse.json({ readReceipt });
|
|
}
|
|
|
|
export async function GET(
|
|
_request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
const session = await getSession();
|
|
if (!session?.user) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const { id } = await params;
|
|
|
|
// Get all read receipts for this announcement
|
|
const reads = await db.announcementRead.findMany({
|
|
where: { announcementId: id },
|
|
include: {
|
|
user: {
|
|
select: { id: true, firstName: true, lastName: true },
|
|
},
|
|
},
|
|
orderBy: { readAt: 'desc' },
|
|
});
|
|
|
|
return NextResponse.json({ reads, totalReads: reads.length });
|
|
}
|