Stunde/Einheit-Dialog: Gruppe + Fach anzeigen, nicht nur Fach

Bei einer Klasse, die in mehreren Fächern unterrichtet wird (mehrere
LearningGroup-Datensätze mit gleichem Namen, z.B. zwei "10c"), zeigte
UnitDialog bisher nur das Fach ("Fach: Chemie"), LessonDialog gar keinen
Gruppen-/Fach-Hinweis. Beide zeigen jetzt "Gruppe · Fach" (z.B. "10c ·
Chemie") direkt unter dem Dialogtitel, nicht editierbar, nur zur Einordnung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 00:47:07 +02:00
co-authored by Claude Sonnet 5
parent 888df39f37
commit fbf70df439
7 changed files with 77 additions and 14 deletions
@@ -19,7 +19,17 @@ public sealed class LessonDialogViewModelTests
FakeTimetableSlots? slots = null, PeriodScheduleService? periodSchedule = null) => FakeTimetableSlots? slots = null, PeriodScheduleService? periodSchedule = null) =>
new(new FakeLessons(), new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]), new(new FakeLessons(), new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
slots ?? new FakeTimetableSlots(), periodSchedule ?? NewPeriodSchedule(), slots ?? new FakeTimetableSlots(), periodSchedule ?? NewPeriodSchedule(),
unitId, groupId, [], [], editing); unitId, groupId, "10c", "Chemie", [], [], editing);
/// Nutzer-Feedback: bei einer Klasse, die in mehreren Fächern unterrichtet wird (mehrere
/// LearningGroup-Datensätze mit gleichem Namen), war im Stunden-Dialog nicht erkennbar,
/// welche der beiden Stunden man gerade bearbeitet.
[Fact]
public void GroupSubjectDisplay_ZeigtGruppeUndFachZusammen()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
Assert.Equal("10c · Chemie", vm.GroupSubjectDisplay);
}
[Fact] [Fact]
public void AddPhase_FuegtZeileMitStandardwertenHinzu() public void AddPhase_FuegtZeileMitStandardwertenHinzu()
@@ -83,7 +93,7 @@ public sealed class LessonDialogViewModelTests
var codes = new FakeShorthandCodes([new ShorthandCode { Code = "Tb" }, new ShorthandCode { Code = "SH" }]); var codes = new FakeShorthandCodes([new ShorthandCode { Code = "Tb" }, new ShorthandCode { Code = "SH" }]);
var vm = new LessonDialogViewModel(new FakeLessons(), codes, new FakeAlternativeLessonPaths([]), var vm = new LessonDialogViewModel(new FakeLessons(), codes, new FakeAlternativeLessonPaths([]),
new FakeTimetableSlots(), NewPeriodSchedule(), new FakeTimetableSlots(), NewPeriodSchedule(),
Guid.NewGuid(), Guid.NewGuid(), [], ["Plenum", "LDE", "Tb"], null); // "Tb" doppelt (Katalog + Historie), soll nur einmal erscheinen Guid.NewGuid(), Guid.NewGuid(), "10c", "Chemie", [], ["Plenum", "LDE", "Tb"], null); // "Tb" doppelt (Katalog + Historie), soll nur einmal erscheinen
Assert.Equal(["LDE", "Plenum", "SH", "Tb"], vm.ShorthandSuggestions); Assert.Equal(["LDE", "Plenum", "SH", "Tb"], vm.ShorthandSuggestions);
} }
@@ -145,7 +155,7 @@ public sealed class LessonDialogViewModelTests
var groupId = Guid.NewGuid(); var groupId = Guid.NewGuid();
var lessons = new FakeLessons(); var lessons = new FakeLessons();
var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]), var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
new FakeTimetableSlots(), NewPeriodSchedule(), unitId, groupId, [], [], null) new FakeTimetableSlots(), NewPeriodSchedule(), unitId, groupId, "10c", "Chemie", [], [], null)
{ {
Topic = "Brechung", DateText = "01.09.2025", StartTimeText = "11:45", Topic = "Brechung", DateText = "01.09.2025", StartTimeText = "11:45",
}; };
@@ -242,7 +252,7 @@ public sealed class LessonDialogViewModelTests
}; };
var vm = new LessonDialogViewModel(new FakeLessons(), new FakeShorthandCodes([]), alternativePaths, var vm = new LessonDialogViewModel(new FakeLessons(), new FakeShorthandCodes([]), alternativePaths,
new FakeTimetableSlots(), NewPeriodSchedule(), Guid.NewGuid(), Guid.NewGuid(), [], [], editing); new FakeTimetableSlots(), NewPeriodSchedule(), Guid.NewGuid(), Guid.NewGuid(), "10c", "Chemie", [], [], editing);
Assert.True(vm.Phases[0].HasAlternativePath); Assert.True(vm.Phases[0].HasAlternativePath);
Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName); Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName);
@@ -0,0 +1,28 @@
using LehrerApp.Desktop.ViewModels.Groups;
using Xunit;
namespace LehrerApp.Desktop.Tests;
/// Nutzer-Feedback: bei einer Klasse, die in mehreren Fächern unterrichtet wird (mehrere
/// LearningGroup-Datensätze mit gleichem Namen), war im Einheiten-Dialog nur das Fach sichtbar,
/// nicht welche Klasse gemeint ist — beides zusammen macht es eindeutig.
public sealed class UnitDialogViewModelTests
{
[Fact]
public void GroupSubjectDisplay_ZeigtGruppeUndFachZusammen()
{
var vm = new UnitDialogViewModel(new FakeUnits(), new FakeCompetencyDomains(),
Guid.NewGuid(), Guid.NewGuid(), 10, "10c", "Chemie", editingUnit: null);
Assert.Equal("10c · Chemie", vm.GroupSubjectDisplay);
}
[Fact]
public void GroupSubjectDisplay_OhneFach_WeistDaraufHin()
{
var vm = new UnitDialogViewModel(new FakeUnits(), new FakeCompetencyDomains(),
Guid.NewGuid(), null, 10, "10c", "", editingUnit: null);
Assert.Equal("10c · kein Fach hinterlegt (siehe Lerngruppe)", vm.GroupSubjectDisplay);
}
}
@@ -39,6 +39,7 @@ public partial class PlanningTabViewModel : ObservableObject
public Guid? SubjectId { get; private set; } public Guid? SubjectId { get; private set; }
public int GradeLevel { get; private set; } public int GradeLevel { get; private set; }
public string SubjectName { get; private set; } = ""; public string SubjectName { get; private set; } = "";
public string GroupLabel { get; private set; } = "";
[ObservableProperty] private UnitSummary? _selectedUnit; [ObservableProperty] private UnitSummary? _selectedUnit;
[ObservableProperty] private LessonSummary? _selectedLesson; [ObservableProperty] private LessonSummary? _selectedLesson;
@@ -88,6 +89,7 @@ public partial class PlanningTabViewModel : ObservableObject
SubjectId = group?.SubjectId; SubjectId = group?.SubjectId;
GradeLevel = group?.GradeLevel ?? 0; GradeLevel = group?.GradeLevel ?? 0;
SubjectName = SubjectId is Guid sid ? _subjects.GetById(sid)?.Name ?? "" : ""; SubjectName = SubjectId is Guid sid ? _subjects.GetById(sid)?.Name ?? "" : "";
GroupLabel = group?.Name ?? "";
LoadUnits(); LoadUnits();
} }
@@ -494,21 +496,23 @@ public partial class UnitDialogViewModel : ObservableObject
public string CompetencySummary => _competencyCodes.Count == 0 public string CompetencySummary => _competencyCodes.Count == 0
? "Keine Kompetenzen" : $"{_competencyCodes.Count} Kompetenz(en)"; ? "Keine Kompetenzen" : $"{_competencyCodes.Count} Kompetenz(en)";
/// Fach kommt von der Lerngruppe, nicht editierbar (jede Gruppe unterrichtet ein Fach). /// Gruppe + Fach kommen von der Lerngruppe, nicht editierbar (jede Gruppe unterrichtet ein
public string SubjectDisplay { get; } /// Fach) — beide zusammen zeigen, da dieselbe Klasse in mehreren Fächern (mehrere
/// Lerngruppen mit gleichem Namen) sonst nicht unterscheidbar wäre (Nutzer-Feedback).
public string GroupSubjectDisplay { get; }
public Unit? Result { get; private set; } public Unit? Result { get; private set; }
public string DialogTitle => _editingUnit is null ? "Neue Einheit anlegen" : "Einheit bearbeiten"; public string DialogTitle => _editingUnit is null ? "Neue Einheit anlegen" : "Einheit bearbeiten";
public string SaveButtonText => _editingUnit is null ? "Anlegen" : "Speichern"; public string SaveButtonText => _editingUnit is null ? "Anlegen" : "Speichern";
public UnitDialogViewModel(IUnitRepository units, ICompetencyDomainRepository competencyDomains, public UnitDialogViewModel(IUnitRepository units, ICompetencyDomainRepository competencyDomains,
Guid groupId, Guid? subjectId, int gradeLevel, string subjectName, Unit? editingUnit) Guid groupId, Guid? subjectId, int gradeLevel, string groupName, string subjectName, Unit? editingUnit)
{ {
_units = units; _competencyDomains = competencyDomains; _units = units; _competencyDomains = competencyDomains;
_groupId = groupId; _subjectId = subjectId; _gradeLevel = gradeLevel; _groupId = groupId; _subjectId = subjectId; _gradeLevel = gradeLevel;
_editingUnit = editingUnit; _editingUnit = editingUnit;
SubjectDisplay = string.IsNullOrWhiteSpace(subjectName) GroupSubjectDisplay = string.IsNullOrWhiteSpace(subjectName)
? "Kein Fach hinterlegt (siehe Lerngruppe)" : $"Fach: {subjectName}"; ? $"{groupName} · kein Fach hinterlegt (siehe Lerngruppe)" : $"{groupName} · {subjectName}";
_competencyCodes = editingUnit is not null ? [.. editingUnit.Competencies] : []; _competencyCodes = editingUnit is not null ? [.. editingUnit.Competencies] : [];
BuildCompetencyTagGroups(); BuildCompetencyTagGroups();
@@ -631,6 +635,12 @@ public partial class LessonDialogViewModel : ObservableObject
public string DialogTitle => _editingLesson is null ? "Neue Stunde anlegen" : "Stunde bearbeiten"; public string DialogTitle => _editingLesson is null ? "Neue Stunde anlegen" : "Stunde bearbeiten";
public string SaveButtonText => _editingLesson is null ? "Anlegen" : "Speichern"; public string SaveButtonText => _editingLesson is null ? "Anlegen" : "Speichern";
/// Gruppe + Fach der Einheit, zu der diese Stunde gehört — nicht editierbar, nur zur
/// Einordnung. Ohne das war bei gleichnamigen Klassen in mehreren Fächern (mehrere
/// Lerngruppen mit gleichem Namen) nicht erkennbar, welche Stunde man gerade bearbeitet
/// (Nutzer-Feedback).
public string GroupSubjectDisplay { get; }
// Für die KI-Unterstützung mit Fokus auf genau diese Stunde (4.5.22) — nur für bereits // Für die KI-Unterstützung mit Fokus auf genau diese Stunde (4.5.22) — nur für bereits
// gespeicherte Stunden sinnvoll, eine gerade erst angelegte, noch ungespeicherte Stunde hat // gespeicherte Stunden sinnvoll, eine gerade erst angelegte, noch ungespeicherte Stunde hat
// keine echte Id, auf die sich die KI beziehen könnte. // keine echte Id, auf die sich die KI beziehen könnte.
@@ -645,12 +655,14 @@ public partial class LessonDialogViewModel : ObservableObject
public LessonDialogViewModel(ILessonRepository lessons, IShorthandCodeRepository shorthandCodes, public LessonDialogViewModel(ILessonRepository lessons, IShorthandCodeRepository shorthandCodes,
IAlternativeLessonPathRepository alternativePaths, ITimetableSlotRepository timetableSlots, IAlternativeLessonPathRepository alternativePaths, ITimetableSlotRepository timetableSlots,
PeriodScheduleService periodSchedule, Guid unitId, Guid groupId, PeriodScheduleService periodSchedule, Guid unitId, Guid groupId, string groupName, string subjectName,
List<string> materialSuggestions, List<string> shorthandHistorySuggestions, Lesson? editingLesson) List<string> materialSuggestions, List<string> shorthandHistorySuggestions, Lesson? editingLesson)
{ {
_lessons = lessons; _alternativePaths = alternativePaths; _lessons = lessons; _alternativePaths = alternativePaths;
_timetableSlots = timetableSlots; _periodSchedule = periodSchedule; _timetableSlots = timetableSlots; _periodSchedule = periodSchedule;
_unitId = unitId; _groupId = groupId; _editingLesson = editingLesson; _unitId = unitId; _groupId = groupId; _editingLesson = editingLesson;
GroupSubjectDisplay = string.IsNullOrWhiteSpace(subjectName)
? $"{groupName} · kein Fach hinterlegt (siehe Lerngruppe)" : $"{groupName} · {subjectName}";
MaterialSuggestions = [.. materialSuggestions]; MaterialSuggestions = [.. materialSuggestions];
// Vorschläge kommen sowohl aus dem gepflegten Kürzel-Katalog (Einstellungen) als auch aus // Vorschläge kommen sowohl aus dem gepflegten Kürzel-Katalog (Einstellungen) als auch aus
@@ -10,7 +10,10 @@
<Grid RowDefinitions="*,Auto" Margin="24"> <Grid RowDefinitions="*,Auto" Margin="24">
<ScrollViewer Grid.Row="0"> <ScrollViewer Grid.Row="0">
<StackPanel Spacing="14" Margin="0,0,12,0"> <StackPanel Spacing="14" Margin="0,0,12,0">
<StackPanel Spacing="2">
<TextBlock Text="{Binding DialogTitle}" Classes="dialogtitle"/> <TextBlock Text="{Binding DialogTitle}" Classes="dialogtitle"/>
<TextBlock Text="{Binding GroupSubjectDisplay}" FontSize="12" Opacity="0.6"/>
</StackPanel>
<Grid ColumnDefinitions="*,12,*,12,*,12,*"> <Grid ColumnDefinitions="*,12,*,12,*,12,*">
<StackPanel Grid.Column="0" Spacing="4"> <StackPanel Grid.Column="0" Spacing="4">
@@ -41,7 +41,7 @@ public partial class PlanningTabView : UserControl
var dialogVm = new UnitDialogViewModel( var dialogVm = new UnitDialogViewModel(
App.Services.GetRequiredService<IUnitRepository>(), App.Services.GetRequiredService<IUnitRepository>(),
App.Services.GetRequiredService<ICompetencyDomainRepository>(), App.Services.GetRequiredService<ICompetencyDomainRepository>(),
groupId, vm.SubjectId, vm.GradeLevel, vm.SubjectName, editingUnit); groupId, vm.SubjectId, vm.GradeLevel, vm.GroupLabel, vm.SubjectName, editingUnit);
var dialog = new UnitDialog { DataContext = dialogVm }; var dialog = new UnitDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window; var owner = TopLevel.GetTopLevel(this) as Window;
@@ -80,13 +80,16 @@ public partial class PlanningTabView : UserControl
private async Task<bool> ShowLessonDialog(Guid unitId, Guid groupId, private async Task<bool> ShowLessonDialog(Guid unitId, Guid groupId,
List<string> materialSuggestions, List<string> shorthandHistorySuggestions, Lesson? editingLesson) List<string> materialSuggestions, List<string> shorthandHistorySuggestions, Lesson? editingLesson)
{ {
if (DataContext is not PlanningTabViewModel vm) return false;
var dialogVm = new LessonDialogViewModel( var dialogVm = new LessonDialogViewModel(
App.Services.GetRequiredService<ILessonRepository>(), App.Services.GetRequiredService<ILessonRepository>(),
App.Services.GetRequiredService<IShorthandCodeRepository>(), App.Services.GetRequiredService<IShorthandCodeRepository>(),
App.Services.GetRequiredService<IAlternativeLessonPathRepository>(), App.Services.GetRequiredService<IAlternativeLessonPathRepository>(),
App.Services.GetRequiredService<ITimetableSlotRepository>(), App.Services.GetRequiredService<ITimetableSlotRepository>(),
App.Services.GetRequiredService<PeriodScheduleService>(), App.Services.GetRequiredService<PeriodScheduleService>(),
unitId, groupId, materialSuggestions, shorthandHistorySuggestions, editingLesson); unitId, groupId, vm.GroupLabel, vm.SubjectName,
materialSuggestions, shorthandHistorySuggestions, editingLesson);
var dialog = new LessonDialog { DataContext = dialogVm }; var dialog = new LessonDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window; var owner = TopLevel.GetTopLevel(this) as Window;
@@ -12,7 +12,7 @@
<StackPanel Spacing="14" Margin="0,0,12,0"> <StackPanel Spacing="14" Margin="0,0,12,0">
<StackPanel Spacing="2"> <StackPanel Spacing="2">
<TextBlock Text="{Binding DialogTitle}" Classes="dialogtitle"/> <TextBlock Text="{Binding DialogTitle}" Classes="dialogtitle"/>
<TextBlock Text="{Binding SubjectDisplay}" FontSize="12" Opacity="0.6"/> <TextBlock Text="{Binding GroupSubjectDisplay}" FontSize="12" Opacity="0.6"/>
</StackPanel> </StackPanel>
<StackPanel Spacing="4"> <StackPanel Spacing="4">
+7
View File
@@ -286,6 +286,13 @@ gruppenspezifischen Tab "Planung" (`GroupDetailView`), siehe unten.
- [x] **4.1.1** Listenansicht der Einheiten je Gruppe mit Status und Zeitraum - [x] **4.1.1** Listenansicht der Einheiten je Gruppe mit Status und Zeitraum
(ersetzt den Platzhalter im Tab "Planung"). (ersetzt den Platzhalter im Tab "Planung").
- [x] **4.1.2** Dialog Einheit anlegen/bearbeiten: Titel, Fach, Zeitraum, Status, Notizen. - [x] **4.1.2** Dialog Einheit anlegen/bearbeiten: Titel, Fach, Zeitraum, Status, Notizen.
**Nachtrag (Nutzer-Feedback, im Zuge des 4.3.3-Bugfixes):** Bei einer Klasse, die in
mehreren Fächern unterrichtet wird (mehrere `LearningGroup`-Datensätze mit gleichem Namen,
z.B. zwei "10c"), zeigte der Dialog bisher nur das Fach ("Fach: Chemie"), nicht welche
Klasse gemeint ist — beides zusammen war nirgends auf einen Blick zu sehen. Jetzt zeigen
sowohl `UnitDialog` (Einheit) als auch `LessonDialog` (Stunde) direkt unter dem Dialogtitel
"Gruppe · Fach" (z.B. "10c · Chemie"), nicht editierbar, nur zur Einordnung.
- [x] **4.1.3** Kompetenzen aus dem Katalog (siehe 8) einer Einheit zuordnen — Mehrfachauswahl. - [x] **4.1.3** Kompetenzen aus dem Katalog (siehe 8) einer Einheit zuordnen — Mehrfachauswahl.
- [x] **4.1.4** Einheit als Vorlage speichern und in eine andere Gruppe kopieren - [x] **4.1.4** Einheit als Vorlage speichern und in eine andere Gruppe kopieren
(inkl. Stunden, ohne Datumsbezug). (inkl. Stunden, ohne Datumsbezug).