Round 20: Student Portal, District Management, userId field, styling enhancements, bug fixes

- Added userId field to Student model with User relation
- Created Student Portal view with 6 tabs (competencies, goals, achievements, schedule, homework, feedback)
- Created StudentGoal model + CRUD API with role-based access
- Created District Management view with CRUD, analytics, school assignment
- Enhanced auth page: animated gradient, floating particles, glassmorphism, staggered entrance
- Enhanced dashboard: time-based greeting, animated stat cards, class overview, school branding
- Enhanced student portal: hero section, circular progress, streak counter, celebration particles
- Fixed student-goals API 403 for STUDENT role (name-matching fallback for legacy data)
- Added userId filter to students API with OR fallback
- Added nav.student-portal i18n key
- 100+ new i18n keys in DE and EN
- All lint checks pass
This commit is contained in:
Mustafa Cetin committed 2026-07-29 18:45:41 +00:00
1 parent 9680cc9244
commit 04ae7aa80c
23 files changed
+4624 -2868

No files matched your search

+32 -1
View File
@@ -25,11 +25,31 @@ export async function GET(request: Request) {
const schoolId = searchParams.get('schoolId');
const classGroupId = searchParams.get('classGroupId');
const search = searchParams.get('search');
const userId = searchParams.get('userId');
const where: Record<string, unknown> = { deletedAt: null };
if (schoolId) where.schoolId = schoolId;
// Filter by userId (direct link to User account)
let userIdFilter: Record<string, unknown>[] | null = null;
if (userId) {
// For STUDENT role, also match by name + school as fallback for legacy data
if (session.user?.role === 'STUDENT' && session.user.id === userId) {
userIdFilter = [
{ userId },
{
userId: null,
firstName: session.user.firstName,
lastName: session.user.lastName,
schoolId: session.user.schoolId,
},
];
} else {
where.userId = userId;
}
}
if (classGroupId) {
where.enrollments = {
some: { classGroupId, endDate: null },
@@ -37,11 +57,22 @@ export async function GET(request: Request) {
}
if (search) {
where.OR = [
const searchOr = [
{ firstName: { contains: search } },
{ lastName: { contains: search } },
{ externalId: { contains: search } },
];
// If userId filter also uses OR, combine with AND
if (userIdFilter) {
where.AND = [
{ OR: userIdFilter },
{ OR: searchOr },
];
} else {
where.OR = searchOr;
}
} else if (userIdFilter) {
where.OR = userIdFilter;
}
const students = await db.student.findMany({