Vernetzung Stundenplan ↔ Unterrichtsplanung: Terminvorschlag, Lesson-Viewer-Sprung, Verzweigung (4.5.1–4.5.3)

4.5.1: Terminvorschlag für neue Stunden schlägt jetzt auch die Stundennummer
vor (bei Doppelstunden die erste Periode), nicht nur den Wochentag —
zieht über den bestehenden LessonNumber-Changed-Hook automatisch auch den
Stundenbeginn nach.

4.5.2 (teilweise): Klick auf eine Stunde im Stundenplan ("Heute"-Liste und
read-only Wochenraster) springt bei bereits vorhandener Lesson direkt in
den Verlaufsplan-Viewer statt nur grob zum Planung-Tab. Das Anlegen einer
neuen Lesson direkt aus dem Stundenplan bleibt bewusst offen (ungeklärte
Unit-Zuordnung, siehe TODO.md).

4.5.3: Der Lesson-Viewer hat jetzt Buttons "Zur Mitarbeit"/"Zu den Noten"
zum Weiterverzweigen in die Lerngruppe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 23:39:27 +02:00
co-authored by Claude Sonnet 5
parent 52a1d45539
commit 391628ae36
10 changed files with 280 additions and 31 deletions
@@ -749,31 +749,39 @@ public partial class LessonDialogViewModel : ObservableObject
}
else
{
DateText = SuggestNextLessonDate().ToString("dd.MM.yyyy");
var (date, lessonNumber) = SuggestNextLesson();
DateText = date.ToString("dd.MM.yyyy");
LessonNumber = lessonNumber;
}
RecomputeTimes();
}
/// <summary>
/// Vorbelegung für eine neue Stunde: statt des sonst über den Feld-Default eingesetzten
/// heutigen Datums (das an einem beliebigen Wochentag steht und die Doppelstunden-Erkennung
/// in <see cref="RecomputeTimeBudget"/> stillschweigend auf eine Einzelperiode zurückfallen
/// lässt, wenn der Wochentag nicht zufällig passt) der nächste laut Stundenplan (4.3) für
/// diese Gruppe passende Wochentag — ab der letzten Stunde dieser Einheit, oder ab heute, wenn
/// noch keine Stunde in dieser Einheit existiert oder die letzte in der Vergangenheit liegt.
/// Ohne Stundenplan-Einträge für die Gruppe (z.B. Klassenrat) bleibt es beim heutigen Datum.
/// Terminvorschlag für eine neue Stunde (4.5.1): statt des sonst über den Feld-Default
/// eingesetzten heutigen Datums (das an einem beliebigen Wochentag steht und die
/// Doppelstunden-Erkennung in <see cref="RecomputeTimeBudget"/> stillschweigend auf eine
/// Einzelperiode zurückfallen lässt, wenn der Wochentag nicht zufällig passt) der nächste laut
/// Stundenplan (4.3) für diese Gruppe passende Wochentag — ab der letzten Stunde dieser
/// Einheit, oder ab heute, wenn noch keine Stunde in dieser Einheit existiert oder die letzte
/// in der Vergangenheit liegt. Die Stundennummer wird passend dazu aus dem frühesten
/// `TimetableSlot` dieses Wochentags vorbelegt (bei einer Doppelstunde also die erste Periode,
/// dieselbe Ankerkonvention wie bei der Serienerzeugung, 4.2.5). Ohne Stundenplan-Einträge für
/// die Gruppe (z.B. Klassenrat) bleibt es beim heutigen Datum ohne Stundenvorschlag.
/// </summary>
private DateOnly SuggestNextLessonDate()
private (DateOnly Date, int? LessonNumber) SuggestNextLesson()
{
var weekdays = _timetableSlots.GetByGroup(_groupId).Select(s => s.Weekday).ToHashSet();
var slots = _timetableSlots.GetByGroup(_groupId);
var today = DateOnly.FromDateTime(DateTime.Today);
if (weekdays.Count == 0) return today;
if (slots.Count == 0) return (today, null);
var weekdays = slots.Select(s => s.Weekday).ToHashSet();
var lastLessonDate = _lessons.GetByUnit(_unitId).Select(l => l.Date)
.DefaultIfEmpty(today.AddDays(-1)).Max();
var candidate = lastLessonDate >= today ? lastLessonDate.AddDays(1) : today;
while (!weekdays.Contains(candidate.DayOfWeek)) candidate = candidate.AddDays(1);
return candidate;
var lessonNumber = slots.Where(s => s.Weekday == candidate.DayOfWeek).Min(s => (int?)s.PeriodNumber);
return (candidate, lessonNumber);
}
[RelayCommand]
@@ -1432,10 +1440,11 @@ public record PhaseViewItem(string Name, int DurationMinutes, string TimeDisplay
/// von Stundenbeginn an nimmt", nicht ab einer gemeinsamen Verzweigungsstelle im Hauptweg.
public record PhaseGroupViewItem(string Label, bool IsMainPath, string? Description, List<PhaseViewItem> Phases);
public class LessonViewerViewModel
public partial class LessonViewerViewModel : ObservableObject
{
private const string MainPathLabel = "Hauptweg";
public Guid GroupId { get; }
public string DateDisplay { get; }
public string Topic { get; }
public string StatusLabel { get; }
@@ -1445,8 +1454,16 @@ public class LessonViewerViewModel
public List<PhaseGroupViewItem> PhaseGroups { get; }
public bool HasAlternatives { get; }
/// Verzweigung aus dem Viewer heraus (4.5.3): Ziel-Tab-Index von GroupDetailView.axaml
/// (3 Mitarbeit, 5 Noten) — das Code-Behind schließt den Viewer und navigiert dorthin.
public Action<int>? OnNavigateToTab { get; set; }
[RelayCommand] private void NavigateToParticipation() => OnNavigateToTab?.Invoke(3);
[RelayCommand] private void NavigateToGrades() => OnNavigateToTab?.Invoke(5);
public LessonViewerViewModel(Lesson lesson, IAlternativeLessonPathRepository alternativePaths)
{
GroupId = lesson.GroupId;
DateDisplay = lesson.Date.ToString("dd.MM.yyyy");
Topic = lesson.Topic;
StatusLabel = LessonStatusDisplay.ToName(lesson.Status);