Schüler hinzufügen, Kompetenzen
This commit is contained in:
@@ -15,7 +15,7 @@ public partial class GroupListViewModel : ObservableObject
|
||||
private readonly SchoolYearService _sy;
|
||||
|
||||
public Action<Guid, int>? OnNavigateToDetail { get; set; }
|
||||
public Action? OnAddGroup { get; set; }
|
||||
public Func<Task>? OnAddGroup { get; set; }
|
||||
|
||||
[ObservableProperty] private string _selectedSchoolYear = "";
|
||||
[ObservableProperty] private string _searchText = "";
|
||||
@@ -54,7 +54,13 @@ public partial class GroupListViewModel : ObservableObject
|
||||
Groups.Add(new GroupListItem(g));
|
||||
}
|
||||
|
||||
[RelayCommand] private void AddGroup() => OnAddGroup?.Invoke();
|
||||
[RelayCommand]
|
||||
private async Task AddGroup()
|
||||
{
|
||||
if (OnAddGroup is null) return;
|
||||
await OnAddGroup();
|
||||
LoadGroups();
|
||||
}
|
||||
[RelayCommand] private void Refresh() => LoadGroups();
|
||||
|
||||
[RelayCommand(CanExecute = nameof(HasSelectedGroup))]
|
||||
@@ -139,9 +145,15 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
{
|
||||
if (Group is null) return;
|
||||
Students.Clear();
|
||||
var enrolled = _students.GetByGroup(Group.Id, Group.SchoolYear);
|
||||
var enrolled = _students.GetByGroup(Group.Id, Group.SchoolYear);
|
||||
var enrollments = _enrollments.GetByGroupAndYear(Group.Id, Group.SchoolYear)
|
||||
.ToDictionary(e => e.StudentId);
|
||||
StudentCount = enrolled.Count;
|
||||
foreach (var s in enrolled) Students.Add(new StudentSummary(s));
|
||||
foreach (var s in enrolled)
|
||||
{
|
||||
enrollments.TryGetValue(s.Id, out var enr);
|
||||
Students.Add(new StudentSummary(s, enr));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -149,7 +161,11 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
{
|
||||
if (OnAddStudent is null) return;
|
||||
var confirmed = await OnAddStudent();
|
||||
if (confirmed) LoadStudents();
|
||||
if (confirmed)
|
||||
{
|
||||
LoadStudents();
|
||||
ParticipationTab.RefreshCurrentGrid();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(HasSelectedStudent))]
|
||||
@@ -162,6 +178,7 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
_enrollments.Delete(enrollment.Id);
|
||||
LoadStudents();
|
||||
SelectedStudent = null;
|
||||
ParticipationTab.RefreshCurrentGrid();
|
||||
}
|
||||
|
||||
partial void OnSelectedStudentChanged(StudentSummary? value) =>
|
||||
@@ -175,9 +192,30 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
|
||||
public class StudentSummary
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public string FullName { get; }
|
||||
public StudentSummary(Core.Models.Student s) { Id = s.Id; FullName = s.FullName; }
|
||||
public Guid Id { get; }
|
||||
public string FullName { get; }
|
||||
public string PeriodLabel { get; }
|
||||
|
||||
public StudentSummary(Core.Models.Student s, Enrollment? e)
|
||||
{
|
||||
Id = s.Id;
|
||||
FullName = s.FullName;
|
||||
PeriodLabel = e?.Period switch
|
||||
{
|
||||
EnrollmentPeriod.H1Only => "H1",
|
||||
EnrollmentPeriod.H2Only => "H2",
|
||||
EnrollmentPeriod.Custom => BuildCustomLabel(e),
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildCustomLabel(Enrollment e)
|
||||
{
|
||||
if (e.JoinedAt.HasValue && e.LeftAt.HasValue) return $"{e.JoinedAt:dd.MM.}–{e.LeftAt:dd.MM.}";
|
||||
if (e.JoinedAt.HasValue) return $"ab {e.JoinedAt:dd.MM.}";
|
||||
if (e.LeftAt.HasValue) return $"bis {e.LeftAt:dd.MM.}";
|
||||
return "Datum";
|
||||
}
|
||||
}
|
||||
|
||||
public class ExamSummary
|
||||
@@ -204,14 +242,23 @@ public class ExamSummary
|
||||
|
||||
public partial class AddStudentToGroupDialogViewModel : ObservableObject
|
||||
{
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IEnrollmentRepository _enrollments;
|
||||
private readonly Guid _groupId;
|
||||
private readonly Guid _groupId;
|
||||
private readonly string _schoolYear;
|
||||
|
||||
[ObservableProperty] private string _searchText = "";
|
||||
[ObservableProperty] private string _searchText = "";
|
||||
[ObservableProperty] private StudentPickerItem? _selectedStudent;
|
||||
[ObservableProperty] private string _validationMessage = "";
|
||||
[ObservableProperty] private string _validationMessage = "";
|
||||
[ObservableProperty] private EnrollmentPeriod _period = EnrollmentPeriod.FullYear;
|
||||
[ObservableProperty] private string _joinedAtText = "";
|
||||
[ObservableProperty] private string _leftAtText = "";
|
||||
|
||||
public bool IsFullYear { get => Period == EnrollmentPeriod.FullYear; set { if (value) Period = EnrollmentPeriod.FullYear; } }
|
||||
public bool IsH1Only { get => Period == EnrollmentPeriod.H1Only; set { if (value) Period = EnrollmentPeriod.H1Only; } }
|
||||
public bool IsH2Only { get => Period == EnrollmentPeriod.H2Only; set { if (value) Period = EnrollmentPeriod.H2Only; } }
|
||||
public bool IsCustom { get => Period == EnrollmentPeriod.Custom; set { if (value) Period = EnrollmentPeriod.Custom; } }
|
||||
public bool IsCustomPeriod => Period == EnrollmentPeriod.Custom;
|
||||
|
||||
public ObservableCollection<StudentPickerItem> AvailableStudents { get; } = [];
|
||||
public Enrollment? Result { get; private set; }
|
||||
@@ -224,6 +271,15 @@ public partial class AddStudentToGroupDialogViewModel : ObservableObject
|
||||
LoadAvailableStudents();
|
||||
}
|
||||
|
||||
partial void OnPeriodChanged(EnrollmentPeriod value)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsFullYear));
|
||||
OnPropertyChanged(nameof(IsH1Only));
|
||||
OnPropertyChanged(nameof(IsH2Only));
|
||||
OnPropertyChanged(nameof(IsCustom));
|
||||
OnPropertyChanged(nameof(IsCustomPeriod));
|
||||
}
|
||||
|
||||
partial void OnSearchTextChanged(string value) => LoadAvailableStudents();
|
||||
|
||||
private void LoadAvailableStudents()
|
||||
@@ -243,11 +299,28 @@ public partial class AddStudentToGroupDialogViewModel : ObservableObject
|
||||
private void Save()
|
||||
{
|
||||
if (SelectedStudent is null) { ValidationMessage = "Bitte einen Schüler auswählen."; return; }
|
||||
|
||||
DateOnly? joinedAt = null, leftAt = null;
|
||||
if (Period == EnrollmentPeriod.Custom)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(JoinedAtText) &&
|
||||
DateOnly.TryParseExact(JoinedAtText, "dd.MM.yyyy",
|
||||
null, System.Globalization.DateTimeStyles.None, out var j))
|
||||
joinedAt = j;
|
||||
if (!string.IsNullOrWhiteSpace(LeftAtText) &&
|
||||
DateOnly.TryParseExact(LeftAtText, "dd.MM.yyyy",
|
||||
null, System.Globalization.DateTimeStyles.None, out var l))
|
||||
leftAt = l;
|
||||
}
|
||||
|
||||
Result = new Enrollment
|
||||
{
|
||||
StudentId = SelectedStudent.Id,
|
||||
GroupId = _groupId,
|
||||
SchoolYear = _schoolYear,
|
||||
Period = Period,
|
||||
JoinedAt = joinedAt,
|
||||
LeftAt = leftAt,
|
||||
};
|
||||
_enrollments.Save(Result);
|
||||
}
|
||||
@@ -255,7 +328,7 @@ public partial class AddStudentToGroupDialogViewModel : ObservableObject
|
||||
|
||||
public class StudentPickerItem
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public Guid Id { get; }
|
||||
public string FullName { get; }
|
||||
public StudentPickerItem(Student s) { Id = s.Id; FullName = s.FullName; }
|
||||
}
|
||||
@@ -264,8 +337,10 @@ public class StudentPickerItem
|
||||
|
||||
public partial class AddGroupDialogViewModel : ObservableObject
|
||||
{
|
||||
private readonly IGroupRepository _groups;
|
||||
private readonly SchoolYearService _sy;
|
||||
private readonly IGroupRepository _groups;
|
||||
private readonly ISubjectRepository _subjects;
|
||||
private readonly SchoolYearService _sy;
|
||||
private List<Subject> _allSubjects = [];
|
||||
|
||||
public List<string> TypeOptions { get; } = ["Klasse", "Kurs"];
|
||||
[ObservableProperty] private string _selectedTypeName = "Kurs";
|
||||
@@ -289,12 +364,15 @@ public partial class AddGroupDialogViewModel : ObservableObject
|
||||
[ObservableProperty] private string _validationMessage = "";
|
||||
|
||||
public List<string> SchoolYears { get; }
|
||||
public List<string> KnownSubjectNames { get; private set; } = [];
|
||||
public LearningGroup? Result { get; private set; }
|
||||
|
||||
public AddGroupDialogViewModel(IGroupRepository groups, SchoolYearService sy)
|
||||
public AddGroupDialogViewModel(IGroupRepository groups, ISubjectRepository subjects, SchoolYearService sy)
|
||||
{
|
||||
_groups = groups; _sy = sy;
|
||||
SchoolYears = sy.RecentSchoolYears(3);
|
||||
_groups = groups; _subjects = subjects; _sy = sy;
|
||||
_allSubjects = subjects.GetAll();
|
||||
KnownSubjectNames = _allSubjects.Select(s => s.Name).ToList();
|
||||
SchoolYears = sy.RecentSchoolYears(3);
|
||||
SelectedSchoolYear = sy.CurrentSchoolYear();
|
||||
PropertyChanged += (_, e) =>
|
||||
{
|
||||
@@ -309,10 +387,31 @@ public partial class AddGroupDialogViewModel : ObservableObject
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Name)) { ValidationMessage = "Bezeichnung erforderlich."; return; }
|
||||
if (GradeLevel is < 1 or > 13) { ValidationMessage = "Klassenstufe 1–13."; return; }
|
||||
|
||||
string? subjectName = IsKurs && !string.IsNullOrWhiteSpace(Subject) ? Subject.Trim() : null;
|
||||
Guid? subjectId = null;
|
||||
|
||||
if (subjectName is not null)
|
||||
{
|
||||
var existing = _allSubjects.FirstOrDefault(
|
||||
s => s.Name.Equals(subjectName, StringComparison.OrdinalIgnoreCase));
|
||||
if (existing is not null)
|
||||
{
|
||||
subjectId = existing.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newSubject = new Subject { Name = subjectName };
|
||||
_subjects.Save(newSubject);
|
||||
subjectId = newSubject.Id;
|
||||
}
|
||||
}
|
||||
|
||||
Result = new LearningGroup
|
||||
{
|
||||
Name = Name.Trim(),
|
||||
Subject = IsKurs && !string.IsNullOrWhiteSpace(Subject) ? Subject.Trim() : null,
|
||||
Subject = subjectName,
|
||||
SubjectId = subjectId,
|
||||
Type = IsKurs ? GroupType.Course : GroupType.Class,
|
||||
GradeLevel = GradeLevel,
|
||||
GradingSystem = GradingSystem,
|
||||
|
||||
@@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels.Groups;
|
||||
|
||||
@@ -14,18 +15,29 @@ public partial class ParticipationTabViewModel : ObservableObject
|
||||
private readonly IParticipationRepository _entries;
|
||||
private readonly IParticipationAspectRepository _aspects;
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IEnrollmentRepository _enrollments;
|
||||
private readonly IGroupRepository _groups;
|
||||
private readonly ICompetencyDomainRepository _competencyDomains;
|
||||
|
||||
private Guid _groupId;
|
||||
private string _schoolYear = "";
|
||||
private Guid? _subjectId;
|
||||
private int _gradeLevel;
|
||||
|
||||
[ObservableProperty] private ParticipationSessionItem? _selectedSession;
|
||||
[ObservableProperty] private string _noDataText = "Keine Bewertungssitzung ausgewählt.";
|
||||
[ObservableProperty] private bool _competencyTagsVisible;
|
||||
[ObservableProperty] private bool _hasCompetencyCatalog;
|
||||
[ObservableProperty] private bool _studentCompetencyRatingsVisible;
|
||||
[ObservableProperty] private int _rebuildColumnsSignal;
|
||||
|
||||
public string SelectedSessionDisplay => SelectedSession?.Display ?? "";
|
||||
public List<string> ActiveCompetencyCodes { get; private set; } = [];
|
||||
|
||||
public ObservableCollection<ParticipationSessionItem> Sessions { get; } = [];
|
||||
public ObservableCollection<ParticipationStudentRow> StudentRows { get; } = [];
|
||||
public ObservableCollection<AspectColumnDef> Aspects { get; } = [];
|
||||
public ObservableCollection<ParticipationSessionItem> Sessions { get; } = [];
|
||||
public ObservableCollection<ParticipationStudentRow> StudentRows { get; } = [];
|
||||
public ObservableCollection<AspectColumnDef> Aspects { get; } = [];
|
||||
public ObservableCollection<CompetencyTagGroup> CompetencyTagGroups { get; } = [];
|
||||
|
||||
public Func<Task<ParticipationSession?>>? OnAddSession { get; set; }
|
||||
public Func<ParticipationTabViewModel, Task>? OnQuickInput { get; set; }
|
||||
@@ -34,16 +46,29 @@ public partial class ParticipationTabViewModel : ObservableObject
|
||||
IParticipationSessionRepository sessions,
|
||||
IParticipationRepository entries,
|
||||
IParticipationAspectRepository aspects,
|
||||
IStudentRepository students)
|
||||
IStudentRepository students,
|
||||
IEnrollmentRepository enrollments,
|
||||
IGroupRepository groups,
|
||||
ICompetencyDomainRepository competencyDomains)
|
||||
{
|
||||
_sessions = sessions; _entries = entries;
|
||||
_aspects = aspects; _students = students;
|
||||
_sessions = sessions; _entries = entries;
|
||||
_aspects = aspects; _students = students;
|
||||
_enrollments = enrollments; _groups = groups;
|
||||
_competencyDomains = competencyDomains;
|
||||
}
|
||||
|
||||
public void Initialize(Guid groupId, string schoolYear)
|
||||
{
|
||||
_groupId = groupId;
|
||||
_schoolYear = schoolYear;
|
||||
|
||||
var group = _groups.GetById(groupId);
|
||||
_subjectId = group?.SubjectId;
|
||||
_gradeLevel = group?.GradeLevel ?? 0;
|
||||
|
||||
HasCompetencyCatalog = _subjectId.HasValue
|
||||
&& _competencyDomains.GetBySubjectAndGrade(_subjectId.Value, _gradeLevel).Count > 0;
|
||||
|
||||
LoadAspects();
|
||||
LoadSessions();
|
||||
}
|
||||
@@ -78,27 +103,54 @@ public partial class ParticipationTabViewModel : ObservableObject
|
||||
partial void OnSelectedSessionChanged(ParticipationSessionItem? value)
|
||||
{
|
||||
OnPropertyChanged(nameof(SelectedSessionDisplay));
|
||||
if (value is null) { StudentRows.Clear(); QuickInputCommand.NotifyCanExecuteChanged(); return; }
|
||||
LoadGrid(value.Id);
|
||||
if (value is null)
|
||||
{
|
||||
StudentRows.Clear();
|
||||
CompetencyTagGroups.Clear();
|
||||
ActiveCompetencyCodes = [];
|
||||
QuickInputCommand.NotifyCanExecuteChanged();
|
||||
RebuildColumnsSignal++;
|
||||
return;
|
||||
}
|
||||
LoadCompetencyTags(value.Id); // sets ActiveCompetencyCodes first
|
||||
LoadGrid(value.Id); // uses ActiveCompetencyCodes, fires RebuildColumnsSignal++
|
||||
}
|
||||
|
||||
private void LoadGrid(Guid sessionId)
|
||||
{
|
||||
StudentRows.Clear();
|
||||
var students = _students.GetByGroup(_groupId, _schoolYear);
|
||||
var entries = _entries.GetBySession(sessionId);
|
||||
var session = _sessions.GetById(sessionId);
|
||||
var sessionDate = session?.Date ?? DateOnly.FromDateTime(DateTime.Today);
|
||||
var students = _students.GetByGroup(_groupId, _schoolYear);
|
||||
var enrollments = _enrollments.GetByGroupAndYear(_groupId, _schoolYear);
|
||||
var entries = _entries.GetBySession(sessionId);
|
||||
|
||||
foreach (var s in students)
|
||||
{
|
||||
var enrollment = enrollments.FirstOrDefault(e => e.StudentId == s.Id);
|
||||
if (enrollment is not null && !IsEnrolledAtDate(enrollment, sessionDate))
|
||||
continue;
|
||||
|
||||
var entry = entries.FirstOrDefault(e => e.StudentId == s.Id)
|
||||
?? new ParticipationEntry { SessionId = sessionId, GroupId = _groupId, StudentId = s.Id };
|
||||
var row = new ParticipationStudentRow(s.Id, s.FullName, entry, Aspects.ToList());
|
||||
row.OnRatingChanged = (studentId, key, val) => SaveRating(sessionId, studentId, key, val);
|
||||
var row = new ParticipationStudentRow(s.Id, s.FullName, entry, Aspects.ToList(), ActiveCompetencyCodes);
|
||||
row.OnRatingChanged = (sid, key, val) => SaveRating(sessionId, sid, key, val);
|
||||
row.OnCompetencyRatingChanged = (sid, code, val) => SaveCompetencyRating(sessionId, sid, code, val);
|
||||
StudentRows.Add(row);
|
||||
}
|
||||
QuickInputCommand.NotifyCanExecuteChanged();
|
||||
RebuildColumnsSignal++;
|
||||
}
|
||||
|
||||
private static bool IsEnrolledAtDate(Enrollment e, DateOnly date) => e.Period switch
|
||||
{
|
||||
EnrollmentPeriod.H1Only => date.Month >= 8 || date.Month <= 1,
|
||||
EnrollmentPeriod.H2Only => date.Month >= 2 && date.Month <= 7,
|
||||
EnrollmentPeriod.Custom => (e.JoinedAt is null || date >= e.JoinedAt.Value)
|
||||
&& (e.LeftAt is null || date <= e.LeftAt.Value),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
private void SaveRating(Guid sessionId, Guid studentId, string key, int? value)
|
||||
{
|
||||
var session = _sessions.GetById(sessionId);
|
||||
@@ -124,6 +176,53 @@ public partial class ParticipationTabViewModel : ObservableObject
|
||||
_entries.Save(entry);
|
||||
}
|
||||
|
||||
public void RefreshCurrentGrid()
|
||||
{
|
||||
if (SelectedSession is not null) LoadGrid(SelectedSession.Id);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleCompetencyTags() => CompetencyTagsVisible = !CompetencyTagsVisible;
|
||||
|
||||
partial void OnStudentCompetencyRatingsVisibleChanged(bool value) => RebuildColumnsSignal++;
|
||||
|
||||
private void LoadCompetencyTags(Guid sessionId)
|
||||
{
|
||||
CompetencyTagGroups.Clear();
|
||||
var session = _sessions.GetById(sessionId);
|
||||
ActiveCompetencyCodes = session?.CompetencyCodes?.ToList() ?? [];
|
||||
|
||||
if (!_subjectId.HasValue) return;
|
||||
var active = ActiveCompetencyCodes.ToHashSet();
|
||||
|
||||
foreach (var domain in _competencyDomains.GetBySubjectAndGrade(_subjectId.Value, _gradeLevel))
|
||||
{
|
||||
var group = new CompetencyTagGroup(domain.Name, domain.Code);
|
||||
foreach (var item in domain.Items.OrderBy(i => i.SortOrder))
|
||||
{
|
||||
var tag = new CompetencyTag(item.Code, item.Description, active.Contains(item.Code));
|
||||
tag.OnChanged = (code, sel) => OnTagToggled(code, sel);
|
||||
group.Items.Add(tag);
|
||||
}
|
||||
if (group.Items.Count > 0)
|
||||
CompetencyTagGroups.Add(group);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTagToggled(string code, bool selected)
|
||||
{
|
||||
if (SelectedSession is null) return;
|
||||
var session = _sessions.GetById(SelectedSession.Id);
|
||||
if (session is null) return;
|
||||
if (selected) { if (!session.CompetencyCodes.Contains(code)) session.CompetencyCodes.Add(code); }
|
||||
else { session.CompetencyCodes.Remove(code); }
|
||||
_sessions.Save(session);
|
||||
|
||||
ActiveCompetencyCodes = session.CompetencyCodes.ToList();
|
||||
if (StudentCompetencyRatingsVisible)
|
||||
LoadGrid(SelectedSession.Id); // reloads rows with updated cells, fires RebuildColumnsSignal++
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddSession()
|
||||
{
|
||||
@@ -132,7 +231,10 @@ public partial class ParticipationTabViewModel : ObservableObject
|
||||
if (session is null) return;
|
||||
session.GroupId = _groupId;
|
||||
_sessions.Save(session);
|
||||
LoadSessions();
|
||||
|
||||
Sessions.Clear();
|
||||
foreach (var s in _sessions.GetByGroup(_groupId))
|
||||
Sessions.Add(new ParticipationSessionItem(s));
|
||||
SelectedSession = Sessions.FirstOrDefault(s => s.Id == session.Id);
|
||||
}
|
||||
|
||||
@@ -161,6 +263,30 @@ public partial class ParticipationTabViewModel : ObservableObject
|
||||
entry.Note = note;
|
||||
_entries.Save(entry);
|
||||
}
|
||||
|
||||
private void SaveCompetencyRating(Guid sessionId, Guid studentId, string code, int? value)
|
||||
{
|
||||
var session = _sessions.GetById(sessionId);
|
||||
var entry = _entries.GetBySessionAndStudent(sessionId, studentId)
|
||||
?? new ParticipationEntry
|
||||
{
|
||||
SessionId = sessionId,
|
||||
GroupId = _groupId,
|
||||
StudentId = studentId,
|
||||
Date = session?.Date ?? DateOnly.FromDateTime(DateTime.Today),
|
||||
};
|
||||
var existing = entry.CompetencyRatings.FirstOrDefault(r => r.Code == code);
|
||||
if (value is null)
|
||||
{
|
||||
if (existing is not null) entry.CompetencyRatings.Remove(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (existing is null) entry.CompetencyRatings.Add(new CompetencyRating { Code = code, Value = value.Value });
|
||||
else existing.Value = value.Value;
|
||||
}
|
||||
_entries.Save(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Zeilendaten für das Bewertungsraster ─────────────────────────────────────
|
||||
@@ -170,17 +296,21 @@ public partial class ParticipationStudentRow : ObservableObject
|
||||
public Guid StudentId { get; }
|
||||
public string Name { get; }
|
||||
|
||||
private readonly ParticipationEntry _entry;
|
||||
private readonly ParticipationEntry _entry;
|
||||
private readonly IReadOnlyList<AspectColumnDef> _aspectDefs;
|
||||
|
||||
public ObservableCollection<RatingCell> Cells { get; } = [];
|
||||
public Action<Guid, string, int?>? OnRatingChanged { get; set; }
|
||||
public ObservableCollection<RatingCell> Cells { get; } = [];
|
||||
public ObservableCollection<RatingCell> CompetencyCells { get; } = [];
|
||||
|
||||
public ParticipationStudentRow(Guid id, string name, ParticipationEntry entry, List<AspectColumnDef> aspects)
|
||||
public Action<Guid, string, int?>? OnRatingChanged { get; set; }
|
||||
public Action<Guid, string, int?>? OnCompetencyRatingChanged { get; set; }
|
||||
|
||||
public ParticipationStudentRow(Guid id, string name, ParticipationEntry entry,
|
||||
List<AspectColumnDef> aspects, List<string> competencyCodes)
|
||||
{
|
||||
StudentId = id;
|
||||
Name = name;
|
||||
_entry = entry;
|
||||
StudentId = id;
|
||||
Name = name;
|
||||
_entry = entry;
|
||||
_aspectDefs = aspects;
|
||||
|
||||
foreach (var a in aspects)
|
||||
@@ -190,6 +320,14 @@ public partial class ParticipationStudentRow : ObservableObject
|
||||
cell.OnChanged = (sid, key, val) => OnRatingChanged?.Invoke(sid, key, val);
|
||||
Cells.Add(cell);
|
||||
}
|
||||
|
||||
foreach (var code in competencyCodes)
|
||||
{
|
||||
var existing = entry.CompetencyRatings.FirstOrDefault(r => r.Code == code);
|
||||
var cell = new RatingCell(id, code, existing?.Value);
|
||||
cell.OnChanged = (sid, key, val) => OnCompetencyRatingChanged?.Invoke(sid, key, val);
|
||||
CompetencyCells.Add(cell);
|
||||
}
|
||||
}
|
||||
|
||||
public int? GetRating(string key) =>
|
||||
@@ -258,6 +396,37 @@ public partial class RatingCell : ObservableObject
|
||||
};
|
||||
}
|
||||
|
||||
// ── Kompetenz-Tags ────────────────────────────────────────────────────────────
|
||||
|
||||
public class CompetencyTagGroup(string name, string code)
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
public string Code { get; } = code;
|
||||
public string DisplayName { get; } = string.IsNullOrEmpty(code) ? name : $"{name} ({code})";
|
||||
public List<CompetencyTag> Items { get; } = [];
|
||||
}
|
||||
|
||||
public partial class CompetencyTag : ObservableObject
|
||||
{
|
||||
public string Code { get; }
|
||||
public string Description { get; }
|
||||
public string Display { get; }
|
||||
|
||||
[ObservableProperty] private bool _isSelected;
|
||||
|
||||
public Action<string, bool>? OnChanged { get; set; }
|
||||
|
||||
public CompetencyTag(string code, string description, bool isSelected)
|
||||
{
|
||||
Code = code;
|
||||
Description = description;
|
||||
Display = string.IsNullOrEmpty(code) ? description : $"[{code}] {description}";
|
||||
_isSelected = isSelected;
|
||||
}
|
||||
|
||||
partial void OnIsSelectedChanged(bool value) => OnChanged?.Invoke(Code, value);
|
||||
}
|
||||
|
||||
// ── Hilfsklassen ──────────────────────────────────────────────────────────────
|
||||
|
||||
public class AspectColumnDef
|
||||
|
||||
Reference in New Issue
Block a user