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>
457 lines
22 KiB
C#
457 lines
22 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using CommunityToolkit.Mvvm.Input;
|
||
using LehrerApp.Core.Interfaces;
|
||
using LehrerApp.Core.Models;
|
||
using LehrerApp.Core.Services;
|
||
using System.Collections.ObjectModel;
|
||
|
||
namespace LehrerApp.Desktop.ViewModels.Planning;
|
||
|
||
// ── Bundesland: deutsche Anzeige (4.3.5) ─────────────────────────────────────
|
||
// Wird sowohl vom Stundenplan (Anzeige) als auch von den Einstellungen (Pflege) verwendet.
|
||
|
||
public static class GermanStateDisplay
|
||
{
|
||
private static readonly (GermanState State, string Name)[] Entries =
|
||
[
|
||
(GermanState.BW, "Baden-Württemberg"),
|
||
(GermanState.BY, "Bayern"),
|
||
(GermanState.BE, "Berlin"),
|
||
(GermanState.BB, "Brandenburg"),
|
||
(GermanState.HB, "Bremen"),
|
||
(GermanState.HH, "Hamburg"),
|
||
(GermanState.HE, "Hessen"),
|
||
(GermanState.MV, "Mecklenburg-Vorpommern"),
|
||
(GermanState.NI, "Niedersachsen"),
|
||
(GermanState.NW, "Nordrhein-Westfalen"),
|
||
(GermanState.RP, "Rheinland-Pfalz"),
|
||
(GermanState.SL, "Saarland"),
|
||
(GermanState.SN, "Sachsen"),
|
||
(GermanState.ST, "Sachsen-Anhalt"),
|
||
(GermanState.SH, "Schleswig-Holstein"),
|
||
(GermanState.TH, "Thüringen"),
|
||
];
|
||
|
||
public static string[] Options { get; } = Entries.Select(e => e.Name).ToArray();
|
||
public static string Label(GermanState s) => Entries.First(e => e.State == s).Name;
|
||
public static GermanState FromLabel(string label) =>
|
||
Entries.FirstOrDefault(e => e.Name == label).State;
|
||
}
|
||
|
||
// ── Stundenplan: "Heute"-Übersicht (Standardansicht, Wochenraster + Tagesliste) + Bearbeiten (4.3) ──
|
||
|
||
public partial class TimetableViewModel : ObservableObject
|
||
{
|
||
private const int FirstPeriod = 1;
|
||
private const int LastPeriod = 10;
|
||
private static readonly DayOfWeek[] Weekdays =
|
||
[DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday];
|
||
private static readonly string[] WeekdayColorPalette =
|
||
["#7F77DD", "#1D9E75", "#D85A30", "#D4537E", "#378ADD", "#639922", "#EF9F27", "#4C86A8"];
|
||
|
||
private readonly ITimetableSlotRepository _slots;
|
||
private readonly IGroupRepository _groups;
|
||
private readonly ISubjectRepository _subjects;
|
||
private readonly ILessonRepository _lessons;
|
||
private readonly IExamRepository _exams;
|
||
private readonly ISchoolHolidayRepository _schoolHolidays;
|
||
private readonly SchoolCalendarSettingsService _calendarSettings;
|
||
private readonly PublicHolidayService _publicHolidays;
|
||
private readonly SchoolYearService _schoolYear;
|
||
|
||
public ObservableCollection<TimetableCellItem> Cells { get; } = [];
|
||
public ObservableCollection<WeekCellItem> WeekItems { get; } = [];
|
||
public ObservableCollection<HoursWarningItem> HoursWarnings { get; } = [];
|
||
public ObservableCollection<TodayLessonItem> TodayItems { get; } = [];
|
||
|
||
[ObservableProperty] private int _activeTabIndex;
|
||
[ObservableProperty] private string _todayLabel = "";
|
||
[ObservableProperty] private string _weekRangeLabel = "";
|
||
[ObservableProperty] private int _weekOffset;
|
||
public bool IsCurrentWeek => WeekOffset == 0;
|
||
|
||
public Func<TimetableCellItem, Task>? OnEditSlot { get; set; }
|
||
public Action<Guid>? OnNavigateToGroup { get; set; }
|
||
|
||
public TimetableViewModel(ITimetableSlotRepository slots, IGroupRepository groups,
|
||
ISubjectRepository subjects, ILessonRepository lessons, IExamRepository exams,
|
||
ISchoolHolidayRepository schoolHolidays, SchoolCalendarSettingsService calendarSettings,
|
||
PublicHolidayService publicHolidays, SchoolYearService schoolYear)
|
||
{
|
||
_slots = slots; _groups = groups; _subjects = subjects; _lessons = lessons; _exams = exams;
|
||
_schoolHolidays = schoolHolidays; _calendarSettings = calendarSettings;
|
||
_publicHolidays = publicHolidays; _schoolYear = schoolYear;
|
||
Load();
|
||
}
|
||
|
||
partial void OnWeekOffsetChanged(int value) => OnPropertyChanged(nameof(IsCurrentWeek));
|
||
|
||
[RelayCommand]
|
||
private void PreviousWeek() { WeekOffset--; Load(); }
|
||
|
||
[RelayCommand]
|
||
private void NextWeek() { WeekOffset++; Load(); }
|
||
|
||
[RelayCommand]
|
||
private void CurrentWeek() { WeekOffset = 0; Load(); }
|
||
|
||
public void Load()
|
||
{
|
||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||
TodayLabel = today.ToString("dddd, dd.MM.yyyy", System.Globalization.CultureInfo.GetCultureInfo("de-DE"));
|
||
|
||
var publicHolidayDates = new HashSet<DateOnly>();
|
||
foreach (var year in new[] { today.Year - 1, today.Year, today.Year + 1 })
|
||
foreach (var h in _publicHolidays.GetHolidays(year, _calendarSettings.State)) publicHolidayDates.Add(h.Date);
|
||
|
||
var holidayBadges = ComputeHolidayBadges(today, publicHolidayDates);
|
||
var examProximity = ComputeExamProximity(today, publicHolidayDates);
|
||
|
||
BuildGrid(holidayBadges);
|
||
BuildWeekOverview(today, publicHolidayDates);
|
||
BuildToday(today);
|
||
BuildHoursWarnings();
|
||
}
|
||
|
||
// ── "Heute": Tagesliste ──────────────────────────────────────────────────
|
||
|
||
private void BuildToday(DateOnly today)
|
||
{
|
||
TodayItems.Clear();
|
||
var slotsToday = _slots.GetAll().Where(s => s.Weekday == today.DayOfWeek).OrderBy(s => s.PeriodNumber).ToList();
|
||
|
||
foreach (var slot in slotsToday)
|
||
{
|
||
var group = _groups.GetById(slot.GroupId);
|
||
if (group is null) continue;
|
||
var lesson = _lessons.GetByGroupAndDate(slot.GroupId, today).FirstOrDefault();
|
||
var exam = _exams.GetByGroup(slot.GroupId).FirstOrDefault(e => e.Date == today);
|
||
TodayItems.Add(new TodayLessonItem(slot.GroupId, slot.PeriodNumber, group.Name,
|
||
slot.Room ?? "", ColorFor(group.Name), lesson?.Topic, exam?.Title));
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void OpenGroup(Guid groupId) => OnNavigateToGroup?.Invoke(groupId);
|
||
|
||
[RelayCommand]
|
||
private void ShowEditor() => ActiveTabIndex = 1;
|
||
|
||
// ── "Heute": Wochenraster (Nutzer-Feedback) — wie das Bearbeiten-Raster, aber nur Anzeige ──
|
||
|
||
/// <summary>
|
||
/// Zeigt Fach, Klasse, Raum und (falls für den Tag hinterlegt) das Thema der Stunde für die
|
||
/// per <see cref="WeekOffset"/> gewählte Kalenderwoche (Mo–Fr, wie das Bearbeiten-Raster) —
|
||
/// anders als die Tagesliste auch für Tage, die noch nicht "heute" sind, damit z.B. der
|
||
/// parallele Kurs oder die nächste Stunde in der Woche auf einen Blick sichtbar sind. Die
|
||
/// Badges (Ferien-Nähe, Klausur-Nähe) werden je Zelle am dort angezeigten Datum ausgerichtet,
|
||
/// nicht am realen "heute" — sonst würde beim Blättern in andere Wochen ein Badge angezeigt,
|
||
/// das eigentlich zu einer ganz anderen Woche gehört.
|
||
/// </summary>
|
||
private void BuildWeekOverview(DateOnly today, HashSet<DateOnly> publicHolidayDates)
|
||
{
|
||
WeekItems.Clear();
|
||
var currentWeekMonday = today.AddDays(-((int)today.DayOfWeek + 6) % 7);
|
||
var monday = currentWeekMonday.AddDays(WeekOffset * 7);
|
||
var friday = monday.AddDays(4);
|
||
WeekRangeLabel = $"{monday:dd.MM.} – {friday:dd.MM.yyyy}";
|
||
|
||
var allSlots = _slots.GetAll();
|
||
var groups = _groups.GetAll(includeInactive: true).ToDictionary(g => g.Id);
|
||
var schoolHolidays = _schoolHolidays.GetAll();
|
||
var dateByWeekday = Weekdays.ToDictionary(w => w, w => monday.AddDays((int)w - (int)DayOfWeek.Monday));
|
||
|
||
WeekItems.Add(WeekCellItem.Corner());
|
||
foreach (var weekday in Weekdays)
|
||
WeekItems.Add(WeekCellItem.WeekdayHeader(weekday, dateByWeekday[weekday], dateByWeekday[weekday] == today));
|
||
|
||
for (var period = FirstPeriod; period <= LastPeriod; period++)
|
||
{
|
||
WeekItems.Add(WeekCellItem.PeriodLabel(period));
|
||
foreach (var weekday in Weekdays)
|
||
{
|
||
var slot = allSlots.FirstOrDefault(s => s.Weekday == weekday && s.PeriodNumber == period);
|
||
if (slot is null) { WeekItems.Add(WeekCellItem.Empty(weekday, period)); continue; }
|
||
|
||
var date = dateByWeekday[weekday];
|
||
var group = groups.GetValueOrDefault(slot.GroupId);
|
||
var subject = group?.SubjectId is { } subjectId ? _subjects.GetById(subjectId) : null;
|
||
var lesson = _lessons.GetByGroupAndDate(slot.GroupId, date).FirstOrDefault();
|
||
var hasExam = _exams.GetByGroup(slot.GroupId).Any(e => e.Date == date);
|
||
var isHoliday = IsFreeDay(date, schoolHolidays, publicHolidayDates);
|
||
var colorHex = isHoliday ? "#BDBDBD" : ColorFor(group?.Name ?? "");
|
||
var holidayBadge = HolidayBadgeFor(date, weekday, schoolHolidays, publicHolidayDates);
|
||
var isLastBeforeExam = IsLastBeforeExamFor(date, weekday, slot.GroupId, publicHolidayDates);
|
||
|
||
WeekItems.Add(WeekCellItem.ForSlot(weekday, period, date == today,
|
||
subject?.ShortName is { Length: > 0 } sn ? sn : subject?.Name ?? "",
|
||
group?.Name ?? "?", slot.Room ?? "", lesson?.Topic ?? "",
|
||
colorHex, holidayBadge, hasExam, isLastBeforeExam,
|
||
MentionsExperiment(lesson), slot.GroupId, isHoliday));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>Badge "1"/"2" für die Zelle mit Datum <paramref name="date"/> selbst, analog zu
|
||
/// <see cref="ComputeHolidayBadges"/>, aber an diesem konkreten Datum statt an "heute"
|
||
/// ausgerichtet — nötig, damit das Badge beim Blättern durch die Wochen zur richtigen Woche
|
||
/// gehört.</summary>
|
||
private string HolidayBadgeFor(DateOnly date, DayOfWeek weekday, List<SchoolHoliday> schoolHolidays,
|
||
HashSet<DateOnly> publicHolidayDates)
|
||
{
|
||
var nextHoliday = schoolHolidays.Where(h => h.StartDate > date).MinBy(h => h.StartDate);
|
||
if (nextHoliday is null) return "";
|
||
var count = CountOccurrences(weekday, date, nextHoliday.StartDate, publicHolidayDates);
|
||
return count is 1 or 2 ? count.ToString() : "";
|
||
}
|
||
|
||
/// <summary>Analog zu <see cref="ComputeExamProximity"/>, aber am Zelldatum statt an "heute"
|
||
/// ausgerichtet.</summary>
|
||
private bool IsLastBeforeExamFor(DateOnly date, DayOfWeek weekday, Guid groupId, HashSet<DateOnly> publicHolidayDates)
|
||
{
|
||
var nextExam = _exams.GetByGroup(groupId).Where(e => e.Date >= date).MinBy(e => e.Date);
|
||
if (nextExam is null) return false;
|
||
return CountOccurrences(weekday, date, nextExam.Date, publicHolidayDates) == 1;
|
||
}
|
||
|
||
private static bool MentionsExperiment(Lesson? lesson) =>
|
||
lesson is not null && lesson.Phases.Any(p =>
|
||
p.Name.Contains("Experiment", StringComparison.OrdinalIgnoreCase) ||
|
||
p.Activity.Contains("Experiment", StringComparison.OrdinalIgnoreCase) ||
|
||
p.Material.Contains("Experiment", StringComparison.OrdinalIgnoreCase));
|
||
|
||
/// <summary>Fällt <paramref name="date"/> auf einen gesetzlichen Feiertag oder in Schulferien?</summary>
|
||
private static bool IsFreeDay(DateOnly date, List<SchoolHoliday> schoolHolidays, HashSet<DateOnly> publicHolidayDates) =>
|
||
publicHolidayDates.Contains(date) || schoolHolidays.Any(h => date >= h.StartDate && date <= h.EndDate);
|
||
|
||
// ── Bearbeiten-Raster ─────────────────────────────────────────────────────
|
||
|
||
private void BuildGrid(Dictionary<(DayOfWeek Weekday, Guid GroupId), string> badges)
|
||
{
|
||
Cells.Clear();
|
||
var allSlots = _slots.GetAll();
|
||
var groupNames = _groups.GetAll(includeInactive: true).ToDictionary(g => g.Id, g => g.Name);
|
||
|
||
Cells.Add(TimetableCellItem.Corner());
|
||
foreach (var weekday in Weekdays)
|
||
Cells.Add(TimetableCellItem.WeekdayHeader(weekday));
|
||
|
||
for (var period = FirstPeriod; period <= LastPeriod; period++)
|
||
{
|
||
Cells.Add(TimetableCellItem.PeriodLabel(period));
|
||
foreach (var weekday in Weekdays)
|
||
{
|
||
var slot = allSlots.FirstOrDefault(s => s.Weekday == weekday && s.PeriodNumber == period);
|
||
var groupName = slot is not null ? groupNames.GetValueOrDefault(slot.GroupId, "?") : "";
|
||
var badge = slot is not null ? badges.GetValueOrDefault((weekday, slot.GroupId), "") : "";
|
||
Cells.Add(TimetableCellItem.ForSlot(weekday, period, slot, groupName, ColorFor(groupName), badge));
|
||
}
|
||
}
|
||
}
|
||
|
||
private static string ColorFor(string name)
|
||
{
|
||
if (name.Length == 0) return "#9E9E9E";
|
||
var hash = 0;
|
||
foreach (var c in name) hash = hash * 31 + c;
|
||
return WeekdayColorPalette[Math.Abs(hash) % WeekdayColorPalette.Length];
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task EditCell(TimetableCellItem? cell)
|
||
{
|
||
if (cell is null || !cell.IsSlotCell || OnEditSlot is null) return;
|
||
await OnEditSlot(cell);
|
||
Load();
|
||
}
|
||
|
||
// ── Abgleich mit Wochenstunden (4.3.4) ────────────────────────────────────
|
||
|
||
private void BuildHoursWarnings()
|
||
{
|
||
HoursWarnings.Clear();
|
||
var currentYear = _schoolYear.CurrentSchoolYear();
|
||
var slotCounts = _slots.GetAll().GroupBy(s => s.GroupId).ToDictionary(g => g.Key, g => g.Count());
|
||
|
||
foreach (var group in _groups.GetBySchoolYear(currentYear).Where(g => g.HoursPerWeek.HasValue))
|
||
{
|
||
var assigned = slotCounts.GetValueOrDefault(group.Id, 0);
|
||
if (assigned != group.HoursPerWeek!.Value)
|
||
HoursWarnings.Add(new HoursWarningItem(group.Name, assigned, group.HoursPerWeek.Value));
|
||
}
|
||
}
|
||
|
||
// ── Badges: letzte/vorletzte Stunde vor Ferien, letzte Stunde vor Klausur ────────────────
|
||
|
||
/// <summary>
|
||
/// Zählt, wie oft <paramref name="weekday"/> zwischen (inkl.) <paramref name="from"/> und
|
||
/// (exkl.) <paramref name="until"/> eintritt — gesetzliche Feiertage werden übersprungen, da
|
||
/// an ihnen ohnehin kein Unterricht stattfindet.
|
||
/// </summary>
|
||
private static int CountOccurrences(DayOfWeek weekday, DateOnly from, DateOnly until, HashSet<DateOnly> publicHolidays)
|
||
{
|
||
var count = 0;
|
||
for (var date = from; date < until; date = date.AddDays(1))
|
||
if (date.DayOfWeek == weekday && !publicHolidays.Contains(date)) count++;
|
||
return count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Badge "1"/"2" für die letzte bzw. vorletzte Stunde eines Wochentags vor den nächsten
|
||
/// anstehenden Schulferien. Pro (Wochentag, Gruppe) statt pro einzelnem <see cref="TimetableSlot"/>
|
||
/// berechnet: eine Doppelstunde besteht aus zwei Slots mit demselben Wochentag/derselben
|
||
/// Gruppe und bekommt dadurch automatisch dasselbe Badge, ohne gesonderte Blockerkennung.
|
||
/// </summary>
|
||
private Dictionary<(DayOfWeek Weekday, Guid GroupId), string> ComputeHolidayBadges(
|
||
DateOnly today, HashSet<DateOnly> publicHolidayDates)
|
||
{
|
||
var result = new Dictionary<(DayOfWeek, Guid), string>();
|
||
var nextHoliday = _schoolHolidays.GetAll().Where(h => h.StartDate > today).MinBy(h => h.StartDate);
|
||
if (nextHoliday is null) return result;
|
||
|
||
foreach (var group in _slots.GetAll().GroupBy(s => (s.Weekday, s.GroupId)))
|
||
{
|
||
var count = CountOccurrences(group.Key.Weekday, today, nextHoliday.StartDate, publicHolidayDates);
|
||
if (count is 1 or 2) result[group.Key] = count.ToString();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Markiert die letzte Stunde eines Wochentags vor der nächsten anstehenden Klausur derselben
|
||
/// Gruppe (sofern eine ansteht) — analog zum Ferien-Badge, aber je Gruppe an deren eigenem
|
||
/// nächsten Klausurtermin statt an einem gemeinsamen Ferientermin ausgerichtet.
|
||
/// </summary>
|
||
private Dictionary<(DayOfWeek Weekday, Guid GroupId), bool> ComputeExamProximity(
|
||
DateOnly today, HashSet<DateOnly> publicHolidayDates)
|
||
{
|
||
var result = new Dictionary<(DayOfWeek, Guid), bool>();
|
||
foreach (var groupSlots in _slots.GetAll().GroupBy(s => s.GroupId))
|
||
{
|
||
var nextExam = _exams.GetByGroup(groupSlots.Key).Where(e => e.Date >= today).MinBy(e => e.Date);
|
||
if (nextExam is null) continue;
|
||
|
||
foreach (var weekday in groupSlots.Select(s => s.Weekday).Distinct())
|
||
{
|
||
var count = CountOccurrences(weekday, today, nextExam.Date, publicHolidayDates);
|
||
if (count == 1) result[(weekday, groupSlots.Key)] = true;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public class TimetableCellItem
|
||
{
|
||
public bool IsHeader { get; private init; }
|
||
public bool IsPeriodLabel { get; private init; }
|
||
public string Text { get; private init; } = "";
|
||
public DayOfWeek? Weekday { get; private init; }
|
||
public int PeriodNumber { get; private init; }
|
||
public TimetableSlot? Slot { get; private init; }
|
||
public string GroupName { get; private init; } = "";
|
||
public string ColorHex { get; private init; } = "#9E9E9E";
|
||
public string BadgeText { get; private init; } = "";
|
||
public bool HasBadge => BadgeText.Length > 0;
|
||
public bool IsAssigned => Slot is not null;
|
||
public bool IsSlotCell => !IsHeader && !IsPeriodLabel;
|
||
|
||
public static TimetableCellItem Corner() => new() { IsHeader = true, Text = "" };
|
||
|
||
public static TimetableCellItem WeekdayHeader(DayOfWeek day) => new()
|
||
{
|
||
IsHeader = true,
|
||
Text = day.ToString() switch
|
||
{
|
||
"Monday" => "Mo", "Tuesday" => "Di", "Wednesday" => "Mi",
|
||
"Thursday" => "Do", "Friday" => "Fr", _ => day.ToString(),
|
||
},
|
||
};
|
||
|
||
public static TimetableCellItem PeriodLabel(int period) => new() { IsPeriodLabel = true, Text = period.ToString() };
|
||
|
||
public static TimetableCellItem ForSlot(DayOfWeek day, int period, TimetableSlot? slot, string groupName,
|
||
string colorHex, string badgeText) => new()
|
||
{
|
||
Weekday = day, PeriodNumber = period, Slot = slot, GroupName = groupName, ColorHex = colorHex,
|
||
BadgeText = badgeText,
|
||
Text = slot is null ? "" : groupName + (string.IsNullOrWhiteSpace(slot.Room) ? "" : $" · {slot.Room}"),
|
||
};
|
||
}
|
||
|
||
/// <summary>Zelle im schreibgeschützten Wochenraster der "Heute"-Ansicht.</summary>
|
||
public class WeekCellItem
|
||
{
|
||
public bool IsHeader { get; private init; }
|
||
public bool IsPeriodLabel { get; private init; }
|
||
public bool IsSlotCell => !IsHeader && !IsPeriodLabel;
|
||
public bool IsAssigned { get; private init; }
|
||
public string Text { get; private init; } = "";
|
||
public DayOfWeek? Weekday { get; private init; }
|
||
public int PeriodNumber { get; private init; }
|
||
public bool IsToday { get; private init; }
|
||
public Guid GroupId { get; private init; }
|
||
public string SubjectLabel { get; private init; } = "";
|
||
public string GroupName { get; private init; } = "";
|
||
public string Room { get; private init; } = "";
|
||
public string Topic { get; private init; } = "";
|
||
public string ColorHex { get; private init; } = "#9E9E9E";
|
||
public string HolidayBadge { get; private init; } = "";
|
||
public bool HasHolidayBadge => HolidayBadge.Length > 0;
|
||
public bool HasExam { get; private init; }
|
||
public bool IsLastBeforeExam { get; private init; }
|
||
public bool HasExperiment { get; private init; }
|
||
public bool IsHoliday { get; private init; }
|
||
public bool HasRoom => !string.IsNullOrWhiteSpace(Room);
|
||
public bool HasTopic => !string.IsNullOrWhiteSpace(Topic);
|
||
|
||
public static WeekCellItem Corner() => new() { IsHeader = true };
|
||
|
||
public static WeekCellItem WeekdayHeader(DayOfWeek day, DateOnly date, bool isToday) => new()
|
||
{
|
||
IsHeader = true,
|
||
IsToday = isToday,
|
||
Text = (day switch
|
||
{
|
||
DayOfWeek.Monday => "Mo", DayOfWeek.Tuesday => "Di", DayOfWeek.Wednesday => "Mi",
|
||
DayOfWeek.Thursday => "Do", DayOfWeek.Friday => "Fr", _ => day.ToString(),
|
||
}) + $" {date:dd.MM.}",
|
||
};
|
||
|
||
public static WeekCellItem PeriodLabel(int period) => new() { IsPeriodLabel = true, Text = period.ToString() };
|
||
|
||
public static WeekCellItem Empty(DayOfWeek day, int period) => new() { Weekday = day, PeriodNumber = period };
|
||
|
||
public static WeekCellItem ForSlot(DayOfWeek day, int period, bool isToday, string subjectLabel,
|
||
string groupName, string room, string topic, string colorHex, string holidayBadge,
|
||
bool hasExam, bool isLastBeforeExam, bool hasExperiment, Guid groupId, bool isHoliday) => new()
|
||
{
|
||
Weekday = day, PeriodNumber = period, IsAssigned = true, IsToday = isToday,
|
||
SubjectLabel = subjectLabel, GroupName = groupName, Room = room, Topic = topic,
|
||
ColorHex = colorHex, HolidayBadge = holidayBadge, HasExam = hasExam,
|
||
IsLastBeforeExam = isLastBeforeExam, HasExperiment = hasExperiment, GroupId = groupId,
|
||
IsHoliday = isHoliday,
|
||
};
|
||
}
|
||
|
||
public class HoursWarningItem(string groupName, int assigned, int expected)
|
||
{
|
||
public string GroupName { get; } = groupName;
|
||
public string Text { get; } = $"{groupName}: {assigned} von {expected} Wochenstunden eingetragen";
|
||
}
|
||
|
||
public class TodayLessonItem(Guid groupId, int periodNumber, string groupName, string room,
|
||
string colorHex, string? lessonTopic, string? examTitle)
|
||
{
|
||
public Guid GroupId { get; } = groupId;
|
||
public int PeriodNumber { get; } = periodNumber;
|
||
public string GroupName { get; } = groupName;
|
||
public string Room { get; } = room;
|
||
public string ColorHex { get; } = colorHex;
|
||
public string? LessonTopic { get; } = lessonTopic;
|
||
public string? ExamTitle { get; } = examTitle;
|
||
public bool HasRoom => !string.IsNullOrWhiteSpace(Room);
|
||
public bool HasLessonTopic => !string.IsNullOrWhiteSpace(LessonTopic);
|
||
public bool HasExam => ExamTitle is not null;
|
||
}
|