feat: Fehlzeiten Calender
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using LehrerApp.Desktop.Services;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels.Students;
|
||||
|
||||
public sealed record AttendanceCalendarSizeChoice(AttendanceCalendarSize Value, string DisplayName);
|
||||
|
||||
public partial class AttendanceCalendarConfigurationViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty] private DateTimeOffset? _startMonth;
|
||||
[ObservableProperty] private int _monthCount;
|
||||
[ObservableProperty] private AttendanceCalendarSizeChoice _selectedSize;
|
||||
|
||||
public IReadOnlyList<int> MonthCounts { get; } = [1, 2, 3];
|
||||
public IReadOnlyList<AttendanceCalendarSizeChoice> Sizes { get; } =
|
||||
[
|
||||
new(AttendanceCalendarSize.Small, "Klein"),
|
||||
new(AttendanceCalendarSize.Medium, "Standard"),
|
||||
new(AttendanceCalendarSize.Large, "Groß"),
|
||||
];
|
||||
|
||||
public AttendanceCalendarConfigurationViewModel(AttendanceCalendarOptions options)
|
||||
{
|
||||
StartMonth = new DateTimeOffset(options.NormalizedStartMonth.ToDateTime(TimeOnly.MinValue));
|
||||
MonthCount = options.NormalizedMonthCount;
|
||||
SelectedSize = Sizes.First(s => s.Value == options.Size);
|
||||
}
|
||||
|
||||
public AttendanceCalendarOptions BuildResult()
|
||||
{
|
||||
var date = DateOnly.FromDateTime((StartMonth ?? DateTimeOffset.Now).LocalDateTime);
|
||||
return new AttendanceCalendarOptions(new DateOnly(date.Year, date.Month, 1), MonthCount,
|
||||
SelectedSize.Value);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
||||
private readonly Student _student;
|
||||
private readonly TemplateStore _templates;
|
||||
private readonly ITemplateRenderer _renderer;
|
||||
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _attendanceCalendarFactory;
|
||||
private AttendanceCalendarOptions _attendanceCalendarOptions = new(
|
||||
new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1), 1);
|
||||
|
||||
[ObservableProperty] private LetterTemplateChoice? _selectedTemplate;
|
||||
[ObservableProperty] private LetterContactChoice? _selectedContact;
|
||||
@@ -21,6 +24,9 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
||||
[ObservableProperty] private string _teacherName = "";
|
||||
[ObservableProperty] private string _generationError = "";
|
||||
[ObservableProperty] private bool _canGenerate;
|
||||
[ObservableProperty] private bool _usesAttendanceCalendar;
|
||||
[ObservableProperty] private bool _attendanceCalendarConfigured;
|
||||
[ObservableProperty] private string _attendanceCalendarSummary = "1 Monat · Standardgröße";
|
||||
|
||||
public string StudentName => _student.FullName;
|
||||
public ObservableCollection<LetterTemplateChoice> Templates { get; } = [];
|
||||
@@ -34,9 +40,11 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
||||
$"{SelectedTemplate?.Name ?? "Elternbrief"}_{_student.LastName}_{_student.FirstName}.pdf");
|
||||
|
||||
public CreateLetterDialogViewModel(Student student, TemplateStore templates, ITemplateRenderer renderer,
|
||||
IGroupMembershipRepository memberships, IGroupRepository groups)
|
||||
IGroupMembershipRepository memberships, IGroupRepository groups,
|
||||
Func<AttendanceCalendarOptions, DrawingValue>? attendanceCalendarFactory = null)
|
||||
{
|
||||
_student = student; _templates = templates; _renderer = renderer;
|
||||
_attendanceCalendarFactory = attendanceCalendarFactory;
|
||||
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))
|
||||
@@ -45,10 +53,21 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
||||
RefreshValidation();
|
||||
}
|
||||
|
||||
partial void OnSelectedTemplateChanged(LetterTemplateChoice? value) { OnPropertyChanged(nameof(SuggestedFileName)); RefreshValidation(); }
|
||||
partial void OnSelectedTemplateChanged(LetterTemplateChoice? value)
|
||||
{
|
||||
OnPropertyChanged(nameof(SuggestedFileName));
|
||||
UsesAttendanceCalendar = TemplateUsesAttendanceCalendar(value);
|
||||
AttendanceCalendarConfigured = false;
|
||||
ResetAttendanceCalendarOptions();
|
||||
RefreshValidation();
|
||||
}
|
||||
partial void OnSelectedContactChanged(LetterContactChoice? value) => RefreshValidation();
|
||||
partial void OnSelectedGroupChanged(LetterGroupChoice? value) => RefreshValidation();
|
||||
partial void OnLetterDateChanged(DateTimeOffset? value) => RefreshValidation();
|
||||
partial void OnLetterDateChanged(DateTimeOffset? value)
|
||||
{
|
||||
if (!AttendanceCalendarConfigured) ResetAttendanceCalendarOptions();
|
||||
RefreshValidation();
|
||||
}
|
||||
partial void OnLetterTextChanged(string value) => RefreshValidation();
|
||||
partial void OnTeacherNameChanged(string value) => RefreshValidation();
|
||||
|
||||
@@ -91,7 +110,60 @@ 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);
|
||||
DateOnly.FromDateTime((LetterDate ?? DateTimeOffset.Now).LocalDateTime), LetterText, TeacherName,
|
||||
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions) : null);
|
||||
|
||||
public AttendanceCalendarOptions GetAttendanceCalendarOptions() => _attendanceCalendarOptions;
|
||||
|
||||
public void SetAttendanceCalendarOptions(AttendanceCalendarOptions options)
|
||||
{
|
||||
_attendanceCalendarOptions = options with { StartMonth = options.NormalizedStartMonth,
|
||||
MonthCount = options.NormalizedMonthCount };
|
||||
AttendanceCalendarConfigured = true;
|
||||
AttendanceCalendarSummary = FormatAttendanceCalendarSummary(_attendanceCalendarOptions);
|
||||
RefreshValidation();
|
||||
}
|
||||
|
||||
private void ResetAttendanceCalendarOptions()
|
||||
{
|
||||
var date = DateOnly.FromDateTime((LetterDate ?? DateTimeOffset.Now).LocalDateTime);
|
||||
_attendanceCalendarOptions = new AttendanceCalendarOptions(new(date.Year, date.Month, 1), 1);
|
||||
AttendanceCalendarSummary = FormatAttendanceCalendarSummary(_attendanceCalendarOptions);
|
||||
}
|
||||
|
||||
private bool TemplateUsesAttendanceCalendar(LetterTemplateChoice? choice)
|
||||
{
|
||||
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));
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException or InvalidDataException or TemplateValidationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UsesPlaceholder(TemplateLayout layout) =>
|
||||
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 });
|
||||
|
||||
private static string FormatAttendanceCalendarSummary(AttendanceCalendarOptions options)
|
||||
{
|
||||
var month = options.NormalizedStartMonth.ToString("MMMM yyyy",
|
||||
System.Globalization.CultureInfo.GetCultureInfo("de-DE"));
|
||||
var size = options.Size switch
|
||||
{
|
||||
AttendanceCalendarSize.Small => "Klein",
|
||||
AttendanceCalendarSize.Large => "Groß",
|
||||
_ => "Standard",
|
||||
};
|
||||
return $"Ab {month} · {options.NormalizedMonthCount} Monat{(options.NormalizedMonthCount == 1 ? "" : "e")} · {size}";
|
||||
}
|
||||
|
||||
private static bool IsEmpty(PlaceholderValue value) => LetterPlaceholderBuilder.IsEmpty(value);
|
||||
private static string SanitizeFileName(string value)
|
||||
|
||||
Reference in New Issue
Block a user