feat: add Today dashboard, backup conflict resolution, and student call names - #58
Merged
Merged
Conversation
Add a Today landing screen showing every active group's attendance, grade, homework, and material status for the current date with a one-tap entry into that group's lesson, replacing the flat Groups list as the post-unlock destination. Add a guided conflict resolution screen for WebDAV sync conflicts, reachable from a new warning banner in Settings > Backups. Lets the teacher compare the canonical and conflicting backups (device, time, size) and either keep this device's version (re-exports it as the new canonical) or adopt the server version, instead of having to spot and interpret raw `_CONFLICT_` files in the flat backup list. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
flutter analyze and a widget-test render pass turned up two real bugs: an unbounded Text inside a mainAxisSize.min Row overflows instead of ellipsizing once the available width is narrower than the label (hit by the new Today dashboard's stat chips, and latent in the pre-existing LessonSummaryCard chip they were modeled on). Wrap both in Flexible so they actually shrink. Also drop two now-unused imports flagged by analyze after fixing the wrong colorFromHex import and an intl import already covered by easy_localization. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
Turn the screen stateful so a teacher can pick any date via a date picker (tap the date header or the calendar icon in the app bar) to review or catch up on a past day's attendance/grades/homework/material across all groups, the same way as for today. A "jump to today" icon appears in the app bar whenever browsing away from the current date. No new repository work needed: TodayRepository.watchTodayOverview was already parameterized by date. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
Accept an optional initialDate on TodayDashboardScreen and read it from a ?date= route query parameter in /today, the same convention Lesson Mode already uses for its own initialDate. Falls back to today when absent, so existing links and the nav bar destination are unaffected; this only adds the ability to open the dashboard directly onto a specific date from a URL. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
Teachers often call a student by a name that isn't their official first name. Add an optional callName field to Student, shown in place of firstName everywhere a name is displayed while the surname always stays visible alongside it (both sort orders). The official firstName and lastName are unchanged and still what CSV exports and WebUntis import matching use. - New nullable students_table.callName column (schema v22, migrated). - studentDisplayName/studentDisplayNameWithOrigin/ isGeneratedStudentDisplayName in formatting.dart prefer callName, falling back to firstName when unset or blank. - Student form gains an optional "Call name" field; StudentDraft, StudentRepository.addStudent/updateStudent, and GroupRepository's clone-group student copy all carry it through. - Every existing display call site (lesson roster, seating plan, notes, lists, group/student detail and summary screens, avatar widgets) now passes the student's callName. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
isGeneratedStudentDisplayName only compared the stored label against the name built from the call name, so a checklist label written before a call name was set (e.g. "Mustermann, Alexander") stopped being recognized as auto-generated the moment one was added. The item then froze at the old name and grew a redundant linked-student chip, because the screen treated it as a custom label. Match against the plain first name as well as the call name, so labels generated either side of the change still re-render with the current name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
Resolving a sync conflict with "keep this device's version" adopted the server's revision and then re-exported. But exportNow() reports upload failures through the backup status message, not its return value, so a failed upload looked like success: the conflict screen told the teacher it was resolved, and the adopted revision stayed persisted. The next auto-export would then overwrite the server copy without ever raising the conflict again. Treat the export as successful only when lastExportedAt actually advances, restore the previous revision otherwise, and return a real error code so the screen reports the failure instead of dismissing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three teacher-facing features, plus fixes for bugs found while building them.
Today dashboard
A landing screen showing every active group's status for one date in one place — attendance, grades, homework, and material — with a single tap into that group's lesson. Replaces the flat Groups list as the post-unlock destination, so a teacher with several periods a day no longer has to open each group and its calendar to find out whether a lesson has already been logged.
/today?date=YYYY-MM-DD, matching the convention Lesson Mode already uses.TodayRepository), so cards update live as data changes.Backup conflict resolution
When a WebDAV export finds the canonical backup has moved on to a revision this device never saw, it already uploaded local changes as a separate
_CONFLICT_copy rather than overwriting. Until now the teacher had to spot and interpret those raw files in the flat backup list themselves._CONFLICT_entries are hidden from the flat list, since they're now represented by the banner.libraryNameForBackupFile, which didn't strip the_CONFLICT_<timestamp>suffix and so displayed those backups under a mangled library name.Student call names (Rufname)
Teachers often address a student by a name that isn't their official first name.
callNameon students, shown in place of the first name everywhere a name is displayed, with the surname always kept alongside it in both sort orders ("Alex Mustermann" / "Mustermann, Alex").firstName/lastNameare unchanged, so CSV exports and WebUntis import matching still use official names.Schema: adds a nullable
students_table.call_namecolumn, schema version 21 → 22, with a migration.Fixes found while building the above
Textinside amainAxisSize.minRowoverflowed instead of ellipsising on narrow widths — hit by the new dashboard's stat chips and latent in the existingLessonSummaryCardchips they were modelled on.exportNow()reports upload failures through the status message rather than its return value. A failed upload therefore looked like success: the screen said the conflict was resolved, and the adopted revision stayed persisted, so the next auto-export would silently overwrite the server copy. The revision is now rolled back unless the export actually lands, and the failure is reported.Testing
flutter analyzeclean; full suite green (102 tests, 5 added covering call-name fallback, generated-label matching, conflict-copy filename parsing, and the revision rollback).Not covered by tests: the multi-device WebDAV conflict flow end to end, which needs two real devices against a live server — the unit tests exercise the controller and filename logic against a fake backup service only.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LJRKq8YRXgS65F9dAcQxNi