Stundenplan: Wochenraster, Wochennavigation, Ferien/Feiertage (Kapitel 4.3)

Neuer Stundenplan mit "Heute"-Standardansicht (Tagesliste unten angedockt,
gruppenübergreifendes Wochenraster mit Fach/Klasse/Raum/Thema, Vor-/Zurück-
Navigation zwischen Kalenderwochen) und separatem Bearbeiten-Raster für die
wöchentliche Zuordnung. Badges für Ferien-/Klausur-Nähe und ausgegraute
Ferientage direkt im Plan statt einer separaten Liste. Ferien-/Feiertage-
Pflege (Bundesland, Schulferien) sitzt jetzt in den Einstellungen statt im
Stundenplan selbst.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 02:29:18 +02:00
co-authored by Claude Sonnet 5
parent ee1a47641e
commit 99b2de6519
26 changed files with 2154 additions and 18 deletions
@@ -0,0 +1,79 @@
using LehrerApp.Core.Models;
namespace LehrerApp.Core.Services;
public record PublicHoliday(DateOnly Date, string Name);
/// <summary>
/// Berechnet die gesetzlichen Feiertage eines Bundeslands für ein Kalenderjahr (4.3.5).
/// Im Gegensatz zu Schulferien sind Feiertage über eine feste Regel je Bundesland herleitbar und
/// werden deshalb nicht in der Datenbank gespeichert.
/// </summary>
public class PublicHolidayService
{
public List<PublicHoliday> GetHolidays(int year, GermanState state)
{
var easterSunday = EasterSunday(year);
var holidays = new List<PublicHoliday>
{
new(new DateOnly(year, 1, 1), "Neujahr"),
new(easterSunday.AddDays(-2), "Karfreitag"),
new(easterSunday.AddDays(1), "Ostermontag"),
new(new DateOnly(year, 5, 1), "Tag der Arbeit"),
new(easterSunday.AddDays(39), "Christi Himmelfahrt"),
new(easterSunday.AddDays(50), "Pfingstmontag"),
new(new DateOnly(year, 10, 3), "Tag der Deutschen Einheit"),
new(new DateOnly(year, 12, 25), "1. Weihnachtstag"),
new(new DateOnly(year, 12, 26), "2. Weihnachtstag"),
};
if (state is GermanState.BW or GermanState.BY or GermanState.ST)
holidays.Add(new(new DateOnly(year, 1, 6), "Heilige Drei Könige"));
if (state is GermanState.BE)
holidays.Add(new(new DateOnly(year, 3, 8), "Internationaler Frauentag"));
if (state is GermanState.BW or GermanState.BY or GermanState.HE or GermanState.NW
or GermanState.RP or GermanState.SL)
holidays.Add(new(easterSunday.AddDays(60), "Fronleichnam"));
if (state is GermanState.SL)
holidays.Add(new(new DateOnly(year, 8, 15), "Mariä Himmelfahrt"));
if (state is GermanState.BB or GermanState.MV or GermanState.SN or GermanState.ST
or GermanState.TH or GermanState.HB or GermanState.HH or GermanState.NI
or GermanState.SH)
holidays.Add(new(new DateOnly(year, 10, 31), "Reformationstag"));
if (state is GermanState.BW or GermanState.BY or GermanState.NW or GermanState.RP
or GermanState.SL)
holidays.Add(new(new DateOnly(year, 11, 1), "Allerheiligen"));
if (state is GermanState.SN)
holidays.Add(new(BussUndBettag(year), "Buß- und Bettag"));
return holidays.OrderBy(h => h.Date).ToList();
}
/// Gauß'sche Osterformel.
private static DateOnly EasterSunday(int year)
{
int a = year % 19, b = year / 100, c = year % 100;
int d = b / 4, e = b % 4, f = (b + 8) / 25, g = (b - f + 1) / 3;
int h = (19 * a + b - d - g + 15) % 30;
int i = c / 4, k = c % 4, l = (32 + 2 * e + 2 * i - h - k) % 7;
int m = (a + 11 * h + 22 * l) / 451;
int month = (h + l - 7 * m + 114) / 31;
int day = (h + l - 7 * m + 114) % 31 + 1;
return new DateOnly(year, month, day);
}
/// Buß- und Bettag: Mittwoch vor dem 23. November (entspricht dem letzten Mittwoch vor dem
/// ersten Advent).
private static DateOnly BussUndBettag(int year)
{
var date = new DateOnly(year, 11, 23);
while (date.DayOfWeek != DayOfWeek.Wednesday) date = date.AddDays(-1);
return date;
}
}
@@ -0,0 +1,42 @@
using System.Text.Json;
using LehrerApp.Core.Models;
namespace LehrerApp.Core.Services;
internal class SchoolCalendarConfig
{
public GermanState State { get; set; } = GermanState.NW;
}
/// <summary>Welches Bundesland für die Feiertagsberechnung (4.3.5) gilt.</summary>
public class SchoolCalendarSettingsService
{
private readonly string _configPath;
private SchoolCalendarConfig _config;
public GermanState State => _config.State;
public SchoolCalendarSettingsService(string appDataPath)
{
_configPath = Path.Combine(appDataPath, "schoolcalendar.json");
_config = Load();
}
public void SetState(GermanState state)
{
_config.State = state;
File.WriteAllText(_configPath, JsonSerializer.Serialize(_config));
}
private SchoolCalendarConfig Load()
{
try
{
if (File.Exists(_configPath))
return JsonSerializer.Deserialize<SchoolCalendarConfig>(File.ReadAllText(_configPath))
?? new SchoolCalendarConfig();
}
catch { /* beschädigte Konfiguration -> Standardwert */ }
return new SchoolCalendarConfig();
}
}