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

+230
View File
@@ -493,6 +493,22 @@ const de: TranslationDict = {
// New styling keys
'dashboard.start_here': 'Jetzt beginnen!',
'dashboard.greeting_morning': 'Guten Morgen',
'dashboard.greeting_afternoon': 'Guten Tag',
'dashboard.greeting_evening': 'Guten Abend',
'dashboard.school_branding': 'Schulprofil',
'dashboard.school_branding_desc': 'Ihre Schule im Überblick',
'dashboard.competency_progress': 'Kompetenzfortschritt',
'dashboard.competency_progress_desc': 'Aktuelle Entwicklung der Kompetenzniveaus',
'dashboard.paper_saved_pages': 'Seiten',
'dashboard.paper_saved_trees': 'Baeume gerettet',
'dashboard.paper_saved_co2': 'kg CO2 eingespart',
'dashboard.daily_tip': 'Tipp des Tages',
'dashboard.class_overview_grid': 'Klassenuebersicht',
'dashboard.class_overview_grid_desc': 'Schnellzugriff auf alle Klassen',
'dashboard.activity_timeline': 'Aktivitaetenverlauf',
'dashboard.activity_timeline_desc': 'Neueste Eintraege im Zeitverlauf',
'dashboard.updated_at': 'Aktualisiert',
'date.today': 'Heute',
'date.yesterday': 'Gestern',
'date.days_ago': '{count} Tage zuvor',
@@ -511,6 +527,7 @@ const de: TranslationDict = {
'header.notifications': 'Aktivitäten',
'auth.feature_reports': 'Berichte & Zeugnisse',
'auth.feature_flower': 'Kompetenzblume',
'auth.feature_data_security': 'Datenschutz & Sicherheit',
'flower.hide_average': 'Vergleich aus',
'flower.show_average': 'Vergleich an',
'flower.download_png': 'Als PNG',
@@ -2665,6 +2682,41 @@ const de: TranslationDict = {
'district.total_students': 'Gesamtschüler',
'district.total_classes': 'Gesamtklassen',
'district.total_teachers': 'Gesamtlehrer',
'district.management': 'Bezirksverwaltung',
'district.overview': 'Übersicht',
'district.teacher_count': 'Anzahl Lehrer',
'district.add_district': 'Bezirk hinzufügen',
'district.edit_district': 'Bezirk bearbeiten',
'district.delete_district': 'Bezirk löschen',
'district.no_schools': 'Keine Schulen vorhanden',
'district.remove_school': 'Schule entfernen',
'district.performance_comparison': 'Leistungsvergleich',
'district.recent_activity': 'Letzte Aktivität',
'district.active': 'Aktiv',
'district.inactive': 'Inaktiv',
'district.details': 'Details',
'district.save_success': 'Bezirk erfolgreich gespeichert',
'district.delete_success': 'Bezirk erfolgreich gelöscht',
'district.school_assigned': 'Schule erfolgreich zugeordnet',
'district.school_removed': 'Schule erfolgreich entfernt',
'district.unassigned_schools': 'Nicht zugeordnete Schulen',
'district.no_unassigned_schools': 'Alle Schulen sind zugeordnet',
'district.all_schools': 'Alle Schulen',
'district.search_districts': 'Bezirke durchsuchen',
'district.overview_stats': 'Bezirksstatistik',
'district.avg_performance': 'Durchschnittliche Leistung',
'district.school_type': 'Schultyp',
'district.select_district': 'Bezirk auswählen',
'district.district_info': 'Bezirksinformationen',
'district.activity_created': 'Bezirk erstellt',
'district.activity_updated': 'Bezirk aktualisiert',
'district.activity_school_added': 'Schule hinzugefügt',
'district.activity_school_removed': 'Schule entfernt',
'district.performance_score': 'Leistungswert',
'district.attendance_rate': 'Anwesenheitsrate',
'district.student_teacher_ratio': 'Schüler-Lehrer-Verhältnis',
'nav.districts': 'Bezirksverwaltung',
'nav.student-portal': 'Mein Portal',
// PWA / Offline
'pwa.offline': 'Sie sind offline',
@@ -3831,6 +3883,69 @@ const de: TranslationDict = {
'admin.health_good': 'Gut',
'admin.health_warning': 'Warnung',
'admin.health_critical': 'Kritisch',
// Student Portal
'student_portal.title': 'Mein Portal',
'student_portal.my_competencies': 'Meine Kompetenzen',
'student_portal.my_goals': 'Meine Ziele',
'student_portal.my_achievements': 'Meine Erfolge',
'student_portal.my_schedule': 'Mein Stundenplan',
'student_portal.my_homework': 'Meine Hausaufgaben',
'student_portal.my_feedback': 'Mein Feedback',
'student_portal.goal_set': 'Ziel setzen',
'student_portal.goal_progress': 'Zielfortschritt',
'student_portal.goal_complete': 'Ziel abschliessen',
'student_portal.no_competencies': 'Noch keine Kompetenzdaten vorhanden',
'student_portal.no_goals': 'Noch keine Ziele gesetzt',
'student_portal.no_achievements': 'Noch keine Erfolge freigeschaltet',
'student_portal.points_earned': 'Punkte gesammelt',
'student_portal.badges_earned': 'Abzeichen verdient',
'student_portal.rewards_available': 'Verfuegbare Belohnungen',
'student_portal.today_schedule': 'Heutiger Stundenplan',
'student_portal.next_period': 'Naechste Stunde',
'student_portal.current_period': 'Aktuelle Stunde',
'student_portal.overdue': 'Ueberfaellig',
'student_portal.due_today': 'Heute faellig',
'student_portal.upcoming': 'Anstehend',
'student_portal.mastery_level': 'Kompetenzniveau',
'student_portal.level_1': 'Anfaenger',
'student_portal.level_2': 'Fortgeschritten',
'student_portal.level_3': 'Kompetent',
'student_portal.level_4': 'Experte',
'student_portal.add_goal': 'Neues Ziel',
'student_portal.goal_title': 'Zieltitel',
'student_portal.goal_description': 'Zielbeschreibung',
'student_portal.target_date': 'Zieldatum',
'student_portal.active_goals': 'Aktive Ziele',
'student_portal.completed_goals': 'Abgeschlossene Ziele',
'student_portal.abandon_goal': 'Ziel aufgeben',
'student_portal.update_progress': 'Fortschritt aktualisieren',
'student_portal.badge_name': 'Abzeichen',
'student_portal.earned_on': 'Verdient am',
'student_portal.no_schedule': 'Kein Stundenplan fuer heute',
'student_portal.period': 'Stunde',
'student_portal.break': 'Pause',
'student_portal.no_homework_due': 'Keine Hausaufgaben faellig',
'student_portal.no_feedback': 'Noch kein Feedback vorhanden',
'student_portal.recent_feedback': 'Letztes Feedback',
'student_portal.from_teacher': 'Von',
'student_portal.subject': 'Fach',
'student_portal.competency_card': 'Kompetenzkarte',
'student_portal.progress_bar': 'Fortschrittsbalken',
'student_portal.total_points': 'Gesamtpunkte',
'student_portal.level': 'Stufe',
'student_portal.no_class': 'Keine Klasse',
'student_portal.schedule_for': 'Stundenplan fuer',
'student_portal.good_morning': 'Guten Morgen',
'student_portal.good_afternoon': 'Guten Tag',
'student_portal.good_evening': 'Guten Abend',
'student_portal.hero_message': 'Weiter so! Jeder Schritt bringt dich naeher an dein Ziel.',
'student_portal.streak_days': 'Tage Strich',
'student_portal.days': 'Tage',
'student_portal.learning_streak': 'Lernstrich',
'student_portal.streak_message': 'Tolle Serie! Halte durch, um deine Tagesreihe zu verlaengern.',
'student_portal.level_up_progress': 'Aufstiegsfortschritt',
'student_portal.level_up_detail': 'Aktuelle Stufe {level} von {max}',
};
const en: TranslationDict = {
@@ -4323,6 +4438,22 @@ const en: TranslationDict = {
// New styling keys
'dashboard.start_here': 'Start here!',
'dashboard.greeting_morning': 'Good morning',
'dashboard.greeting_afternoon': 'Good afternoon',
'dashboard.greeting_evening': 'Good evening',
'dashboard.school_branding': 'School Profile',
'dashboard.school_branding_desc': 'Your school at a glance',
'dashboard.competency_progress': 'Competency Progress',
'dashboard.competency_progress_desc': 'Current development of competency levels',
'dashboard.paper_saved_pages': 'pages',
'dashboard.paper_saved_trees': 'trees saved',
'dashboard.paper_saved_co2': 'kg CO2 saved',
'dashboard.daily_tip': 'Tip of the day',
'dashboard.class_overview_grid': 'Class Overview',
'dashboard.class_overview_grid_desc': 'Quick access to all classes',
'dashboard.activity_timeline': 'Activity Timeline',
'dashboard.activity_timeline_desc': 'Latest entries over time',
'dashboard.updated_at': 'Updated',
'date.today': 'Today',
'date.yesterday': 'Yesterday',
'date.days_ago': '{count} days ago',
@@ -4341,6 +4472,7 @@ const en: TranslationDict = {
'header.notifications': 'Activity',
'auth.feature_reports': 'Reports & Transcripts',
'auth.feature_flower': 'Competence Flower',
'auth.feature_data_security': 'Data Security & Privacy',
'flower.hide_average': 'Hide Average',
'flower.show_average': 'Show Average',
'flower.download_png': 'As PNG',
@@ -6494,6 +6626,41 @@ const en: TranslationDict = {
'district.total_students': 'Total Students',
'district.total_classes': 'Total Classes',
'district.total_teachers': 'Total Teachers',
'district.management': 'District Management',
'district.overview': 'Overview',
'district.teacher_count': 'Teacher Count',
'district.add_district': 'Add District',
'district.edit_district': 'Edit District',
'district.delete_district': 'Delete District',
'district.no_schools': 'No schools found',
'district.remove_school': 'Remove School',
'district.performance_comparison': 'Performance Comparison',
'district.recent_activity': 'Recent Activity',
'district.active': 'Active',
'district.inactive': 'Inactive',
'district.details': 'Details',
'district.save_success': 'District saved successfully',
'district.delete_success': 'District deleted successfully',
'district.school_assigned': 'School assigned successfully',
'district.school_removed': 'School removed successfully',
'district.unassigned_schools': 'Unassigned Schools',
'district.no_unassigned_schools': 'All schools are assigned',
'district.all_schools': 'All Schools',
'district.search_districts': 'Search districts',
'district.overview_stats': 'District Statistics',
'district.avg_performance': 'Average Performance',
'district.school_type': 'School Type',
'district.select_district': 'Select District',
'district.district_info': 'District Information',
'district.activity_created': 'District created',
'district.activity_updated': 'District updated',
'district.activity_school_added': 'School added',
'district.activity_school_removed': 'School removed',
'district.performance_score': 'Performance Score',
'district.attendance_rate': 'Attendance Rate',
'district.student_teacher_ratio': 'Student-Teacher Ratio',
'nav.districts': 'District Management',
'nav.student-portal': 'My Portal',
// PWA / Offline
'pwa.offline': 'You are offline',
@@ -7660,6 +7827,69 @@ const en: TranslationDict = {
'admin.health_good': 'Good',
'admin.health_warning': 'Warning',
'admin.health_critical': 'Critical',
// Student Portal
'student_portal.title': 'My Portal',
'student_portal.my_competencies': 'My Competencies',
'student_portal.my_goals': 'My Goals',
'student_portal.my_achievements': 'My Achievements',
'student_portal.my_schedule': 'My Schedule',
'student_portal.my_homework': 'My Homework',
'student_portal.my_feedback': 'My Feedback',
'student_portal.goal_set': 'Set Goal',
'student_portal.goal_progress': 'Goal Progress',
'student_portal.goal_complete': 'Complete Goal',
'student_portal.no_competencies': 'No competency data yet',
'student_portal.no_goals': 'No goals set yet',
'student_portal.no_achievements': 'No achievements unlocked yet',
'student_portal.points_earned': 'Points Earned',
'student_portal.badges_earned': 'Badges Earned',
'student_portal.rewards_available': 'Rewards Available',
'student_portal.today_schedule': 'Today\'s Schedule',
'student_portal.next_period': 'Next Period',
'student_portal.current_period': 'Current Period',
'student_portal.overdue': 'Overdue',
'student_portal.due_today': 'Due Today',
'student_portal.upcoming': 'Upcoming',
'student_portal.mastery_level': 'Mastery Level',
'student_portal.level_1': 'Beginner',
'student_portal.level_2': 'Developing',
'student_portal.level_3': 'Competent',
'student_portal.level_4': 'Expert',
'student_portal.add_goal': 'New Goal',
'student_portal.goal_title': 'Goal Title',
'student_portal.goal_description': 'Goal Description',
'student_portal.target_date': 'Target Date',
'student_portal.active_goals': 'Active Goals',
'student_portal.completed_goals': 'Completed Goals',
'student_portal.abandon_goal': 'Abandon Goal',
'student_portal.update_progress': 'Update Progress',
'student_portal.badge_name': 'Badge',
'student_portal.earned_on': 'Earned on',
'student_portal.no_schedule': 'No schedule for today',
'student_portal.period': 'Period',
'student_portal.break': 'Break',
'student_portal.no_homework_due': 'No homework due',
'student_portal.no_feedback': 'No feedback yet',
'student_portal.recent_feedback': 'Recent Feedback',
'student_portal.from_teacher': 'From',
'student_portal.subject': 'Subject',
'student_portal.competency_card': 'Competency Card',
'student_portal.progress_bar': 'Progress Bar',
'student_portal.total_points': 'Total Points',
'student_portal.level': 'Level',
'student_portal.no_class': 'No Class',
'student_portal.schedule_for': 'Schedule for',
'student_portal.good_morning': 'Good Morning',
'student_portal.good_afternoon': 'Good Afternoon',
'student_portal.good_evening': 'Good Evening',
'student_portal.hero_message': 'Keep going! Every step brings you closer to your goal.',
'student_portal.streak_days': 'Day Streak',
'student_portal.days': 'days',
'student_portal.learning_streak': 'Learning Streak',
'student_portal.streak_message': 'Great streak! Keep going to extend your daily streak.',
'student_portal.level_up_progress': 'Level Up Progress',
'student_portal.level_up_detail': 'Current level {level} of {max}',
};
const translations: Record<string, TranslationDict> = { de, en };