feat: Importkonflikte gesammelt übernehmen

This commit is contained in:
2026-08-18 00:52:05 +02:00
parent 99efa3a0fe
commit 65c2211dab
4 changed files with 69 additions and 2 deletions
@@ -30,6 +30,7 @@ public partial class StudentImportDialogViewModel : ObservableObject
public ObservableCollection<StudentImportConflictItem> Conflicts { get; } = [];
public bool HasMessages => Messages.Count > 0;
public bool HasConflicts => Conflicts.Count > 0;
public bool CanUseExistingForAll => Conflicts.Any(conflict => conflict.HasSingleExistingStudentOption);
public bool IsReadyWithoutConflicts => !HasConflicts && _preview.CanApply;
public bool HasBlockingErrors => !_preview.CanApply;
public bool CanApply => _preview.CanApply && !IsBusy;
@@ -65,6 +66,16 @@ public partial class StudentImportDialogViewModel : ObservableObject
.ToList()
.AsReadOnly();
/// <summary>
/// Wählt für alle eindeutigen Schüler-Konflikte den jeweils vorhandenen Datensatz.
/// Mehrdeutige Treffer und Lerngruppen-Konflikte bleiben unverändert.
/// </summary>
public void UseExistingForAll()
{
foreach (var conflict in Conflicts)
conflict.TrySelectSingleExistingStudent();
}
public async Task<bool> TryApplyAsync()
{
Error = "";
@@ -116,6 +127,8 @@ public sealed class StudentImportMessageItem
public partial class StudentImportConflictItem : ObservableObject
{
private const string ExistingStudentOptionPrefix = "existing:";
[ObservableProperty] private StudentImportConflictOptionItem _selectedOption;
public string Id { get; }
@@ -125,6 +138,11 @@ public partial class StudentImportConflictItem : ObservableObject
public string ExistingValue { get; }
public string SourceReference { get; }
public IReadOnlyList<StudentImportConflictOptionItem> Options { get; }
public bool HasSingleExistingStudentOption => ExistingStudentOptions.Count == 1;
private IReadOnlyList<StudentImportConflictOptionItem> ExistingStudentOptions => Options
.Where(option => option.Id.StartsWith(ExistingStudentOptionPrefix, StringComparison.Ordinal))
.ToList();
public StudentImportConflictItem(ImportConflict conflict)
{
@@ -141,6 +159,14 @@ public partial class StudentImportConflictItem : ObservableObject
.AsReadOnly();
_selectedOption = Options.First(option => option.Id == conflict.DefaultOptionId);
}
public bool TrySelectSingleExistingStudent()
{
var options = ExistingStudentOptions;
if (options.Count != 1) return false;
SelectedOption = options[0];
return true;
}
}
public sealed record StudentImportConflictOptionItem(
@@ -34,8 +34,14 @@
</StackPanel>
<Grid Grid.Row="1" RowDefinitions="Auto,*" Margin="0,16,0,0">
<TextBlock Grid.Row="0" Text="Entscheidungen" FontSize="15" FontWeight="SemiBold"
Margin="0,0,0,8" IsVisible="{Binding HasConflicts}"/>
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" Margin="0,0,0,8"
IsVisible="{Binding HasConflicts}">
<TextBlock Grid.Column="0" Text="Entscheidungen" FontSize="15" FontWeight="SemiBold"
VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Übernehmen für alle" Click="OnUseExistingForAll"
IsVisible="{Binding CanUseExistingForAll}"
ToolTip.Tip="Wählt bei allen eindeutigen Treffern den jeweils vorhandenen Schüler aus."/>
</Grid>
<Border Grid.Row="1" IsVisible="{Binding IsReadyWithoutConflicts}"
Background="#1422A06B" CornerRadius="6" Padding="14"
@@ -14,5 +14,11 @@ public partial class StudentImportDialog : Window
Close(true);
}
private void OnUseExistingForAll(object? sender, RoutedEventArgs e)
{
if (DataContext is StudentImportDialogViewModel vm)
vm.UseExistingForAll();
}
private void OnCancel(object? sender, RoutedEventArgs e) => Close(false);
}