This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,13 @@ public partial class SettingsViewModel
|
||||
_eventQueue.MarkReviewed(item.Id);
|
||||
SyncConflicts.Remove(item);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ClearSyncConflicts()
|
||||
{
|
||||
_eventQueue.ClearConflicts();
|
||||
SyncConflicts.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public class SyncConflictListItem(ConflictEntry c)
|
||||
|
||||
@@ -304,6 +304,11 @@ public partial class DocumentationDialogViewModel : ObservableObject
|
||||
var valid = true;
|
||||
|
||||
if (CanPickStudent && SelectedStudent is null) { StudentError = "Bezug auswählen."; valid = false; }
|
||||
if (!CanPickStudent && _studentId == Guid.Empty && _contextGroupId is null)
|
||||
{
|
||||
StudentError = "Die Schülerliste ist noch nicht geladen. Dialog schließen und erneut öffnen.";
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Title)) { TitleError = "Titel erforderlich."; valid = false; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user