Dashboard 9.1/9.2: Uhrzeit/Raum bei heutigen Stunden + Absprung zur Mitarbeit

TodaysLessons löst je Stunde den Raum über den passenden TimetableSlot auf
und die Uhrzeit aus Lesson.StartTime bzw. dem Stundenraster - gleiche
Quellen wie im Verlaufsplan-Editor und Stundenplan. Bewusst nicht dupliziert:
die Vertretung/Ausfall-Logik der Stundenplan-eigenen "Heute"-Ansicht bleibt
dort, das Dashboard zeigt nur die einfache geplante Stunde.

Klick auf eine Stunde springt in die Mitarbeitserfassung der Gruppe -
bewusst anderes Ziel als der bestehende Stundenplan-Sprung (dort "Planung"),
da vom Dashboard aus morgens eher die Mitarbeitserfassung naheliegt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 23:54:52 +02:00
co-authored by Claude Sonnet 5
parent e40585801c
commit f8dd567ef8
5 changed files with 188 additions and 17 deletions
+1
View File
@@ -99,6 +99,7 @@ public class App : Application
var dash = Services.GetRequiredService<DashboardViewModel>();
dash.OnNavigateToGroup = id => main.NavigateToGroupDetail(id);
dash.OnNavigateToStudent = id => main.NavigateToStudent(id);
dash.OnNavigateToLesson = id => main.NavigateToGroupDetail(id, 2); // Tab "Mitarbeit"
// StudentList → StudentDetail + Anlegen
var sl = Services.GetRequiredService<StudentListViewModel>();
@@ -21,6 +21,8 @@ public partial class DashboardViewModel : ObservableObject
private readonly IParticipationRepository _participationEntries;
private readonly IStudentRepository _students;
private readonly IDocumentationRepository _documentation;
private readonly ITimetableSlotRepository _timetableSlots;
private readonly PeriodScheduleService _periodSchedule;
private readonly AttendanceBalanceService _attendanceBalance;
private readonly SchoolYearService _sy;
@@ -46,15 +48,22 @@ public partial class DashboardViewModel : ObservableObject
// Navigation-Callback wird von App.axaml.cs verdrahtet
public Action<Guid>? OnNavigateToGroup { get; set; }
public Action<Guid>? OnNavigateToStudent { get; set; }
// Sprungziel eigens für eine Stunde in TodaysLessons (9.2) — bewusst getrennt von
// OnNavigateToGroup (Lerngruppen-Kacheln, Tab "Übersicht"), da der Sprung von einer konkreten
// Stunde aus sinnvollerweise direkt in die Mitarbeitserfassung führt.
public Action<Guid>? OnNavigateToLesson { get; set; }
public DashboardViewModel(IGroupRepository groups, ISubjectRepository subjects, ILessonRepository lessons,
IExamRepository exams, IWorkTaskRepository tasks, IParticipationSessionRepository participationSessions,
IParticipationRepository participationEntries, IStudentRepository students,
IDocumentationRepository documentation, AttendanceBalanceService attendanceBalance, SchoolYearService sy)
IDocumentationRepository documentation, ITimetableSlotRepository timetableSlots,
PeriodScheduleService periodSchedule, AttendanceBalanceService attendanceBalance, SchoolYearService sy)
{
_groups = groups; _subjects = subjects; _lessons = lessons; _exams = exams; _tasks = tasks;
_participationSessions = participationSessions; _participationEntries = participationEntries;
_students = students; _documentation = documentation; _attendanceBalance = attendanceBalance; _sy = sy;
_students = students; _documentation = documentation;
_timetableSlots = timetableSlots; _periodSchedule = periodSchedule;
_attendanceBalance = attendanceBalance; _sy = sy;
Load();
}
@@ -73,8 +82,23 @@ public partial class DashboardViewModel : ObservableObject
foreach (var l in groups.Keys.SelectMany(gid => _lessons.GetByGroupAndDate(gid, today))
.OrderBy(l => l.LessonNumber))
{
if (groups.TryGetValue(l.GroupId, out var g))
TodaysLessons.Add(new() { GroupName = g.Name, Topic = l.Topic });
if (!groups.TryGetValue(l.GroupId, out var g)) continue;
// Raum kommt aus dem Stundenplan-Slot (4.3), sofern die Stunde eine Stundennummer hat
// und dafür ein Slot am heutigen Wochentag existiert — Vertretungen/Ausfälle werden
// hier bewusst NICHT berücksichtigt (das leistet bereits die "Heute"-Ansicht im
// Stundenplan selbst, eine Dopplung dieser Logik wäre hier nicht sinnvoll).
var slot = l.LessonNumber is int period
? _timetableSlots.GetByGroup(l.GroupId).FirstOrDefault(s => s.Weekday == today.DayOfWeek && s.PeriodNumber == period)
: null;
var timeDisplay = l.StartTime is { } st ? st.ToString("HH:mm")
: l.LessonNumber is int p2 && _periodSchedule.GetTimes(p2) is { } t ? t.Start.ToString("HH:mm") : "";
TodaysLessons.Add(new()
{
LessonId = l.Id, GroupId = l.GroupId, GroupName = g.Name, Topic = l.Topic,
TimeDisplay = timeDisplay, Room = slot?.Room ?? "",
});
}
OpenTasks.Clear();
@@ -264,6 +288,7 @@ public partial class DashboardViewModel : ObservableObject
}
[RelayCommand] private void OpenGroup(GroupChip? c) { if (c is not null) OnNavigateToGroup?.Invoke(c.GroupId); }
[RelayCommand] private void OpenLesson(LessonItem? l) { if (l is not null) OnNavigateToLesson?.Invoke(l.GroupId); }
[RelayCommand] private void Refresh() => Load();
private class DayAgg
@@ -275,7 +300,16 @@ public partial class DashboardViewModel : ObservableObject
}
}
public class LessonItem { public string GroupName { get; set; } = ""; public string Topic { get; set; } = ""; }
public class LessonItem
{
public Guid LessonId { get; set; }
public Guid GroupId { get; set; }
public string GroupName { get; set; } = "";
public string Topic { get; set; } = "";
public string TimeDisplay { get; set; } = "";
public string Room { get; set; } = "";
public bool HasRoom => !string.IsNullOrWhiteSpace(Room);
}
public class TaskItem { public string Title { get; set; } = ""; public string DueDate { get; set; } = ""; public bool IsOverdue { get; set; } }
public class GroupChip { public Guid GroupId { get; set; } public string Name { get; set; } = ""; public string Subject { get; set; } = ""; }
@@ -32,16 +32,28 @@
<ItemsControl ItemsSource="{Binding TodaysLessons}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:LessonItem">
<Grid ColumnDefinitions="4,*" Margin="0,4">
<Border Grid.Column="0" Width="4" CornerRadius="2"
Background="{DynamicResource SystemAccentColor}"
Margin="0,0,10,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding GroupName}" FontWeight="SemiBold" FontSize="13"/>
<TextBlock Text="{Binding Topic}" FontSize="12" Opacity="0.7"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Grid>
<Button Command="{Binding $parent[ItemsControl].((vm:DashboardViewModel)DataContext).OpenLessonCommand}"
CommandParameter="{Binding}"
Background="Transparent" BorderThickness="0" Padding="0" Margin="0,4"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Grid ColumnDefinitions="4,*,Auto">
<Border Grid.Column="0" Width="4" CornerRadius="2"
Background="{DynamicResource SystemAccentColor}"
Margin="0,0,10,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding GroupName}" FontWeight="SemiBold" FontSize="13"/>
<TextBlock Text="{Binding Topic}" FontSize="12" Opacity="0.7"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
<StackPanel Grid.Column="2" HorizontalAlignment="Right" Margin="8,0,0,0">
<TextBlock Text="{Binding TimeDisplay}" FontSize="12" Opacity="0.7"
HorizontalAlignment="Right"
IsVisible="{Binding TimeDisplay, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Text="{Binding Room}" FontSize="11" Opacity="0.6"
HorizontalAlignment="Right" IsVisible="{Binding HasRoom}"/>
</StackPanel>
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>