Round 19: Competition system, German curriculum learning, enhanced notebooks, student dashboard

- Fix portfolio-view.tsx: replace entry.isPublic with form.isPublic (runtime error)
- Add Competition system: 5 Prisma models (Competition, CompetitionParticipant, CompetitionReward, CompetitionLeaderboard, RewardClaim)
- Add German curriculum learning: 4 Prisma models (SubjectTopic, SubjectLesson, LessonQuestion, StudentAnswer)
- Add 12 API routes for competitions and rewards
- Add 7 API routes for subject topics, lessons, questions, and student answers
- Create competitions-view.tsx with full CRUD, leaderboard, rewards, registration
- Enhance notebooks-view.tsx with 'Lernen' tab for German curriculum subjects
- Add student-specific dashboard with Papier sparen banner, notebook quick access, learning progress
- Add competitions to teacher and student navigation
- Add 130+ i18n keys for DE and EN (competitions, rewards, learning, environment)
- Enhanced student nav with competitions and notebooks
- DSGVO-compliant reward claims with status lifecycle
This commit is contained in:
Mustafa Cetin committed 2026-07-29 14:18:18 +00:00
1 parent 74404708e5
commit 849b48a5a4
22 files changed
+6543 -5

No files matched your search

+286
View File
@@ -2080,6 +2080,51 @@ const de: TranslationDict = {
'notebooks.tab_all': 'Meine Hefte',
'notebooks.owner': 'Von',
// ─── Learning Content (German Curriculum) ──────────────────────────
'notebooks.learn': 'Lernen',
'notebooks.topics': 'Themen',
'notebooks.lessons': 'Lektionen',
'notebooks.exercises': 'Uebungen',
'notebooks.quiz': 'Quiz',
'notebooks.flashcards': 'Karteikarten',
'notebooks.progress': 'Fortschritt',
'notebooks.grade_level': 'Klassenstufe',
'notebooks.curriculum': 'Lehrplan',
'notebooks.subject_topic': 'Thema',
'notebooks.difficulty': 'Schwierigkeitsgrad',
'notebooks.easy': 'Leicht',
'notebooks.medium': 'Mittel',
'notebooks.hard': 'Schwer',
'notebooks.question': 'Frage',
'notebooks.answer': 'Antwort',
'notebooks.correct': 'Richtig',
'notebooks.incorrect': 'Falsch',
'notebooks.explanation': 'Erklaerung',
'notebooks.try_again': 'Nochmal versuchen',
'notebooks.next_question': 'Naechste Frage',
'notebooks.score': 'Punktzahl',
'notebooks.time_taken': 'Benoetigte Zeit',
'notebooks.mastery': 'Beherrschung',
'notebooks.practice_more': 'Mehr ueben',
'notebooks.well_done': 'Gut gemacht!',
'notebooks.keep_practicing': 'Weiter ueben!',
'notebooks.subject_math': 'Mathematik',
'notebooks.subject_german': 'Deutsch',
'notebooks.subject_science': 'Sachkunde',
'notebooks.subject_english': 'Englisch',
'notebooks.subject_music': 'Musik',
'notebooks.subject_art': 'Kunst',
'notebooks.subject_religion': 'Religion/Ethik',
'notebooks.subject_pe': 'Sport',
'notebooks.papier_sparen': 'Papier sparen, Umwelt schuetzen',
'notebooks.digital_instead_paper': 'Digitale Hefte statt Papierhefte',
'notebooks.environment_tip': 'Mit digitalen Heften schuetzt du die Umwelt — kein Papierverschwendung!',
'notebooks.type_lined': 'Liniert',
'notebooks.type_grid': 'Kariert',
'notebooks.type_blank': 'Unliniert',
'notebooks.type_dotted': 'Punktiert',
'notebooks.type_music': 'Notenlinien',
// ─── Enhanced Notifications ──────────────────────────────────────
'notifications.today': 'Heute',
'notifications.yesterday': 'Gestern',
@@ -2123,6 +2168,19 @@ const de: TranslationDict = {
'student.view_all': 'Alle anzeigen',
'student.welcome_back': 'Willkommen zurueck',
'student.progress_overview': 'Dein Lernfortschritt',
'student.papier_sparen_title': 'Papier sparen, Umwelt schuetzen',
'student.papier_sparen_subtitle': 'Digitale Hefte statt Papierhefte — Mit digitalen Heften schuetzt du die Umwelt!',
'student.go_to_notebooks': 'Zu meinen Heften',
'student.my_notebooks_quick': 'Meine Hefte',
'student.show_all_notebooks': 'Alle Hefte anzeigen',
'student.start_learning': 'Lernen starten',
'student.learning_progress': 'Lernfortschritt',
'student.continue_learning': 'Weiter lernen',
'student.subject_progress': 'Fachfortschritt',
'student.notebook_math': 'Mathematik',
'student.notebook_german': 'Deutsch',
'student.notebook_english': 'Englisch',
'student.notebook_science': 'Naturwissenschaften',
'nav.student_competencies': 'Meine Kompetenzen',
'nav.student_grades': 'Meine Noten',
'nav.student_notebooks': 'Meine Hefte',
@@ -2593,6 +2651,7 @@ const de: TranslationDict = {
'nav.portfolio': 'Portfolio',
'nav.timetable': 'Stundenplan',
'nav.resources': 'Mediathek',
'nav.competitions': 'Wettbewerbe',
// Navigation - Homework
'nav.homework': 'Hausaufgaben',
@@ -3266,6 +3325,90 @@ const de: TranslationDict = {
'avatar.change': 'Foto ändern',
'avatar.remove': 'Foto entfernen',
// Competition System
'competition.title': 'Wettbewerbe',
'competition.create': 'Wettbewerb erstellen',
'competition.edit': 'Wettbewerb bearbeiten',
'competition.delete': 'Wettbewerb löschen',
'competition.manage': 'Wettbewerbe verwalten',
'competition.details': 'Wettbewerbsdetails',
'competition.name': 'Wettbewerb',
'competition.description': 'Beschreibung',
'competition.type': 'Typ',
'competition.category': 'Kategorie',
'competition.status': 'Status',
'competition.start_date': 'Startdatum',
'competition.end_date': 'Enddatum',
'competition.registration_deadline': 'Anmeldungsfrist',
'competition.max_participants': 'Maximale Teilnehmer',
'competition.scoring_type': 'Wertung',
'competition.rules': 'Regeln',
'competition.is_public': 'Öffentlich sichtbar',
'competition.subject': 'Fach',
'competition.created_by': 'Erstellt von',
'competition.no_competitions': 'Keine Wettbewerbe vorhanden',
'competition.type.class': 'Klassenwettbewerb',
'competition.type.inter_class': 'Klassenübergreifend',
'competition.type.inter_school': 'Schulübergreifend',
'competition.category.academic': 'Akademisch',
'competition.category.sports': 'Sport',
'competition.category.creativity': 'Kreativität',
'competition.category.citizenship': 'Gemeinschaft',
'competition.category.digital': 'Digital',
'competition.category.reading': 'Lesen',
'competition.category.stem': 'MINT',
'competition.category.other': 'Sonstiges',
'competition.status.draft': 'Entwurf',
'competition.status.registration': 'Anmeldung',
'competition.status.active': 'Aktiv',
'competition.status.completed': 'Abgeschlossen',
'competition.status.cancelled': 'Abgesagt',
'competition.scoring.points': 'Punkte',
'competition.scoring.rank': 'Platzierung',
'competition.scoring.time': 'Zeit',
'competition.scoring.badge_count': 'Abzeichenanzahl',
'competition.register': 'Anmelden',
'competition.register_participant': 'Teilnehmer anmelden',
'competition.update_score': 'Punkte aktualisieren',
'competition.leaderboard': 'Bestenliste',
'competition.participants': 'Teilnehmer',
'competition.rewards': 'Preise',
'competition.reward.create': 'Preis hinzufügen',
'competition.reward.type': 'Preisart',
'competition.reward.type.digital_code': 'Digitaler Code',
'competition.reward.type.badge': 'Abzeichen',
'competition.reward.type.certificate': 'Zertifikat',
'competition.reward.type.experience': 'Erlebnis',
'competition.reward.type.physical': 'Physischer Preis',
'competition.reward.provider': 'Anbieter',
'competition.reward.provider.netflix': 'Netflix',
'competition.reward.provider.amazon': 'Amazon',
'competition.reward.provider.cinema': 'Kino',
'competition.reward.provider.theater': 'Theater',
'competition.reward.provider.concert': 'Konzert',
'competition.reward.provider.custom': 'Sonstige',
'competition.reward.quantity': 'Menge',
'competition.reward.claimed': 'Eingelöst',
'competition.reward.available': 'Verfügbar',
'competition.reward.rank_requirement': 'Mindestplatzierung',
'competition.reward.points_required': 'Mindestpunkte',
'competition.reward.image': 'Preisbild',
'competition.reward.code': 'Code',
'competition.reward.claim': 'Einlösen',
'competition.reward.claimed_at': 'Eingelöst am',
'competition.reward.expires': 'Läuft ab am',
'competition.reward.status': 'Status',
'competition.reward.status.pending': 'Ausstehend',
'competition.reward.status.claimed': 'Eingelöst',
'competition.reward.status.expired': 'Abgelaufen',
'competition.reward.status.revoked': 'Widerrufen',
'competition.reward.my_claims': 'Meine Preise',
'competition.reward.already_claimed': 'Preis bereits eingelöst',
'competition.reward.not_available': 'Preis nicht mehr verfügbar',
'competition.reward.requirement_not_met': 'Voraussetzung nicht erfüllt',
'competition.dsgvo_notice': 'Hinweis: Personenbezogene Daten werden gemäß DSGVO verarbeitet.',
'competition.jugendschutz_notice': 'Hinweis: Dieser Wettbewerb entspricht den Anforderungen des Jugendschutzgesetzes.',
// German Grade Labels
'grading.sehr_gut': 'Sehr gut',
'grading.gut': 'Gut',
@@ -5352,6 +5495,51 @@ const en: TranslationDict = {
'notebooks.tab_all': 'My Notebooks',
'notebooks.owner': 'By',
// ─── Learning Content (German Curriculum) ──────────────────────────
'notebooks.learn': 'Learn',
'notebooks.topics': 'Topics',
'notebooks.lessons': 'Lessons',
'notebooks.exercises': 'Exercises',
'notebooks.quiz': 'Quiz',
'notebooks.flashcards': 'Flashcards',
'notebooks.progress': 'Progress',
'notebooks.grade_level': 'Grade Level',
'notebooks.curriculum': 'Curriculum',
'notebooks.subject_topic': 'Topic',
'notebooks.difficulty': 'Difficulty',
'notebooks.easy': 'Easy',
'notebooks.medium': 'Medium',
'notebooks.hard': 'Hard',
'notebooks.question': 'Question',
'notebooks.answer': 'Answer',
'notebooks.correct': 'Correct',
'notebooks.incorrect': 'Incorrect',
'notebooks.explanation': 'Explanation',
'notebooks.try_again': 'Try Again',
'notebooks.next_question': 'Next Question',
'notebooks.score': 'Score',
'notebooks.time_taken': 'Time Taken',
'notebooks.mastery': 'Mastery',
'notebooks.practice_more': 'Practice More',
'notebooks.well_done': 'Well Done!',
'notebooks.keep_practicing': 'Keep Practicing!',
'notebooks.subject_math': 'Mathematics',
'notebooks.subject_german': 'German',
'notebooks.subject_science': 'General Studies',
'notebooks.subject_english': 'English',
'notebooks.subject_music': 'Music',
'notebooks.subject_art': 'Art',
'notebooks.subject_religion': 'Religion/Ethics',
'notebooks.subject_pe': 'Physical Education',
'notebooks.papier_sparen': 'Save Paper, Protect the Environment',
'notebooks.digital_instead_paper': 'Digital Notebooks Instead of Paper',
'notebooks.environment_tip': 'With digital notebooks you protect the environment — no paper waste!',
'notebooks.type_lined': 'Lined',
'notebooks.type_grid': 'Grid',
'notebooks.type_blank': 'Blank',
'notebooks.type_dotted': 'Dotted',
'notebooks.type_music': 'Music Lines',
// ─── Enhanced Notifications ──────────────────────────────────────
'notifications.today': 'Today',
'notifications.yesterday': 'Yesterday',
@@ -5395,6 +5583,19 @@ const en: TranslationDict = {
'student.view_all': 'View all',
'student.welcome_back': 'Welcome back',
'student.progress_overview': 'Your Learning Progress',
'student.papier_sparen_title': 'Save Paper, Protect the Environment',
'student.papier_sparen_subtitle': 'Digital notebooks instead of paper — With digital notebooks you protect the environment!',
'student.go_to_notebooks': 'Go to my Notebooks',
'student.my_notebooks_quick': 'My Notebooks',
'student.show_all_notebooks': 'Show all notebooks',
'student.start_learning': 'Start learning',
'student.learning_progress': 'Learning Progress',
'student.continue_learning': 'Continue learning',
'student.subject_progress': 'Subject Progress',
'student.notebook_math': 'Mathematics',
'student.notebook_german': 'German',
'student.notebook_english': 'English',
'student.notebook_science': 'Sciences',
'nav.student_competencies': 'My Competencies',
'nav.student_grades': 'My Grades',
'nav.student_notebooks': 'My Notebooks',
@@ -5865,6 +6066,7 @@ const en: TranslationDict = {
'nav.portfolio': 'Portfolio',
'nav.timetable': 'Timetable',
'nav.resources': 'Resources',
'nav.competitions': 'Competitions',
// Navigation - Homework
'nav.homework': 'Homework',
@@ -6538,6 +6740,90 @@ const en: TranslationDict = {
'avatar.change': 'Change Photo',
'avatar.remove': 'Remove Photo',
// Competition System
'competition.title': 'Competitions',
'competition.create': 'Create Competition',
'competition.edit': 'Edit Competition',
'competition.delete': 'Delete Competition',
'competition.manage': 'Manage Competitions',
'competition.details': 'Competition Details',
'competition.name': 'Competition',
'competition.description': 'Description',
'competition.type': 'Type',
'competition.category': 'Category',
'competition.status': 'Status',
'competition.start_date': 'Start Date',
'competition.end_date': 'End Date',
'competition.registration_deadline': 'Registration Deadline',
'competition.max_participants': 'Max Participants',
'competition.scoring_type': 'Scoring',
'competition.rules': 'Rules',
'competition.is_public': 'Publicly Visible',
'competition.subject': 'Subject',
'competition.created_by': 'Created by',
'competition.no_competitions': 'No competitions available',
'competition.type.class': 'Class Competition',
'competition.type.inter_class': 'Inter-Class',
'competition.type.inter_school': 'Inter-School',
'competition.category.academic': 'Academic',
'competition.category.sports': 'Sports',
'competition.category.creativity': 'Creativity',
'competition.category.citizenship': 'Citizenship',
'competition.category.digital': 'Digital',
'competition.category.reading': 'Reading',
'competition.category.stem': 'STEM',
'competition.category.other': 'Other',
'competition.status.draft': 'Draft',
'competition.status.registration': 'Registration',
'competition.status.active': 'Active',
'competition.status.completed': 'Completed',
'competition.status.cancelled': 'Cancelled',
'competition.scoring.points': 'Points',
'competition.scoring.rank': 'Ranking',
'competition.scoring.time': 'Time',
'competition.scoring.badge_count': 'Badge Count',
'competition.register': 'Register',
'competition.register_participant': 'Register Participant',
'competition.update_score': 'Update Score',
'competition.leaderboard': 'Leaderboard',
'competition.participants': 'Participants',
'competition.rewards': 'Rewards',
'competition.reward.create': 'Add Reward',
'competition.reward.type': 'Reward Type',
'competition.reward.type.digital_code': 'Digital Code',
'competition.reward.type.badge': 'Badge',
'competition.reward.type.certificate': 'Certificate',
'competition.reward.type.experience': 'Experience',
'competition.reward.type.physical': 'Physical Reward',
'competition.reward.provider': 'Provider',
'competition.reward.provider.netflix': 'Netflix',
'competition.reward.provider.amazon': 'Amazon',
'competition.reward.provider.cinema': 'Cinema',
'competition.reward.provider.theater': 'Theater',
'competition.reward.provider.concert': 'Concert',
'competition.reward.provider.custom': 'Custom',
'competition.reward.quantity': 'Quantity',
'competition.reward.claimed': 'Claimed',
'competition.reward.available': 'Available',
'competition.reward.rank_requirement': 'Minimum Rank',
'competition.reward.points_required': 'Minimum Points',
'competition.reward.image': 'Reward Image',
'competition.reward.code': 'Code',
'competition.reward.claim': 'Claim',
'competition.reward.claimed_at': 'Claimed on',
'competition.reward.expires': 'Expires on',
'competition.reward.status': 'Status',
'competition.reward.status.pending': 'Pending',
'competition.reward.status.claimed': 'Claimed',
'competition.reward.status.expired': 'Expired',
'competition.reward.status.revoked': 'Revoked',
'competition.reward.my_claims': 'My Rewards',
'competition.reward.already_claimed': 'Reward already claimed',
'competition.reward.not_available': 'Reward no longer available',
'competition.reward.requirement_not_met': 'Requirement not met',
'competition.dsgvo_notice': 'Notice: Personal data is processed in accordance with GDPR.',
'competition.jugendschutz_notice': 'Notice: This competition complies with the Youth Protection Act.',
// German Grade Labels
'grading.sehr_gut': 'Sehr gut',
'grading.gut': 'Gut',