Fix: Sync Log und verwaise Einträge
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-09-07 17:09:38 +02:00
parent 835cccadec
commit 86e114580f
12 changed files with 145 additions and 12 deletions
@@ -199,6 +199,7 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
public bool ShowAbsenceListEmpty => !ShowMonthlyCalendar && !HasAbsenceEntries;
public bool HasCategoryAggregates => CategoryAggregates.Count > 0;
public bool HasOwnDocumentationEntries => OwnDocumentationEntries.Count > 0;
public bool CanAddOwnDocumentation => !Busy && _rosterMatches.Count > 0;
/// Die Kategorien-Chipreihe fasst nur WebUntis-Kategorien zusammen (<see cref="CategoryAggregates"/>)
/// — im Modus "Eigene Dokumentation" ausgeblendet, dort gibt es kein Äquivalent zu CategoryGroup.
public bool ShowCategoryAggregates => HasCategoryAggregates && !ShowOwnDocumentation;
@@ -431,7 +432,7 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
_loadedStart, _loadedEnd, StudentFilter);
foreach (var d in entries)
{
var name = _rosterMatches.First(m => m.StudentId == d.StudentId).DisplayName;
var name = DocumentationStudentDisplay(d, _rosterMatches);
OwnDocumentationEntries.Add(new DocumentationItem(d, name));
}
OwnDocumentationCount = OwnDocumentationEntries.Count;
@@ -452,7 +453,9 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
{
var matchedIds = rosterMatches.Select(m => m.StudentId).ToHashSet();
return all
.Where(d => !d.IsDeleted && matchedIds.Contains(d.StudentId) && d.Date >= start && d.Date <= end)
.Where(d => !d.IsDeleted &&
(matchedIds.Contains(d.StudentId) || IsUnassignedClassDocumentation(d)) &&
d.Date >= start && d.Date <= end)
.Where(d => MatchesOwnDocStudentFilter(d, rosterMatches, studentFilter))
.OrderByDescending(d => d.IsDraft).ThenByDescending(d => d.Date)
.ToList();
@@ -462,14 +465,37 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
IReadOnlyList<(Guid StudentId, string DisplayName)> rosterMatches, string studentFilter)
{
if (string.IsNullOrWhiteSpace(studentFilter)) return true;
if (IsUnassignedClassDocumentation(d))
return d.Participants.Any(p => NameContainsSearchTerm(p, studentFilter));
var name = rosterMatches.FirstOrDefault(m => m.StudentId == d.StudentId).DisplayName;
return name is not null && UntisNameMatching.NamesMatch(name, studentFilter);
return name is not null && NameContainsSearchTerm(name, studentFilter);
}
private static bool IsUnassignedClassDocumentation(Documentation d) =>
d.StudentId == Guid.Empty && d.GroupId is null;
private static bool NameContainsSearchTerm(string name, string search)
{
if (UntisNameMatching.NamesMatch(name, search)) return true;
var searchKey = UntisNameMatching.NameKey(search);
return searchKey.Length > 0 && UntisNameMatching.NameKey(name).Split(' ').Contains(searchKey);
}
private static string DocumentationStudentDisplay(Documentation d,
IReadOnlyList<(Guid StudentId, string DisplayName)> rosterMatches)
{
if (d.StudentId != Guid.Empty)
return rosterMatches.FirstOrDefault(m => m.StudentId == d.StudentId).DisplayName
?? "Unbekannter Schülerbezug";
return d.Participants.Count > 0
? $"Ohne festen Bezug · {string.Join(", ", d.Participants)}"
: "Ohne Schülerbezug";
}
[RelayCommand]
private async Task AddOwnDocumentation()
{
if (OnEditOwnDocumentation is null) return;
if (OnEditOwnDocumentation is null || !CanAddOwnDocumentation) return;
var options = _rosterMatches.Select(m => new StudentOption(m.StudentId, m.DisplayName)).ToList();
var result = await OnEditOwnDocumentation(options, null);
if (result is null) return;
@@ -553,6 +579,7 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
OnPropertyChanged(nameof(HasCategoryAggregates));
OnPropertyChanged(nameof(ShowCategoryAggregates));
OnPropertyChanged(nameof(HasOwnDocumentationEntries));
OnPropertyChanged(nameof(CanAddOwnDocumentation));
OnPropertyChanged(nameof(UntisCriticalCount));
}
}