Round 19b: 10 Runtime Bug Fixes, Student Notebook Access, German Curriculum Types
Bug Fixes:
- Fixed duplicate i18n keys (notebooks.type_lined etc.)
- Fixed settings-view.tsx: studentsList undefined → students
- Fixed student-detail-view.tsx: pe.competencyId → pe.competency.id
- Fixed student-detail-view.tsx: locale variable not in scope
- Fixed student-detail-view.tsx: studentId/setStudent undefined
- Fixed notebooks-view.tsx: onClick={onAddPage} passing MouseEvent
- Fixed curriculum-coverage-view.tsx: Map import shadowing built-in
- Fixed api.ts: avatarUrl missing from AttendanceRecord/Report types
- Fixed badge-check.ts: invalid Prisma queries with wrong field names
Student Notebook Access:
- Students can create/view/edit their own notebooks
- Students see shared notebooks from teachers in enrolled classes
- Fixed handleCreateFromTemplate to use correct ownerType for students
German Curriculum Notebook Types (9 types):
- Deutschheft (lined, red), Matheheft (grid, blue), Sachbuch (grid, teal)
- Musikheft (music staff, emerald), Kunstheft (blank, purple)
- Englischheft (lined, amber), Geschichtsheft (lined, brown)
- Religionsheft (lined, orange), Sachkundeheft (lined+drawing, teal)
Environment Features:
- Papier gespart counter with tree calculation
- Eco badge on every notebook card
- Lehrplan-Heft curriculum badge on German types
60+ new i18n keys (DE/EN)
This commit is contained in:
1 parent
9b2168d775
commit
58cb7afd81
14 files changed
+593
-64
No files matched your search
@@ -121,7 +121,7 @@ export async function GET(request: Request) {
|
||||
teacher: { select: { id: true, firstName: true, lastName: true } },
|
||||
records: {
|
||||
include: {
|
||||
student: { select: { id: true, firstName: true, lastName: true } },
|
||||
student: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
|
||||
},
|
||||
orderBy: { student: { lastName: 'asc' } },
|
||||
},
|
||||
@@ -237,7 +237,7 @@ export async function POST(request: Request) {
|
||||
teacher: { select: { id: true, firstName: true, lastName: true } },
|
||||
records: {
|
||||
include: {
|
||||
student: { select: { id: true, firstName: true, lastName: true } },
|
||||
student: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
|
||||
},
|
||||
orderBy: { student: { lastName: 'asc' } },
|
||||
},
|
||||
@@ -341,7 +341,7 @@ export async function PUT(request: Request) {
|
||||
teacher: { select: { id: true, firstName: true, lastName: true } },
|
||||
records: {
|
||||
include: {
|
||||
student: { select: { id: true, firstName: true, lastName: true } },
|
||||
student: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
|
||||
},
|
||||
orderBy: { student: { lastName: 'asc' } },
|
||||
},
|
||||
|
||||
@@ -58,7 +58,13 @@ const updateNotebookSchema = z.object({
|
||||
title: z.string().min(1).max(200).optional(),
|
||||
description: z.string().max(1000).optional().nullable(),
|
||||
notebookType: z
|
||||
.enum(['lined', 'grid', 'blank', 'dotted', 'music', 'calligraphy'])
|
||||
.enum([
|
||||
'lined', 'grid', 'blank', 'dotted', 'music', 'calligraphy',
|
||||
// German curriculum types
|
||||
'deutschheft', 'matheheft', 'sachbuch', 'musikheft',
|
||||
'kunstheft', 'englischheft', 'geschichtsheft', 'religionsheft',
|
||||
'sachkundeheft',
|
||||
])
|
||||
.optional(),
|
||||
color: z.string().optional(),
|
||||
icon: z.string().optional().nullable(),
|
||||
|
||||
@@ -150,7 +150,13 @@ const createNotebookSchema = z.object({
|
||||
title: z.string().min(1).max(200),
|
||||
description: z.string().max(1000).optional().nullable(),
|
||||
notebookType: z
|
||||
.enum(['lined', 'grid', 'blank', 'dotted', 'music', 'calligraphy'])
|
||||
.enum([
|
||||
'lined', 'grid', 'blank', 'dotted', 'music', 'calligraphy',
|
||||
// German curriculum types
|
||||
'deutschheft', 'matheheft', 'sachbuch', 'musikheft',
|
||||
'kunstheft', 'englischheft', 'geschichtsheft', 'religionsheft',
|
||||
'sachkundeheft',
|
||||
])
|
||||
.default('lined'),
|
||||
color: z.string().default('#10b981'),
|
||||
icon: z.string().optional().nullable(),
|
||||
|
||||
@@ -2,7 +2,8 @@ import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { getSession } from '@/lib/auth';
|
||||
|
||||
// ── GET: Get public notebooks shared by other teachers in the same school ──
|
||||
// ── GET: Get public notebooks shared by others in the same school ──
|
||||
// Teachers see other teachers' notebooks; students see teacher notebooks shared with their classes
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const session = await getSession();
|
||||
@@ -20,19 +21,69 @@ export async function GET(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
// Only show public notebooks from other teachers in the same school
|
||||
const userRole = session.user.role;
|
||||
|
||||
if (userRole === 'STUDENT') {
|
||||
// Students see public notebooks from teachers in their enrolled classes
|
||||
const matchingStudents = await db.student.findMany({
|
||||
where: {
|
||||
schoolId,
|
||||
firstName: session.user.firstName,
|
||||
lastName: session.user.lastName,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
const studentIds = matchingStudents.map(s => s.id);
|
||||
|
||||
const studentEnrollments = studentIds.length > 0
|
||||
? await db.enrollment.findMany({
|
||||
where: { studentId: { in: studentIds } },
|
||||
select: { classGroupId: true },
|
||||
})
|
||||
: [];
|
||||
|
||||
const enrolledClassGroupIds = studentEnrollments.map(e => e.classGroupId);
|
||||
|
||||
const sharedNotebooks = await db.notebook.findMany({
|
||||
where: {
|
||||
schoolId,
|
||||
isPublic: true,
|
||||
isArchived: false,
|
||||
deletedAt: null,
|
||||
ownerId: { not: session.user.id },
|
||||
ownerType: 'TEACHER',
|
||||
...(enrolledClassGroupIds.length > 0
|
||||
? { classGroupId: { in: enrolledClassGroupIds } }
|
||||
: {}),
|
||||
},
|
||||
include: {
|
||||
subject: { select: { id: true, name: true } },
|
||||
classGroup: { select: { id: true, name: true } },
|
||||
owner: { select: { id: true, firstName: true, lastName: true } },
|
||||
_count: { select: { pages: true } },
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
});
|
||||
|
||||
return NextResponse.json(sharedNotebooks);
|
||||
}
|
||||
|
||||
// Teachers / Admins: see public notebooks from other teachers in the same school
|
||||
// Also include student notebooks shared publicly
|
||||
const sharedNotebooks = await db.notebook.findMany({
|
||||
where: {
|
||||
schoolId,
|
||||
isPublic: true,
|
||||
isArchived: false,
|
||||
deletedAt: null,
|
||||
ownerId: { not: session.user.id }, // Exclude own notebooks
|
||||
ownerId: { not: session.user.id },
|
||||
},
|
||||
include: {
|
||||
subject: { select: { id: true, name: true } },
|
||||
classGroup: { select: { id: true, name: true } },
|
||||
owner: { select: { id: true, firstName: true, lastName: true } },
|
||||
owner: { select: { id: true, firstName: true, lastName: true, role: true } },
|
||||
_count: { select: { pages: true } },
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function GET(request: Request) {
|
||||
where,
|
||||
orderBy: { generatedAt: 'desc' },
|
||||
include: {
|
||||
student: { select: { id: true, firstName: true, lastName: true } },
|
||||
student: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
|
||||
classGroup: { select: { id: true, name: true, gradeLevel: true } },
|
||||
schoolYear: { select: { id: true, label: true } },
|
||||
generatedByUser: {
|
||||
@@ -116,7 +116,7 @@ export async function POST(request: Request) {
|
||||
: undefined,
|
||||
},
|
||||
include: {
|
||||
student: { select: { id: true, firstName: true, lastName: true } },
|
||||
student: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
|
||||
classGroup: { select: { id: true, name: true } },
|
||||
schoolYear: { select: { id: true, label: true } },
|
||||
generatedByUser: {
|
||||
@@ -186,7 +186,7 @@ export async function PUT(request: Request) {
|
||||
where: { id },
|
||||
data: updateData,
|
||||
include: {
|
||||
student: { select: { id: true, firstName: true, lastName: true } },
|
||||
student: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
|
||||
classGroup: { select: { id: true, name: true, gradeLevel: true } },
|
||||
schoolYear: { select: { id: true, label: true } },
|
||||
generatedByUser: {
|
||||
|
||||
Reference in new issue
Block a user