feat: Fehlzeiten Tabelle für Templates

This commit is contained in:
2026-09-15 10:35:55 +02:00
parent 1671796844
commit 09cbbf1b77
11 changed files with 178 additions and 18 deletions
@@ -13,6 +13,7 @@ public partial class CreateLetterDialogViewModel : ObservableObject
private readonly TemplateStore _templates;
private readonly ITemplateRenderer _renderer;
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _attendanceCalendarFactory;
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _absenceDayListFactory;
private AttendanceCalendarOptions _attendanceCalendarOptions = new(
new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1), 1);
@@ -25,6 +26,7 @@ public partial class CreateLetterDialogViewModel : ObservableObject
[ObservableProperty] private string _generationError = "";
[ObservableProperty] private bool _canGenerate;
[ObservableProperty] private bool _usesAttendanceCalendar;
[ObservableProperty] private bool _usesAbsenceDayList;
[ObservableProperty] private bool _attendanceCalendarConfigured;
[ObservableProperty] private string _attendanceCalendarSummary = "1 Monat · Standardgröße";
@@ -36,15 +38,18 @@ public partial class CreateLetterDialogViewModel : ObservableObject
public bool HasIssues => Issues.Count > 0;
public bool HasNoTemplates => Templates.Count == 0;
public bool HasNoContacts => Contacts.Count == 0;
public bool UsesAttendanceAdvancedContent => UsesAttendanceCalendar || UsesAbsenceDayList;
public string SuggestedFileName => SanitizeFileName(
$"{SelectedTemplate?.Name ?? "Elternbrief"}_{_student.LastName}_{_student.FirstName}.pdf");
public CreateLetterDialogViewModel(Student student, TemplateStore templates, ITemplateRenderer renderer,
IGroupMembershipRepository memberships, IGroupRepository groups,
Func<AttendanceCalendarOptions, DrawingValue>? attendanceCalendarFactory = null)
Func<AttendanceCalendarOptions, DrawingValue>? attendanceCalendarFactory = null,
Func<AttendanceCalendarOptions, DrawingValue>? absenceDayListFactory = null)
{
_student = student; _templates = templates; _renderer = renderer;
_attendanceCalendarFactory = attendanceCalendarFactory;
_absenceDayListFactory = absenceDayListFactory;
foreach (var template in templates.GetTemplates()) Templates.Add(new(template));
foreach (var contact in student.Contacts.Where(c => !c.InvalidSince.HasValue).OrderBy(c => c.Name)) Contacts.Add(new(contact));
foreach (var membership in memberships.GetByStudent(student.Id))
@@ -56,7 +61,11 @@ public partial class CreateLetterDialogViewModel : ObservableObject
partial void OnSelectedTemplateChanged(LetterTemplateChoice? value)
{
OnPropertyChanged(nameof(SuggestedFileName));
UsesAttendanceCalendar = TemplateUsesAttendanceCalendar(value);
UsesAttendanceCalendar = TemplateUsesPlaceholder(value,
StudentAttendanceCalendarDrawingBuilder.PlaceholderName);
UsesAbsenceDayList = TemplateUsesPlaceholder(value,
StudentAbsenceDayListDrawingBuilder.PlaceholderName);
OnPropertyChanged(nameof(UsesAttendanceAdvancedContent));
AttendanceCalendarConfigured = false;
ResetAttendanceCalendarOptions();
RefreshValidation();
@@ -111,7 +120,8 @@ public partial class CreateLetterDialogViewModel : ObservableObject
private IReadOnlyDictionary<string, PlaceholderValue> BuildValues() => LetterPlaceholderBuilder.BuildStandardValues(
_student, SelectedContact?.Model, SelectedGroup?.Model,
DateOnly.FromDateTime((LetterDate ?? DateTimeOffset.Now).LocalDateTime), LetterText, TeacherName,
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions) : null);
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions) : null,
UsesAbsenceDayList ? _absenceDayListFactory?.Invoke(_attendanceCalendarOptions) : null);
public AttendanceCalendarOptions GetAttendanceCalendarOptions() => _attendanceCalendarOptions;
@@ -131,14 +141,14 @@ public partial class CreateLetterDialogViewModel : ObservableObject
AttendanceCalendarSummary = FormatAttendanceCalendarSummary(_attendanceCalendarOptions);
}
private bool TemplateUsesAttendanceCalendar(LetterTemplateChoice? choice)
private bool TemplateUsesPlaceholder(LetterTemplateChoice? choice, string placeholderName)
{
if (choice is null) return false;
try
{
var loaded = _templates.Load(choice.Model);
return UsesPlaceholder(loaded.Layout) ||
(loaded.ContinuationLayout is not null && UsesPlaceholder(loaded.ContinuationLayout));
return UsesPlaceholder(loaded.Layout, placeholderName) ||
(loaded.ContinuationLayout is not null && UsesPlaceholder(loaded.ContinuationLayout, placeholderName));
}
catch (Exception ex) when (ex is IOException or InvalidDataException or TemplateValidationException)
{
@@ -146,11 +156,11 @@ public partial class CreateLetterDialogViewModel : ObservableObject
}
}
private static bool UsesPlaceholder(TemplateLayout layout) =>
private static bool UsesPlaceholder(TemplateLayout layout, string placeholderName) =>
layout.Elements.Concat(layout.PageTemplates.SelectMany(p => p.Elements))
.Concat(layout.ContentFlows.SelectMany(f => f.Elements))
.Any(e => e is DrawBoxElement { Placeholder: StudentAttendanceCalendarDrawingBuilder.PlaceholderName }
or FlowDrawBoxElement { Placeholder: StudentAttendanceCalendarDrawingBuilder.PlaceholderName });
.Any(e => e is DrawBoxElement draw && draw.Placeholder == placeholderName ||
e is FlowDrawBoxElement flow && flow.Placeholder == placeholderName);
private static string FormatAttendanceCalendarSummary(AttendanceCalendarOptions options)
{