Mitarbeit bewerten begonnen, Schülerdaten, Gruppen
This commit is contained in:
@@ -21,6 +21,9 @@ public partial class GroupListViewModel : ObservableObject
|
||||
[ObservableProperty] private string _searchText = "";
|
||||
[ObservableProperty] private GroupListItem? _selectedGroup;
|
||||
|
||||
public string SelectedGroupDisplayName => SelectedGroup?.DisplayName ?? "";
|
||||
public string SelectedGroupSubtitle => SelectedGroup?.Subtitle ?? "";
|
||||
|
||||
public ObservableCollection<string> SchoolYears { get; } = [];
|
||||
public ObservableCollection<GroupListItem> Groups { get; } = [];
|
||||
|
||||
@@ -33,8 +36,12 @@ public partial class GroupListViewModel : ObservableObject
|
||||
|
||||
partial void OnSelectedSchoolYearChanged(string value) => LoadGroups();
|
||||
partial void OnSearchTextChanged(string value) => LoadGroups();
|
||||
partial void OnSelectedGroupChanged(GroupListItem? value) =>
|
||||
partial void OnSelectedGroupChanged(GroupListItem? value)
|
||||
{
|
||||
OnPropertyChanged(nameof(SelectedGroupDisplayName));
|
||||
OnPropertyChanged(nameof(SelectedGroupSubtitle));
|
||||
NavigateToSectionCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void LoadGroups()
|
||||
{
|
||||
@@ -88,6 +95,7 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
{
|
||||
private readonly IGroupRepository _groups;
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IEnrollmentRepository _enrollments;
|
||||
private readonly IExamRepository _exams;
|
||||
private readonly IGradeRepository _grades;
|
||||
|
||||
@@ -96,14 +104,21 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
[ObservableProperty] private string _groupSubtitle = "";
|
||||
[ObservableProperty] private int _studentCount;
|
||||
[ObservableProperty] private int _activeTabIndex = 0;
|
||||
[ObservableProperty] private StudentSummary? _selectedStudent;
|
||||
|
||||
public ObservableCollection<StudentSummary> Students { get; } = [];
|
||||
public ObservableCollection<ExamSummary> Exams { get; } = [];
|
||||
|
||||
public ParticipationTabViewModel ParticipationTab { get; }
|
||||
public Func<Task<bool>>? OnAddStudent { get; set; }
|
||||
|
||||
public GroupDetailViewModel(IGroupRepository groups, IStudentRepository students,
|
||||
IExamRepository exams, IGradeRepository grades)
|
||||
IEnrollmentRepository enrollments, IExamRepository exams, IGradeRepository grades,
|
||||
ParticipationTabViewModel participationTab)
|
||||
{
|
||||
_groups = groups; _students = students; _exams = exams; _grades = grades;
|
||||
_groups = groups; _students = students; _enrollments = enrollments;
|
||||
_exams = exams; _grades = grades;
|
||||
ParticipationTab = participationTab;
|
||||
}
|
||||
|
||||
public void LoadGroup(Guid id)
|
||||
@@ -114,19 +129,48 @@ public partial class GroupDetailViewModel : ObservableObject
|
||||
GroupSubtitle = $"{Group.SchoolYear} · " +
|
||||
$"{(Group.Type == GroupType.Class ? "Klasse" : "Kurs")} · " +
|
||||
$"Stufe {Group.GradeLevel} · Noten {(Group.GradingSystem == GradingSystem.Grades1To6 ? "1–6" : "0–15")}";
|
||||
LoadStudents();
|
||||
Exams.Clear();
|
||||
foreach (var e in _exams.GetByGroup(Group.Id)) Exams.Add(new ExamSummary(e));
|
||||
ParticipationTab.Initialize(Group.Id, Group.SchoolYear);
|
||||
}
|
||||
|
||||
public void LoadStudents()
|
||||
{
|
||||
if (Group is null) return;
|
||||
Students.Clear();
|
||||
var enrolled = _students.GetByGroup(Group.Id, Group.SchoolYear);
|
||||
StudentCount = enrolled.Count;
|
||||
foreach (var s in enrolled) Students.Add(new StudentSummary(s));
|
||||
|
||||
Exams.Clear();
|
||||
foreach (var e in _exams.GetByGroup(Group.Id)) Exams.Add(new ExamSummary(e));
|
||||
}
|
||||
|
||||
[RelayCommand] private void AddStudent() { /* TODO */ }
|
||||
[RelayCommand] private void AddExam() { /* TODO */ }
|
||||
[RelayCommand] private void Refresh() { if (Group is not null) LoadGroup(Group.Id); }
|
||||
[RelayCommand]
|
||||
private async Task AddStudent()
|
||||
{
|
||||
if (OnAddStudent is null) return;
|
||||
var confirmed = await OnAddStudent();
|
||||
if (confirmed) LoadStudents();
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(HasSelectedStudent))]
|
||||
private void RemoveStudent()
|
||||
{
|
||||
if (Group is null || SelectedStudent is null) return;
|
||||
var enrollment = _enrollments.GetByGroupAndYear(Group.Id, Group.SchoolYear)
|
||||
.FirstOrDefault(e => e.StudentId == SelectedStudent.Id);
|
||||
if (enrollment is null) return;
|
||||
_enrollments.Delete(enrollment.Id);
|
||||
LoadStudents();
|
||||
SelectedStudent = null;
|
||||
}
|
||||
|
||||
partial void OnSelectedStudentChanged(StudentSummary? value) =>
|
||||
RemoveStudentCommand.NotifyCanExecuteChanged();
|
||||
|
||||
private bool HasSelectedStudent() => SelectedStudent is not null;
|
||||
|
||||
[RelayCommand] private void AddExam() { /* TODO */ }
|
||||
[RelayCommand] private void Refresh() { if (Group is not null) LoadGroup(Group.Id); }
|
||||
}
|
||||
|
||||
public class StudentSummary
|
||||
@@ -156,6 +200,66 @@ public class ExamSummary
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dialog: Schüler zur Gruppe hinzufügen ─────────────────────────────────────
|
||||
|
||||
public partial class AddStudentToGroupDialogViewModel : ObservableObject
|
||||
{
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IEnrollmentRepository _enrollments;
|
||||
private readonly Guid _groupId;
|
||||
private readonly string _schoolYear;
|
||||
|
||||
[ObservableProperty] private string _searchText = "";
|
||||
[ObservableProperty] private StudentPickerItem? _selectedStudent;
|
||||
[ObservableProperty] private string _validationMessage = "";
|
||||
|
||||
public ObservableCollection<StudentPickerItem> AvailableStudents { get; } = [];
|
||||
public Enrollment? Result { get; private set; }
|
||||
|
||||
public AddStudentToGroupDialogViewModel(IStudentRepository students,
|
||||
IEnrollmentRepository enrollments, Guid groupId, string schoolYear)
|
||||
{
|
||||
_students = students; _enrollments = enrollments;
|
||||
_groupId = groupId; _schoolYear = schoolYear;
|
||||
LoadAvailableStudents();
|
||||
}
|
||||
|
||||
partial void OnSearchTextChanged(string value) => LoadAvailableStudents();
|
||||
|
||||
private void LoadAvailableStudents()
|
||||
{
|
||||
var alreadyEnrolled = _enrollments.GetByGroupAndYear(_groupId, _schoolYear)
|
||||
.Select(e => e.StudentId).ToHashSet();
|
||||
var all = _students.GetAll();
|
||||
var available = all
|
||||
.Where(s => !alreadyEnrolled.Contains(s.Id))
|
||||
.Where(s => string.IsNullOrWhiteSpace(SearchText) ||
|
||||
s.FullName.Contains(SearchText, StringComparison.OrdinalIgnoreCase));
|
||||
AvailableStudents.Clear();
|
||||
foreach (var s in available) AvailableStudents.Add(new StudentPickerItem(s));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
{
|
||||
if (SelectedStudent is null) { ValidationMessage = "Bitte einen Schüler auswählen."; return; }
|
||||
Result = new Enrollment
|
||||
{
|
||||
StudentId = SelectedStudent.Id,
|
||||
GroupId = _groupId,
|
||||
SchoolYear = _schoolYear,
|
||||
};
|
||||
_enrollments.Save(Result);
|
||||
}
|
||||
}
|
||||
|
||||
public class StudentPickerItem
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public string FullName { get; }
|
||||
public StudentPickerItem(Student s) { Id = s.Id; FullName = s.FullName; }
|
||||
}
|
||||
|
||||
// ── Dialog: Neue Lerngruppe anlegen ──────────────────────────────────────────
|
||||
|
||||
public partial class AddGroupDialogViewModel : ObservableObject
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels.Groups;
|
||||
|
||||
// ── Tab-ViewModel ─────────────────────────────────────────────────────────────
|
||||
|
||||
public partial class ParticipationTabViewModel : ObservableObject
|
||||
{
|
||||
private readonly IParticipationSessionRepository _sessions;
|
||||
private readonly IParticipationRepository _entries;
|
||||
private readonly IParticipationAspectRepository _aspects;
|
||||
private readonly IStudentRepository _students;
|
||||
|
||||
private Guid _groupId;
|
||||
private string _schoolYear = "";
|
||||
|
||||
[ObservableProperty] private ParticipationSessionItem? _selectedSession;
|
||||
[ObservableProperty] private string _noDataText = "Keine Bewertungssitzung ausgewählt.";
|
||||
|
||||
public string SelectedSessionDisplay => SelectedSession?.Display ?? "";
|
||||
|
||||
public ObservableCollection<ParticipationSessionItem> Sessions { get; } = [];
|
||||
public ObservableCollection<ParticipationStudentRow> StudentRows { get; } = [];
|
||||
public ObservableCollection<AspectColumnDef> Aspects { get; } = [];
|
||||
|
||||
public Func<Task<ParticipationSession?>>? OnAddSession { get; set; }
|
||||
public Func<ParticipationTabViewModel, Task>? OnQuickInput { get; set; }
|
||||
|
||||
public ParticipationTabViewModel(
|
||||
IParticipationSessionRepository sessions,
|
||||
IParticipationRepository entries,
|
||||
IParticipationAspectRepository aspects,
|
||||
IStudentRepository students)
|
||||
{
|
||||
_sessions = sessions; _entries = entries;
|
||||
_aspects = aspects; _students = students;
|
||||
}
|
||||
|
||||
public void Initialize(Guid groupId, string schoolYear)
|
||||
{
|
||||
_groupId = groupId;
|
||||
_schoolYear = schoolYear;
|
||||
LoadAspects();
|
||||
LoadSessions();
|
||||
}
|
||||
|
||||
private void LoadAspects()
|
||||
{
|
||||
Aspects.Clear();
|
||||
var defaults = _aspects.GetDefaults();
|
||||
var specific = _aspects.GetByGroup(_groupId);
|
||||
var all = defaults.Concat(specific).ToList();
|
||||
|
||||
if (!all.Any())
|
||||
{
|
||||
foreach (var a in DefaultParticipationAspects.All)
|
||||
Aspects.Add(new AspectColumnDef(a));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var a in all) Aspects.Add(new AspectColumnDef(a));
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadSessions()
|
||||
{
|
||||
Sessions.Clear();
|
||||
foreach (var s in _sessions.GetByGroup(_groupId))
|
||||
Sessions.Add(new ParticipationSessionItem(s));
|
||||
if (SelectedSession is null && Sessions.Any())
|
||||
SelectedSession = Sessions[0];
|
||||
}
|
||||
|
||||
partial void OnSelectedSessionChanged(ParticipationSessionItem? value)
|
||||
{
|
||||
OnPropertyChanged(nameof(SelectedSessionDisplay));
|
||||
if (value is null) { StudentRows.Clear(); QuickInputCommand.NotifyCanExecuteChanged(); return; }
|
||||
LoadGrid(value.Id);
|
||||
}
|
||||
|
||||
private void LoadGrid(Guid sessionId)
|
||||
{
|
||||
StudentRows.Clear();
|
||||
var students = _students.GetByGroup(_groupId, _schoolYear);
|
||||
var entries = _entries.GetBySession(sessionId);
|
||||
|
||||
foreach (var s in students)
|
||||
{
|
||||
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);
|
||||
StudentRows.Add(row);
|
||||
}
|
||||
QuickInputCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void SaveRating(Guid sessionId, Guid studentId, string key, 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.Ratings.FirstOrDefault(r => r.Key == key);
|
||||
if (value is null)
|
||||
{
|
||||
if (existing is not null) entry.Ratings.Remove(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (existing is null) entry.Ratings.Add(new AspectRating { Key = key, Value = value.Value });
|
||||
else existing.Value = value.Value;
|
||||
}
|
||||
_entries.Save(entry);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddSession()
|
||||
{
|
||||
if (OnAddSession is null) return;
|
||||
var session = await OnAddSession();
|
||||
if (session is null) return;
|
||||
session.GroupId = _groupId;
|
||||
_sessions.Save(session);
|
||||
LoadSessions();
|
||||
SelectedSession = Sessions.FirstOrDefault(s => s.Id == session.Id);
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanQuickInput))]
|
||||
private async Task QuickInput()
|
||||
{
|
||||
if (OnQuickInput is null) return;
|
||||
await OnQuickInput(this);
|
||||
if (SelectedSession is not null) LoadGrid(SelectedSession.Id);
|
||||
}
|
||||
|
||||
private bool CanQuickInput() => SelectedSession is not null && StudentRows.Count > 0;
|
||||
|
||||
[RelayCommand]
|
||||
private void DeleteSession()
|
||||
{
|
||||
if (SelectedSession is null) return;
|
||||
_sessions.Delete(SelectedSession.Id);
|
||||
LoadSessions();
|
||||
}
|
||||
|
||||
public void SaveNote(Guid sessionId, Guid studentId, string note)
|
||||
{
|
||||
var entry = _entries.GetBySessionAndStudent(sessionId, studentId);
|
||||
if (entry is null) return;
|
||||
entry.Note = note;
|
||||
_entries.Save(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Zeilendaten für das Bewertungsraster ─────────────────────────────────────
|
||||
|
||||
public partial class ParticipationStudentRow : ObservableObject
|
||||
{
|
||||
public Guid StudentId { get; }
|
||||
public string Name { get; }
|
||||
|
||||
private readonly ParticipationEntry _entry;
|
||||
private readonly IReadOnlyList<AspectColumnDef> _aspectDefs;
|
||||
|
||||
public ObservableCollection<RatingCell> Cells { get; } = [];
|
||||
public Action<Guid, string, int?>? OnRatingChanged { get; set; }
|
||||
|
||||
public ParticipationStudentRow(Guid id, string name, ParticipationEntry entry, List<AspectColumnDef> aspects)
|
||||
{
|
||||
StudentId = id;
|
||||
Name = name;
|
||||
_entry = entry;
|
||||
_aspectDefs = aspects;
|
||||
|
||||
foreach (var a in aspects)
|
||||
{
|
||||
var existing = entry.Ratings.FirstOrDefault(r => r.Key == a.Key);
|
||||
var cell = new RatingCell(id, a.Key, existing?.Value);
|
||||
cell.OnChanged = (sid, key, val) => OnRatingChanged?.Invoke(sid, key, val);
|
||||
Cells.Add(cell);
|
||||
}
|
||||
}
|
||||
|
||||
public int? GetRating(string key) =>
|
||||
_entry.Ratings.FirstOrDefault(r => r.Key == key)?.Value;
|
||||
|
||||
public void SetRating(string key, int? value)
|
||||
{
|
||||
var cell = Cells.FirstOrDefault(c => c.AspectKey == key);
|
||||
cell?.SetValue(value);
|
||||
OnRatingChanged?.Invoke(StudentId, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Eine Bewertungszelle ──────────────────────────────────────────────────────
|
||||
|
||||
public partial class RatingCell : ObservableObject
|
||||
{
|
||||
public Guid StudentId { get; }
|
||||
public string AspectKey { get; }
|
||||
|
||||
[ObservableProperty] private int? _value;
|
||||
[ObservableProperty] private string _displayLabel = "";
|
||||
|
||||
public Action<Guid, string, int?>? OnChanged { get; set; }
|
||||
|
||||
public RatingCell(Guid studentId, string key, int? value)
|
||||
{
|
||||
StudentId = studentId;
|
||||
AspectKey = key;
|
||||
_value = value;
|
||||
UpdateLabel();
|
||||
}
|
||||
|
||||
public void SetValue(int? value)
|
||||
{
|
||||
Value = value;
|
||||
UpdateLabel();
|
||||
OnChanged?.Invoke(StudentId, AspectKey, value);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CycleUp()
|
||||
{
|
||||
var next = Value is null ? -2 : Math.Min(2, Value.Value + 1);
|
||||
SetValue(next);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CycleDown()
|
||||
{
|
||||
var next = Value is null ? 2 : Math.Max(-2, Value.Value - 1);
|
||||
SetValue(next);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Clear() => SetValue(null);
|
||||
|
||||
private void UpdateLabel() => DisplayLabel = Value switch
|
||||
{
|
||||
2 => "++",
|
||||
1 => "+",
|
||||
0 => "~",
|
||||
-1 => "−",
|
||||
-2 => "−−",
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
|
||||
// ── Hilfsklassen ──────────────────────────────────────────────────────────────
|
||||
|
||||
public class AspectColumnDef
|
||||
{
|
||||
public string Key { get; }
|
||||
public string Label { get; }
|
||||
public AspectColumnDef(ParticipationAspect a) { Key = a.Key; Label = a.Label; }
|
||||
}
|
||||
|
||||
public class ParticipationSessionItem
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public string Display { get; }
|
||||
public string Comment { get; }
|
||||
public DateOnly Date { get; }
|
||||
|
||||
public ParticipationSessionItem(ParticipationSession s)
|
||||
{
|
||||
Id = s.Id;
|
||||
Date = s.Date;
|
||||
Comment = s.Comment ?? "";
|
||||
Display = s.Comment is { Length: > 0 }
|
||||
? $"{s.Date:dd.MM.yyyy} – {s.Comment}"
|
||||
: s.Date.ToString("dd.MM.yyyy");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dialog: Sitzung anlegen ───────────────────────────────────────────────────
|
||||
|
||||
public partial class AddSessionDialogViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty] private DateOnly _date = DateOnly.FromDateTime(DateTime.Today);
|
||||
[ObservableProperty] private string _comment = "";
|
||||
[ObservableProperty] private string _dateText = DateOnly.FromDateTime(DateTime.Today).ToString("dd.MM.yyyy");
|
||||
[ObservableProperty] private string _validationMessage = "";
|
||||
|
||||
public ParticipationSession? Result { get; private set; }
|
||||
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
{
|
||||
if (!DateOnly.TryParseExact(DateText, "dd.MM.yyyy", null,
|
||||
System.Globalization.DateTimeStyles.None, out var date))
|
||||
{
|
||||
ValidationMessage = "Datum im Format TT.MM.JJJJ eingeben.";
|
||||
return;
|
||||
}
|
||||
Result = new ParticipationSession { Date = date, Comment = Comment.Trim() };
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dialog: Schnelleingabe ────────────────────────────────────────────────────
|
||||
|
||||
public partial class QuickInputViewModel : ObservableObject
|
||||
{
|
||||
private readonly List<ParticipationStudentRow> _rows;
|
||||
private readonly List<AspectColumnDef> _aspects;
|
||||
|
||||
[ObservableProperty] private int _studentIndex;
|
||||
[ObservableProperty] private int _aspectIndex;
|
||||
[ObservableProperty] private string _studentName = "";
|
||||
[ObservableProperty] private string _currentAspectLabel = "";
|
||||
[ObservableProperty] private string _currentValueLabel = "";
|
||||
[ObservableProperty] private string _progressText = "";
|
||||
|
||||
public ObservableCollection<QuickAspectRow> AspectRows { get; } = [];
|
||||
|
||||
public QuickInputViewModel(List<ParticipationStudentRow> rows, List<AspectColumnDef> aspects)
|
||||
{
|
||||
_rows = rows;
|
||||
_aspects = aspects;
|
||||
if (rows.Any()) ShowStudent(0);
|
||||
}
|
||||
|
||||
private void ShowStudent(int index)
|
||||
{
|
||||
if (index < 0 || index >= _rows.Count) return;
|
||||
StudentIndex = index;
|
||||
var row = _rows[index];
|
||||
StudentName = row.Name;
|
||||
ProgressText = $"{index + 1} / {_rows.Count}";
|
||||
|
||||
AspectRows.Clear();
|
||||
foreach (var (a, i) in _aspects.Select((a, i) => (a, i)))
|
||||
{
|
||||
var val = row.GetRating(a.Key);
|
||||
AspectRows.Add(new QuickAspectRow(i, a.Label, val, i == AspectIndex));
|
||||
}
|
||||
UpdateCurrentAspect();
|
||||
}
|
||||
|
||||
private void UpdateCurrentAspect()
|
||||
{
|
||||
if (!_aspects.Any()) return;
|
||||
var safeIdx = Math.Clamp(AspectIndex, 0, _aspects.Count - 1);
|
||||
CurrentAspectLabel = _aspects[safeIdx].Label;
|
||||
var row = AspectRows.ElementAtOrDefault(safeIdx);
|
||||
CurrentValueLabel = row?.DisplayLabel ?? "";
|
||||
foreach (var r in AspectRows) r.IsActive = r.Index == safeIdx;
|
||||
}
|
||||
|
||||
public void SetRatingByNumber(int num)
|
||||
{
|
||||
// 1=−−, 2=−, 3=~, 4=+, 5=++
|
||||
var val = num switch { 1 => -2, 2 => -1, 3 => 0, 4 => 1, 5 => 2, _ => (int?)null };
|
||||
if (val is null) return;
|
||||
ApplyRating(val.Value);
|
||||
}
|
||||
|
||||
public void IncrementRating()
|
||||
{
|
||||
var cell = GetCurrentCell();
|
||||
if (cell is null) return;
|
||||
var next = cell.Value is null ? -2 : Math.Min(2, cell.Value.Value + 1);
|
||||
ApplyRating(next);
|
||||
}
|
||||
|
||||
public void DecrementRating()
|
||||
{
|
||||
var cell = GetCurrentCell();
|
||||
if (cell is null) return;
|
||||
var next = cell.Value is null ? 2 : Math.Max(-2, cell.Value.Value - 1);
|
||||
ApplyRating(next);
|
||||
}
|
||||
|
||||
private void ApplyRating(int val)
|
||||
{
|
||||
if (_rows.Count == 0 || !_aspects.Any()) return;
|
||||
var key = _aspects[Math.Clamp(AspectIndex, 0, _aspects.Count - 1)].Key;
|
||||
_rows[StudentIndex].SetRating(key, val);
|
||||
var row = AspectRows.ElementAtOrDefault(AspectIndex);
|
||||
if (row is not null)
|
||||
{
|
||||
row.Value = val;
|
||||
row.UpdateLabel();
|
||||
}
|
||||
CurrentValueLabel = RatingLabel(val);
|
||||
}
|
||||
|
||||
public void SelectAspect(int index)
|
||||
{
|
||||
if (index < 0 || index >= _aspects.Count) return;
|
||||
AspectIndex = index;
|
||||
UpdateCurrentAspect();
|
||||
}
|
||||
|
||||
public void NextAspect()
|
||||
{
|
||||
AspectIndex = (AspectIndex + 1) % _aspects.Count;
|
||||
UpdateCurrentAspect();
|
||||
}
|
||||
|
||||
public void NextStudent()
|
||||
{
|
||||
if (StudentIndex >= _rows.Count - 1) return;
|
||||
AspectIndex = 0;
|
||||
ShowStudent(StudentIndex + 1);
|
||||
}
|
||||
|
||||
public void PreviousStudent()
|
||||
{
|
||||
if (StudentIndex <= 0) return;
|
||||
AspectIndex = 0;
|
||||
ShowStudent(StudentIndex - 1);
|
||||
}
|
||||
|
||||
public bool IsLastStudent => StudentIndex >= _rows.Count - 1;
|
||||
|
||||
private RatingCell? GetCurrentCell()
|
||||
{
|
||||
if (_rows.Count == 0 || !_aspects.Any()) return null;
|
||||
var key = _aspects[Math.Clamp(AspectIndex, 0, _aspects.Count - 1)].Key;
|
||||
return _rows[StudentIndex].Cells.FirstOrDefault(c => c.AspectKey == key);
|
||||
}
|
||||
|
||||
private static string RatingLabel(int? v) => v switch
|
||||
{
|
||||
2 => "++", 1 => "+", 0 => "~", -1 => "−", -2 => "−−", _ => "",
|
||||
};
|
||||
}
|
||||
|
||||
public partial class QuickAspectRow : ObservableObject
|
||||
{
|
||||
public int Index { get; }
|
||||
public string Label { get; }
|
||||
[ObservableProperty] private bool _isActive;
|
||||
[ObservableProperty] private string _displayLabel = "";
|
||||
public int? Value { get; set; }
|
||||
|
||||
public QuickAspectRow(int index, string label, int? value, bool isActive)
|
||||
{
|
||||
Index = index;
|
||||
Label = label;
|
||||
Value = value;
|
||||
IsActive = isActive;
|
||||
UpdateLabel();
|
||||
}
|
||||
|
||||
public void UpdateLabel() => DisplayLabel = Value switch
|
||||
{
|
||||
2 => "++", 1 => "+", 0 => "~", -1 => "−", -2 => "−−", _ => "·",
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user