Kompetenzen fast fertig

This commit is contained in:
2026-08-16 02:32:12 +02:00
parent 7c7fe6dfcb
commit 4437f951d9
22 changed files with 979 additions and 29 deletions
@@ -0,0 +1,29 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace LehrerApp.Desktop.ViewModels.Settings;
public partial class CompetencyCatalogCopyTargetViewModel : ObservableObject
{
[ObservableProperty] private int _targetGradeLevel;
[ObservableProperty] private string _error = "";
public string SourceLabel { get; }
public int SourceGradeLevel { get; }
public CompetencyCatalogCopyTargetViewModel(string subjectName, int sourceGradeLevel)
{
SourceGradeLevel = sourceGradeLevel;
SourceLabel = $"{subjectName} · Klassenstufe {sourceGradeLevel}";
_targetGradeLevel = sourceGradeLevel < 13 ? sourceGradeLevel + 1 : sourceGradeLevel - 1;
}
public bool Validate()
{
Error = TargetGradeLevel == SourceGradeLevel
? "Quell- und Zielklassenstufe müssen verschieden sein."
: TargetGradeLevel is < 1 or > 13
? "Die Klassenstufe muss zwischen 1 und 13 liegen."
: "";
return Error.Length == 0;
}
}
@@ -16,6 +16,7 @@ public partial class CompetencyCatalogImportViewModel : ObservableObject
public IReadOnlyList<ImportModeOption> Modes { get; }
public ObservableCollection<CompetencyImportConflictItem> Conflicts { get; } = [];
public IReadOnlyList<string> Warnings => _preview.Warnings;
public string Heading { get; }
public string Target => $"{_preview.SubjectName} · Klassenstufe {_preview.GradeLevel}";
public string Summary =>
$"{_preview.NewDomains} neue Bereiche · {_preview.NewCompetencies} neue Kompetenzen · " +
@@ -26,10 +27,12 @@ public partial class CompetencyCatalogImportViewModel : ObservableObject
public bool IsReplace => SelectedMode.Mode == CompetencyCatalogImportMode.Replace;
public CompetencyCatalogImportViewModel(
SettingsViewModel settings, CompetencyCatalogImportPreview preview)
SettingsViewModel settings, CompetencyCatalogImportPreview preview,
string heading = "Import prüfen")
{
_settings = settings;
_preview = preview;
Heading = heading;
Modes =
[
new(CompetencyCatalogImportMode.Merge, "Zusammenführen (empfohlen)"),
@@ -733,7 +733,8 @@ public partial class SettingsViewModel : ObservableObject
CatalogValidation = "";
if (CatalogSubject is null) return;
foreach (var d in _domainRepo.GetBySubjectAndGrade(CatalogSubject.Id, CatalogGradeLevel))
Domains.Add(new DomainEditItem(d, _domainRepo));
Domains.Add(CreateDomainEditItem(d));
RefreshDomainMoveState();
}
// ── Katalog: Bereich hinzufügen / löschen ────────────────────────────────
@@ -753,7 +754,8 @@ public partial class SettingsViewModel : ObservableObject
SortOrder = Domains.Count,
};
_domainRepo.Save(domain);
Domains.Add(new DomainEditItem(domain, _domainRepo));
Domains.Add(CreateDomainEditItem(domain));
RefreshDomainMoveState();
NewDomainName = ""; NewDomainCode = ""; CatalogValidation = "";
}
@@ -763,6 +765,31 @@ public partial class SettingsViewModel : ObservableObject
if (item is null) return;
_domainRepo.Delete(item.Id);
Domains.Remove(item);
PersistDomainOrder();
}
private DomainEditItem CreateDomainEditItem(CompetencyDomain domain) =>
new(domain, _domainRepo, item => MoveDomain(item, -1), item => MoveDomain(item, 1));
private void MoveDomain(DomainEditItem item, int offset)
{
var oldIndex = Domains.IndexOf(item);
var newIndex = oldIndex + offset;
if (oldIndex < 0 || newIndex < 0 || newIndex >= Domains.Count) return;
Domains.Move(oldIndex, newIndex);
PersistDomainOrder();
}
private void PersistDomainOrder()
{
for (var i = 0; i < Domains.Count; i++) Domains[i].SetSortOrder(i);
RefreshDomainMoveState();
}
private void RefreshDomainMoveState()
{
for (var i = 0; i < Domains.Count; i++)
Domains[i].SetMoveState(i > 0, i < Domains.Count - 1);
}
// ── JSON Import / Export ──────────────────────────────────────────────────
@@ -798,11 +825,40 @@ public partial class SettingsViewModel : ObservableObject
}
public string ExportCatalog()
=> SerializeCatalog(CatalogGradeLevel);
public CompetencyCatalogImportPreview? PrepareCatalogCopy(int targetGradeLevel)
{
if (CatalogSubject is null)
{
CatalogValidation = "Bitte zuerst ein Fach auswählen.";
return null;
}
if (Domains.Count == 0)
{
CatalogValidation = "Der ausgewählte Katalog enthält keine Bereiche.";
return null;
}
if (targetGradeLevel is < 1 or > 13 || targetGradeLevel == CatalogGradeLevel)
{
CatalogValidation = "Bitte eine andere Zielklassenstufe zwischen 1 und 13 auswählen.";
return null;
}
CatalogValidation = "";
return _catalogImport.Analyze(SerializeCatalog(targetGradeLevel), CatalogSubject.Id,
CatalogSubject.Name, targetGradeLevel);
}
public void SetCatalogCopyStatus(int targetGradeLevel) =>
CatalogValidation = $"Kompetenzkatalog wurde in Klassenstufe {targetGradeLevel} kopiert.";
private string SerializeCatalog(int gradeLevel)
{
var dto = new CatalogDto
{
Subject = CatalogSubject?.Name ?? "",
GradeLevel = CatalogGradeLevel,
GradeLevel = gradeLevel,
Domains = Domains.Select(d => new DomainDto
{
Name = d.Name,
@@ -834,8 +890,11 @@ public partial class DomainEditItem : ObservableObject
[ObservableProperty] private string _newItemDesc = "";
public ObservableCollection<CompetencyItemVm> Items { get; } = [];
public IRelayCommand MoveUpCommand { get; }
public IRelayCommand MoveDownCommand { get; }
public DomainEditItem(CompetencyDomain domain, ICompetencyDomainRepository repo)
public DomainEditItem(CompetencyDomain domain, ICompetencyDomainRepository repo,
Action<DomainEditItem>? onMoveUp = null, Action<DomainEditItem>? onMoveDown = null)
{
_domain = domain;
_repo = repo;
@@ -846,8 +905,31 @@ public partial class DomainEditItem : ObservableObject
? domain.Name
: $"{domain.Name} ({domain.Code})";
foreach (var item in domain.Items.OrderBy(i => i.SortOrder))
Items.Add(new CompetencyItemVm(item, DeleteItem));
MoveUpCommand = new RelayCommand(() => onMoveUp?.Invoke(this), () => _canMoveUp);
MoveDownCommand = new RelayCommand(() => onMoveDown?.Invoke(this), () => _canMoveDown);
_domain.Items = domain.Items.OrderBy(i => i.SortOrder).ToList();
foreach (var item in _domain.Items)
Items.Add(CreateItemViewModel(item));
RefreshItemMoveState();
}
private bool _canMoveUp;
private bool _canMoveDown;
internal void SetMoveState(bool canMoveUp, bool canMoveDown)
{
_canMoveUp = canMoveUp;
_canMoveDown = canMoveDown;
MoveUpCommand.NotifyCanExecuteChanged();
MoveDownCommand.NotifyCanExecuteChanged();
}
internal void SetSortOrder(int sortOrder)
{
if (_domain.SortOrder == sortOrder) return;
_domain.SortOrder = sortOrder;
_repo.Save(_domain);
}
[RelayCommand]
@@ -862,15 +944,42 @@ public partial class DomainEditItem : ObservableObject
};
_domain.Items.Add(item);
_repo.Save(_domain);
Items.Add(new CompetencyItemVm(item, DeleteItem));
Items.Add(CreateItemViewModel(item));
RefreshItemMoveState();
NewItemCode = ""; NewItemDesc = "";
}
private void DeleteItem(CompetencyItemVm vm)
{
_domain.Items.RemoveAll(i => i.Id == vm.ItemId);
_repo.Save(_domain);
Items.Remove(vm);
PersistItemOrder();
}
private CompetencyItemVm CreateItemViewModel(CompetencyItem item) =>
new(item, DeleteItem, vm => MoveItem(vm, -1), vm => MoveItem(vm, 1));
private void MoveItem(CompetencyItemVm item, int offset)
{
var oldIndex = Items.IndexOf(item);
var newIndex = oldIndex + offset;
if (oldIndex < 0 || newIndex < 0 || newIndex >= Items.Count) return;
Items.Move(oldIndex, newIndex);
PersistItemOrder();
}
private void PersistItemOrder()
{
_domain.Items = Items.Select(x => x.Model).ToList();
for (var i = 0; i < _domain.Items.Count; i++) _domain.Items[i].SortOrder = i;
_repo.Save(_domain);
RefreshItemMoveState();
}
private void RefreshItemMoveState()
{
for (var i = 0; i < Items.Count; i++)
Items[i].SetMoveState(i > 0, i < Items.Count - 1);
}
}
@@ -878,14 +987,19 @@ public partial class DomainEditItem : ObservableObject
public class CompetencyItemVm
{
internal CompetencyItem Model { get; }
public Guid ItemId { get; }
public string Code { get; }
public string Description { get; }
public string Display { get; }
public IRelayCommand DeleteCommand { get; }
public IRelayCommand MoveUpCommand { get; }
public IRelayCommand MoveDownCommand { get; }
public CompetencyItemVm(CompetencyItem item, Action<CompetencyItemVm> onDelete)
public CompetencyItemVm(CompetencyItem item, Action<CompetencyItemVm> onDelete,
Action<CompetencyItemVm>? onMoveUp = null, Action<CompetencyItemVm>? onMoveDown = null)
{
Model = item;
ItemId = item.Id;
Code = item.Code;
Description = item.Description;
@@ -893,6 +1007,19 @@ public class CompetencyItemVm
? item.Description
: $"[{item.Code}] {item.Description}";
DeleteCommand = new RelayCommand(() => onDelete(this));
MoveUpCommand = new RelayCommand(() => onMoveUp?.Invoke(this), () => _canMoveUp);
MoveDownCommand = new RelayCommand(() => onMoveDown?.Invoke(this), () => _canMoveDown);
}
private bool _canMoveUp;
private bool _canMoveDown;
internal void SetMoveState(bool canMoveUp, bool canMoveDown)
{
_canMoveUp = canMoveUp;
_canMoveDown = canMoveDown;
MoveUpCommand.NotifyCanExecuteChanged();
MoveDownCommand.NotifyCanExecuteChanged();
}
}