fix: Namensabgleich und Tab-Wechsel im Klassenlehrer-Bereich, Fehltag-Hervorhebung

Zwei Bugs behoben:
- Gefilterte Listen blieben nach Klick auf eine Roster-Zeile leer: WebUntis
  liefert Schülernamen je nach Bericht in unterschiedlicher Reihenfolge
  (Schülerreport "Vorname Nachname", Fehlzeiten-/Klassenbuchberichte
  "Nachname Vorname"). Neuer reihenfolge-unabhängiger Namensvergleich
  (UntisNameMatching), betraf auch den Roster-Namensabgleich der Übersicht
  selbst (Ampel-Symbole/Badges waren ohne ExternKey vermutlich fast immer
  falsch).
- Nach dem ersten Tab-Wechsel (Klick auf Roster-Zeile) blieb ein zweiter
  Klick wirkungslos, sobald man zwischendurch manuell zur Übersicht
  zurückgeklickt hatte: TabbedPage.SelectedIndex war ohne Mode=TwoWay
  gebunden. Explizit ergänzt.

Zusätzlich: Fehlzeiten-Zeilen für einen kompletten Fehltag werden jetzt
dezent hervorgehoben (Nutzer-Feedback, bisher nicht erkennbar).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 23:27:14 +02:00
co-authored by Claude Sonnet 5
parent 2b29dea824
commit 69cc16bbd2
8 changed files with 131 additions and 29 deletions
@@ -100,8 +100,33 @@ public sealed class ClassTeacherViewModelsTests
Assert.False(cem.HasRecentClassRegisterEntry);
}
private static UntisStudentDto Student(int externKey, string displayName) => new(
UntisId: externKey, ExternKey: externKey, ClassName: "6a", Name: displayName, LongName: null,
[Fact]
public void RosterBuild_ErkenntNamenAuchInVertauschterReihenfolge()
{
// WebUntis liefert Namen im Schülerreport als "Vorname Nachname", in Fehlzeiten-/
// Klassenbuchberichten aber typischerweise als "Nachname Vorname" - der Namensabgleich
// muss das auch ohne ExternKey erkennen (Regressionstest für genau diesen Bug).
var students = new[] { Student(externKey: null, displayName: "Ben Schmidt") };
var todayAbsences = new[]
{
new ClassAbsenceDaySummaryRow(new DateOnly(2026, 8, 25), "Schmidt Ben", null, 1, 45,
["Che"], [3], ["nicht entsch."], ["Absent"], null, null, false),
};
var recentClassRegister = new[]
{
new UntisForeignClassRegisterEventDto("6a", 20260824, "Deu", "Schmidt Ben", "mueller",
"Vergessen", "Organisation", "Buch vergessen"),
};
var roster = ClassTeacherRosterRow.Build(students, todayAbsences, recentClassRegister);
var ben = Assert.Single(roster);
Assert.True(ben.HasAbsenceToday);
Assert.True(ben.HasRecentClassRegisterEntry);
}
private static UntisStudentDto Student(int? externKey, string displayName) => new(
UntisId: externKey ?? 0, ExternKey: externKey, ClassName: "6a", Name: displayName, LongName: null,
ForeName: null, DisplayName: displayName, Gender: null, BirthDate: null, BirthDateRaw: null,
EntryDate: null, EntryDateRaw: null, ExitDate: null, ExitDateRaw: null, Text: null,
MedicalReportDuty: null, Schulpflicht: null, Majority: null,
@@ -0,0 +1,21 @@
using LehrerApp.Desktop.ViewModels.ClassTeacher;
using Xunit;
namespace LehrerApp.Desktop.Tests;
public sealed class UntisNameMatchingTests
{
[Theory]
[InlineData("Ben Schmidt", "Schmidt Ben")]
[InlineData("Ada Müller", "MÜLLER ADA")]
[InlineData("Max Mustermann", "mustermann max")]
public void NamesMatch_IstReihenfolgeUndGrossKleinschreibungUnabhaengig(string a, string b) =>
Assert.True(UntisNameMatching.NamesMatch(a, b));
[Theory]
[InlineData("Ben Schmidt", "Ben Müller")]
[InlineData(null, "Ben Schmidt")]
[InlineData("Ben Schmidt", "")]
public void NamesMatch_LehntUnterschiedlicheOderFehlendeNamenAb(string? a, string b) =>
Assert.False(UntisNameMatching.NamesMatch(a, b));
}