Stundenplan: Dropdown für Unterrichtsansicht/Sitzplan/Planung/Planungsviewer
Die zwei Buttons in der "Heute"-Tagesliste mit teils vom Lesson-Status
abhängiger Doppelbedeutung ("Verlaufsplan ansehen"/"Zur Lerngruppe") waren
unklar. Ersetzt durch ein Dropdown mit vier ausdrücklich benannten Zielen;
drei nutzen bestehende Commands, "Sitzplan" (Sitzpläne-Tab) ist neu und
bisher nur indirekt über den Unterrichtsmodus erreichbar gewesen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1177,3 +1177,45 @@ public sealed class TimetableViewModelTests
|
|||||||
Assert.False(vm.HasUntisMismatch);
|
Assert.False(vm.HasUntisMismatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nutzer-Feedback: unklar, wie man aus der "Heute"-Tagesliste zwischen Unterrichtsansicht,
|
||||||
|
/// Sitzplan, Planung und Planungsviewer wechselt — TodayLessonItem.DestinationOptions ersetzt die
|
||||||
|
/// bisherigen zwei Buttons durch ein Dropdown mit ausdrücklich benannten Zielen.
|
||||||
|
public sealed class TodayLessonItemDestinationOptionsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void MitLesson_BietetAlleVierZiele()
|
||||||
|
{
|
||||||
|
var lesson = new Lesson { Topic = "Redox" };
|
||||||
|
var item = new TodayLessonItem(Guid.NewGuid(), 3, "Q1 Chemie", "R204",
|
||||||
|
"#4C8DFF", "Redox", null, lesson: lesson);
|
||||||
|
|
||||||
|
Assert.True(item.HasDestinationOptions);
|
||||||
|
Assert.Equal(
|
||||||
|
[
|
||||||
|
TimetableLessonDestination.TeachingMode, TimetableLessonDestination.Viewer,
|
||||||
|
TimetableLessonDestination.SeatingPlan, TimetableLessonDestination.Planning,
|
||||||
|
],
|
||||||
|
item.DestinationOptions.Select(o => o.Kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OhneLesson_BietetNurSitzplanUndPlanung()
|
||||||
|
{
|
||||||
|
var item = new TodayLessonItem(Guid.NewGuid(), 3, "Q1 Chemie", "R204",
|
||||||
|
"#4C8DFF", null, null);
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
[TimetableLessonDestination.SeatingPlan, TimetableLessonDestination.Planning],
|
||||||
|
item.DestinationOptions.Select(o => o.Kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OhneGroupId_BietetKeineZiele()
|
||||||
|
{
|
||||||
|
var item = TodayLessonItem.ForSubstitution(3, new SubstitutionEntry { GroupId = null, Description = "Vertretung" });
|
||||||
|
|
||||||
|
Assert.False(item.HasDestinationOptions);
|
||||||
|
Assert.Empty(item.DestinationOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -900,6 +900,9 @@ public class UpcomingExamItem(DateOnly date, string groupName, string title)
|
|||||||
public string Title { get; } = title;
|
public string Title { get; } = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TimetableLessonDestination { TeachingMode, Viewer, SeatingPlan, Planning }
|
||||||
|
public sealed record TimetableDestinationOption(TimetableLessonDestination Kind, string Label);
|
||||||
|
|
||||||
public class TodayLessonItem
|
public class TodayLessonItem
|
||||||
{
|
{
|
||||||
public Guid GroupId { get; private init; }
|
public Guid GroupId { get; private init; }
|
||||||
@@ -922,6 +925,33 @@ public class TodayLessonItem
|
|||||||
public bool HasLesson => Lesson is not null;
|
public bool HasLesson => Lesson is not null;
|
||||||
public string OpenButtonLabel => Lesson is not null ? "Verlaufsplan ansehen" : "Zur Lerngruppe";
|
public string OpenButtonLabel => Lesson is not null ? "Verlaufsplan ansehen" : "Zur Lerngruppe";
|
||||||
|
|
||||||
|
/// Nutzer-Feedback: es war unklar, wie man aus der "Heute"-Tagesliste zwischen
|
||||||
|
/// Unterrichtsansicht, Sitzplan, Planung und Planungsviewer wechselt — bisher zwei Buttons
|
||||||
|
/// mit unterschiedlicher, teils vom Lesson-Status abhängiger Bedeutung
|
||||||
|
/// ("Verlaufsplan ansehen"/"Zur Lerngruppe"). Ein Dropdown mit ausdrücklich benannten Zielen
|
||||||
|
/// statt dessen (Wiring/Routing in TimetableView.axaml.cs). Unterrichtsansicht/Planungsviewer
|
||||||
|
/// brauchen eine existierende Lesson, Sitzplan/Planung sind auch ohne bereits möglich (Planung
|
||||||
|
/// ist ohnehin der Ort, an dem man eine Lesson für den Slot erst anlegt).
|
||||||
|
public IReadOnlyList<TimetableDestinationOption> DestinationOptions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var options = new List<TimetableDestinationOption>();
|
||||||
|
if (HasLesson)
|
||||||
|
{
|
||||||
|
options.Add(new(TimetableLessonDestination.TeachingMode, "▶ Unterrichtsansicht"));
|
||||||
|
options.Add(new(TimetableLessonDestination.Viewer, "📋 Planungsviewer"));
|
||||||
|
}
|
||||||
|
if (HasGroupId)
|
||||||
|
{
|
||||||
|
options.Add(new(TimetableLessonDestination.SeatingPlan, "🪑 Sitzplan"));
|
||||||
|
options.Add(new(TimetableLessonDestination.Planning, "✏️ Planung"));
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool HasDestinationOptions => DestinationOptions.Count > 0;
|
||||||
|
|
||||||
public TodayLessonItem(Guid groupId, int periodNumber, string groupName, string room,
|
public TodayLessonItem(Guid groupId, int periodNumber, string groupName, string room,
|
||||||
string colorHex, string? lessonTopic, string? examTitle, bool hasUnhandledHomework = false,
|
string colorHex, string? lessonTopic, string? examTitle, bool hasUnhandledHomework = false,
|
||||||
Lesson? lesson = null)
|
Lesson? lesson = null)
|
||||||
|
|||||||
@@ -111,17 +111,24 @@
|
|||||||
Foreground="#8E6C00" FontWeight="SemiBold"
|
Foreground="#8E6C00" FontWeight="SemiBold"
|
||||||
IsVisible="{Binding HasUnhandledHomework}"/>
|
IsVisible="{Binding HasUnhandledHomework}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="6" VerticalAlignment="Center">
|
<!-- Nutzer-Feedback: unklar, wie man aus der Tagesliste zwischen
|
||||||
<Button Content="▶ Unterricht" FontSize="11" Padding="9,4"
|
Unterrichtsansicht/Sitzplan/Planung/Planungsviewer wechselt — jetzt
|
||||||
IsVisible="{Binding HasLesson}"
|
ein Dropdown mit ausdrücklich benannten Zielen statt zweier Buttons
|
||||||
ToolTip.Tip="Unterrichtsmodus: Verlaufsplan und Sitzplan mit Schnellbewertung auf einem Bildschirm."
|
mit vom Lesson-Status abhängiger Doppelbedeutung. Wiring/Routing in
|
||||||
Command="{Binding $parent[ItemsControl].((vm:TimetableViewModel)DataContext).StartTeachingModeCommand}"
|
TimetableView.axaml.cs (SelectionChanged setzt die Auswahl danach
|
||||||
CommandParameter="{Binding}"/>
|
bewusst zurück auf null — Menü-Charakter, keine dauerhafte
|
||||||
<Button Content="{Binding OpenButtonLabel}" FontSize="11" Padding="9,4"
|
Auswahl). -->
|
||||||
IsVisible="{Binding HasGroupId}"
|
<ComboBox Grid.Column="3" FontSize="11" MinWidth="150" VerticalAlignment="Center"
|
||||||
Command="{Binding $parent[ItemsControl].((vm:TimetableViewModel)DataContext).OpenTodayLessonCommand}"
|
IsVisible="{Binding HasDestinationOptions}"
|
||||||
CommandParameter="{Binding}"/>
|
PlaceholderText="Öffnen ▾"
|
||||||
</StackPanel>
|
ItemsSource="{Binding DestinationOptions}"
|
||||||
|
SelectionChanged="OnLessonDestinationSelected">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:TimetableDestinationOption">
|
||||||
|
<TextBlock Text="{Binding Label}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Avalonia.Controls;
|
|||||||
using LehrerApp.Core.Interfaces;
|
using LehrerApp.Core.Interfaces;
|
||||||
using LehrerApp.Core.Models;
|
using LehrerApp.Core.Models;
|
||||||
using LehrerApp.Core.Services;
|
using LehrerApp.Core.Services;
|
||||||
|
using LehrerApp.Desktop.ViewModels;
|
||||||
using LehrerApp.Desktop.ViewModels.Planning;
|
using LehrerApp.Desktop.ViewModels.Planning;
|
||||||
using LehrerApp.Desktop.ViewModels.Groups;
|
using LehrerApp.Desktop.ViewModels.Groups;
|
||||||
using LehrerApp.Desktop.Views.Groups;
|
using LehrerApp.Desktop.Views.Groups;
|
||||||
@@ -27,6 +28,39 @@ public partial class TimetableView : UserControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nutzer-Feedback: unklar, wie man aus der "Heute"-Tagesliste zwischen Unterrichtsansicht,
|
||||||
|
/// Sitzplan, Planung und Planungsviewer wechselt. Statt der bisherigen zwei Buttons mit
|
||||||
|
/// teils vom Lesson-Status abhängiger Doppelbedeutung ein Dropdown mit ausdrücklich benannten
|
||||||
|
/// Zielen (TodayLessonItem.DestinationOptions) — Menü-Charakter, die Auswahl wird danach
|
||||||
|
/// bewusst zurückgesetzt statt dauerhaft angezeigt. Drei der vier Ziele nutzen unverändert
|
||||||
|
/// die bestehenden TimetableViewModel-Commands, nur "Sitzplan" ist neu (bisher nur über den
|
||||||
|
/// Unterrichtsmodus erreichbar, siehe TODO.md 4.5.23-Nachtrag).
|
||||||
|
private void OnLessonDestinationSelected(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not ComboBox comboBox) return;
|
||||||
|
var item = comboBox.DataContext as TodayLessonItem;
|
||||||
|
var option = e.AddedItems.Count > 0 ? e.AddedItems[0] as TimetableDestinationOption : null;
|
||||||
|
comboBox.SelectedItem = null;
|
||||||
|
if (item is null || option is null || DataContext is not TimetableViewModel vm) return;
|
||||||
|
|
||||||
|
switch (option.Kind)
|
||||||
|
{
|
||||||
|
case TimetableLessonDestination.TeachingMode:
|
||||||
|
vm.StartTeachingModeCommand.Execute(item);
|
||||||
|
break;
|
||||||
|
case TimetableLessonDestination.Viewer:
|
||||||
|
vm.OpenTodayLessonCommand.Execute(item);
|
||||||
|
break;
|
||||||
|
case TimetableLessonDestination.Planning:
|
||||||
|
vm.OpenGroupCommand.Execute(item.GroupId);
|
||||||
|
break;
|
||||||
|
case TimetableLessonDestination.SeatingPlan:
|
||||||
|
App.Services.GetRequiredService<MainWindowViewModel>()
|
||||||
|
.NavigateToGroupDetail(item.GroupId, 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ShowWebUntisTimetableDialog()
|
private async Task ShowWebUntisTimetableDialog()
|
||||||
{
|
{
|
||||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||||
|
|||||||
@@ -1918,6 +1918,18 @@ folgenden Punkte gehören direkt in `LehrerApp.Desktop`:
|
|||||||
den vollen Verlaufsplan-Editor öffnen müssen. Neue kleine
|
den vollen Verlaufsplan-Editor öffnen müssen. Neue kleine
|
||||||
`TeachingModeHomeworkViewModel : ObservableObject`, da `TeachingModeViewModel` selbst (wie
|
`TeachingModeHomeworkViewModel : ObservableObject`, da `TeachingModeViewModel` selbst (wie
|
||||||
`LessonViewerViewModel`) keine Bindable-Basisklasse hat.
|
`LessonViewerViewModel`) keine Bindable-Basisklasse hat.
|
||||||
|
- [x] **4.5.24** Dropdown in der "Heute"-Tagesliste für Unterrichtsansicht/Sitzplan/Planung/
|
||||||
|
Planungsviewer (August 2026, Nutzer-Feedback): unklar, wie man aus dem Stundenplan zwischen
|
||||||
|
diesen vier Ansichten wechselt — bisher zwei Buttons mit teils vom Lesson-Status abhängiger
|
||||||
|
Doppelbedeutung ("Verlaufsplan ansehen"/"Zur Lerngruppe"). Ersetzt durch eine `ComboBox` pro
|
||||||
|
Zeile mit vier ausdrücklich benannten Zielen (`TodayLessonItem.DestinationOptions`,
|
||||||
|
`TimetableLessonDestination`-Enum) — Unterrichtsansicht/Planungsviewer nur wenn für den Slot
|
||||||
|
schon eine `Lesson` existiert, Sitzplan/Planung immer. Menü-Charakter: `SelectionChanged` in
|
||||||
|
`TimetableView.axaml.cs` setzt `SelectedItem` nach jeder Auswahl bewusst auf `null` zurück,
|
||||||
|
statt die Auswahl dauerhaft anzuzeigen. Drei der vier Ziele nutzen unverändert bestehende
|
||||||
|
`TimetableViewModel`-Commands (`StartTeachingModeCommand`/`OpenTodayLessonCommand`/
|
||||||
|
`OpenGroupCommand` — letzterer navigiert bereits zum Planung-Tab, Index 6); "Sitzplan" ist
|
||||||
|
neu (Sitzpläne-Tab, Index 2 — bisher nur indirekt über den Unterrichtsmodus erreichbar).
|
||||||
|
|
||||||
**Wichtige Abweichung von der ursprünglichen Planung (5.2):** Vor der Umsetzung zeigte sich,
|
**Wichtige Abweichung von der ursprünglichen Planung (5.2):** Vor der Umsetzung zeigte sich,
|
||||||
dass 5.2 wie ursprünglich beschrieben eine zweite, parallele Fehlzeiten-Erfassung neben dem
|
dass 5.2 wie ursprünglich beschrieben eine zweite, parallele Fehlzeiten-Erfassung neben dem
|
||||||
|
|||||||
Reference in New Issue
Block a user