KI-Backend: Antwortretten - Funktion
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-09-04 12:10:42 +02:00
parent 7ef0c95a73
commit fedfceb81d
10 changed files with 435 additions and 14 deletions
@@ -78,8 +78,12 @@
<TextBlock Text="Anfrage läuft…" FontSize="12" Opacity="0.6"/>
<ProgressBar IsIndeterminate="True" Height="4"/>
</StackPanel>
<TextBlock Text="{Binding ErrorMessage}" Foreground="Red" FontSize="12" TextWrapping="Wrap"
IsVisible="{Binding ErrorMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<StackPanel Spacing="8" IsVisible="{Binding ErrorMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding ErrorMessage}" Foreground="Red" FontSize="12" TextWrapping="Wrap"/>
<Button Content="🛟 Anfrage retten…" HorizontalAlignment="Left" Click="OnRescue"
IsVisible="{Binding CanRescueResponse}"
ToolTip.Tip="Vollständige KI-Antwort öffnen, JSON markieren oder korrigieren und manuell importieren."/>
</StackPanel>
</StackPanel>
</ScrollViewer>
@@ -27,6 +27,22 @@ public partial class AiAssistDialog : Window
await vm.SendCommand.ExecuteAsync(null);
}
private async void OnRescue(object? s, RoutedEventArgs e)
{
if (DataContext is not AiAssistDialogViewModel { RawResponse: { } raw } vm) return;
var rescueVm = new AiResponseRescueDialogViewModel(
App.Services.GetRequiredService<AiPlanningService>(),
App.Services.GetRequiredService<LehrerApp.Core.Interfaces.ILessonRepository>(),
vm.Unit, raw, vm.FocusLessonId);
var rescue = new AiResponseRescueDialog { DataContext = rescueVm };
if (await rescue.ShowDialog<bool>(this))
{
vm.MarkRescueImported();
Close(true);
}
}
private void OnApply(object? s, RoutedEventArgs e)
{
if (DataContext is AiAssistDialogViewModel vm && vm.ApplyCommand.CanExecute(null))
@@ -0,0 +1,50 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:LehrerApp.Desktop.ViewModels.Groups"
x:Class="LehrerApp.Desktop.Views.Groups.AiResponseRescueDialog"
x:DataType="vm:AiResponseRescueDialogViewModel"
Title="KI-Antwort retten"
Width="820" Height="720" MinWidth="620" MinHeight="520"
CanResize="True" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,*,Auto,Auto,Auto" Margin="24">
<StackPanel Grid.Row="0" Spacing="6" Margin="0,0,0,12">
<TextBlock Text="KI-Antwort retten" Classes="dialogtitle"/>
<TextBlock Text="Die vollständige Antwort steht unten. Markiere ein vollständiges JSON-Objekt, ein Array von Stunden oder die normale Antwort mit dem Feld „lessons“. Du kannst den Text vorher auch korrigieren. Ohne Markierung wird der gesamte Text geprüft."
TextWrapping="Wrap" FontSize="12" Opacity="0.75"/>
</StackPanel>
<TextBox x:Name="ResponseTextBox" Grid.Row="1" Text="{Binding ResponseText}"
AcceptsReturn="True" AcceptsTab="True" TextWrapping="NoWrap"
FontFamily="Monospace" FontSize="12"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<Grid Grid.Row="2" ColumnDefinitions="Auto,*" Margin="0,12,0,0">
<Button Grid.Column="0" Content="Markierung prüfen" Click="OnValidateSelection"/>
<TextBlock Grid.Column="1" Text="{Binding ParseMessage}" Margin="12,0,0,0"
VerticalAlignment="Center" TextWrapping="Wrap" FontSize="12"/>
</Grid>
<Grid Grid.Row="3" ColumnDefinitions="*,12,*" Margin="0,14,0,0">
<StackPanel Grid.Column="0" Spacing="5">
<TextBlock Text="Erkannte Stunde" FontSize="12" Opacity="0.7"/>
<ComboBox ItemsSource="{Binding ParsedLessons}" SelectedItem="{Binding SelectedParsedLesson}"
DisplayMemberBinding="{Binding Label}" IsEnabled="{Binding HasParsedLesson}"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="5">
<TextBlock Text="Vorhandene Zielstunde" FontSize="12" Opacity="0.7"/>
<ComboBox ItemsSource="{Binding ExistingLessons}" SelectedItem="{Binding SelectedTarget}"
DisplayMemberBinding="{Binding Label}"/>
</StackPanel>
</Grid>
<Grid Grid.Row="4" ColumnDefinitions="Auto,*,Auto,10,Auto" Margin="0,20,0,0">
<Button Grid.Column="0" Content="Abbrechen" Click="OnCancel"/>
<Button Grid.Column="2" Content="In Zielstunde importieren" Click="OnImportIntoExisting"
IsEnabled="{Binding CanImportIntoExisting}"/>
<Button Grid.Column="4" Content="Als neue Stunde importieren" Click="OnImportAsNew"
IsEnabled="{Binding HasParsedLesson}"/>
</Grid>
</Grid>
</Window>
@@ -0,0 +1,35 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using LehrerApp.Desktop.ViewModels.Groups;
namespace LehrerApp.Desktop.Views.Groups;
public partial class AiResponseRescueDialog : Window
{
public AiResponseRescueDialog() => InitializeComponent();
private void OnValidateSelection(object? sender, RoutedEventArgs e)
{
if (DataContext is not AiResponseRescueDialogViewModel vm) return;
var candidate = string.IsNullOrWhiteSpace(ResponseTextBox.SelectedText)
? ResponseTextBox.Text ?? ""
: ResponseTextBox.SelectedText;
vm.ParseSelection(candidate);
}
private void OnImportIntoExisting(object? sender, RoutedEventArgs e)
{
if (DataContext is not AiResponseRescueDialogViewModel vm || !vm.CanImportIntoExisting) return;
vm.ImportIntoExistingCommand.Execute(null);
if (vm.Result) Close(true);
}
private void OnImportAsNew(object? sender, RoutedEventArgs e)
{
if (DataContext is not AiResponseRescueDialogViewModel vm || !vm.HasParsedLesson) return;
vm.ImportAsNewCommand.Execute(null);
if (vm.Result) Close(true);
}
private void OnCancel(object? sender, RoutedEventArgs e) => Close(false);
}