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

+7
View File
@@ -59,6 +59,7 @@ import {
Library as LibraryIcon,
Trophy,
FolderOpen,
Building2,
} from 'lucide-react';
import {
Sidebar,
@@ -131,6 +132,7 @@ import { Skeleton } from '@/components/ui/skeleton';
// Static imports for lightweight views
import DashboardView from './dashboard-view';
import StudentPortalView from './student-portal-view';
import ClassesView from './classes-view';
import CompetencyGridView from './competency-grid-view';
import ProgressEntriesView from './progress-entries-view';
@@ -153,6 +155,7 @@ import PortfolioView from './portfolio-view';
import TimetableView from './timetable-view';
import ResourceLibraryView from './resource-library-view';
import CompetitionsView from './competitions-view';
import DistrictManagementView from './district-management-view';
// Dynamic imports for heavy components with loading skeletons
const AnalyticsView = dynamic(() => import('./analytics-view'), {
@@ -218,6 +221,7 @@ const navSections: NavSection[] = [
labelKey: 'polish.nav_setup',
items: [
{ key: 'competencies', icon: BookOpen, labelKey: 'nav.competencies' },
{ key: 'districts', icon: Building2, labelKey: 'nav.districts' },
{ key: 'settings', icon: Settings, labelKey: 'nav.settings' },
],
},
@@ -230,6 +234,7 @@ const studentNavSections: NavSection[] = [
labelKey: 'student.dashboard_title',
items: [
{ key: 'dashboard', icon: LayoutDashboard, labelKey: 'nav.dashboard' },
{ key: 'student-portal', icon: GraduationCap, labelKey: 'student_portal.title' },
{ key: 'notebooks', icon: BookOpen, labelKey: 'nav.student_notebooks' },
{ key: 'flower', icon: Flower2, labelKey: 'nav.student_competencies' },
{ key: 'grading', icon: Calculator, labelKey: 'nav.student_grades' },
@@ -361,6 +366,7 @@ function AnnouncementBanner({ schoolId }: { schoolId: string | null }) {
function renderView(view: ViewName) {
switch (view) {
case 'dashboard': return <DashboardView />;
case 'student-portal': return <StudentPortalView />;
case 'classes': return <ClassesView />;
case 'competencies': return <CompetencyGridView />;
case 'progress': return <ProgressEntriesView />;
@@ -387,6 +393,7 @@ function renderView(view: ViewName) {
case 'timetable': return <TimetableView />;
case 'resources': return <ResourceLibraryView />;
case 'competitions': return <CompetitionsView />;
case 'districts': return <DistrictManagementView />;
default: return <DashboardView />;
}
}