TemplateDesigner: UI update
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-08-30 22:47:53 +02:00
parent 9f28081931
commit 9dce6576e2
5 changed files with 166 additions and 111 deletions
@@ -55,6 +55,8 @@ public sealed class ProjectLifecycleTests
placeholder.Sample = "Deutsch"; placeholder.Sample = "Deutsch";
Assert.Same(placeholder, viewModel.SelectedPlaceholder); Assert.Same(placeholder, viewModel.SelectedPlaceholder);
Assert.True(viewModel.HasSelectedPlaceholder);
Assert.False(viewModel.HasNoSelectedPlaceholder);
var definition = Assert.Single(viewModel.BuildManifest().Placeholders); var definition = Assert.Single(viewModel.BuildManifest().Placeholders);
Assert.Equal("Sprache", definition.Name); Assert.Equal("Sprache", definition.Name);
Assert.Equal(LehrerApp.Templating.PlaceholderType.Text, definition.Type); Assert.Equal(LehrerApp.Templating.PlaceholderType.Text, definition.Type);
@@ -63,6 +65,8 @@ public sealed class ProjectLifecycleTests
viewModel.RemoveSelectedPlaceholder(); viewModel.RemoveSelectedPlaceholder();
Assert.Empty(viewModel.Placeholders); Assert.Empty(viewModel.Placeholders);
Assert.Null(viewModel.SelectedPlaceholder); Assert.Null(viewModel.SelectedPlaceholder);
Assert.False(viewModel.HasSelectedPlaceholder);
Assert.True(viewModel.HasNoSelectedPlaceholder);
} }
[Fact] [Fact]
+1
View File
@@ -2,6 +2,7 @@
x:Class="LehrerApp.TemplateDesigner.App" RequestedThemeVariant="Default"> x:Class="LehrerApp.TemplateDesigner.App" RequestedThemeVariant="Default">
<Application.Styles> <Application.Styles>
<FluentTheme/> <FluentTheme/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
<Style Selector="TextBlock.section"><Setter Property="FontWeight" Value="SemiBold"/><Setter Property="FontSize" Value="15"/></Style> <Style Selector="TextBlock.section"><Setter Property="FontWeight" Value="SemiBold"/><Setter Property="FontSize" Value="15"/></Style>
<Style Selector="TextBlock.label"><Setter Property="Opacity" Value="0.7"/><Setter Property="FontSize" Value="12"/></Style> <Style Selector="TextBlock.label"><Setter Property="Opacity" Value="0.7"/><Setter Property="FontSize" Value="12"/></Style>
</Application.Styles> </Application.Styles>
@@ -59,9 +59,17 @@ public partial class DesignerViewModel : ObservableObject
public Dictionary<string, byte[]> Assets { get; } = new(StringComparer.OrdinalIgnoreCase); public Dictionary<string, byte[]> Assets { get; } = new(StringComparer.OrdinalIgnoreCase);
public ObservableCollection<DesignerAsset> AssetItems { get; } = []; public ObservableCollection<DesignerAsset> AssetItems { get; } = [];
public ObservableCollection<StarterTemplateItem> StarterTemplates { get; } = []; public ObservableCollection<StarterTemplateItem> StarterTemplates { get; } = [];
public bool HasSelectedPlaceholder => SelectedPlaceholder is not null;
public bool HasNoSelectedPlaceholder => SelectedPlaceholder is null;
public DesignerViewModel() => SelectedPlaceholder = Placeholders.FirstOrDefault(); public DesignerViewModel() => SelectedPlaceholder = Placeholders.FirstOrDefault();
partial void OnSelectedPlaceholderChanged(DesignerPlaceholder? value)
{
OnPropertyChanged(nameof(HasSelectedPlaceholder));
OnPropertyChanged(nameof(HasNoSelectedPlaceholder));
}
public void Reset() public void Reset()
{ {
TemplateId = "neue-vorlage"; TemplateName = "Neue Vorlage"; Description = ""; TemplateId = "neue-vorlage"; TemplateName = "Neue Vorlage"; Description = "";
+126 -111
View File
@@ -42,123 +42,138 @@
</Grid> </Grid>
</Border> </Border>
<Grid Grid.Row="2" ColumnDefinitions="460,*,390"> <Grid Grid.Row="2" ColumnDefinitions="460,*,390">
<ScrollViewer Grid.Column="0" Padding="18"> <TabControl Grid.Column="0" Margin="10">
<StackPanel Spacing="14"> <TabItem Header="Projekt">
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Meine Ausgangsvorlagen" Classes="section"/> <ScrollViewer Padding="8">
<Button Grid.Column="1" Content="Importieren …" Click="OnImportStarterTemplate"/></Grid> <StackPanel Spacing="14">
<ComboBox ItemsSource="{Binding StarterTemplates}" SelectedItem="{Binding SelectedStarterTemplate}" <Grid ColumnDefinitions="*,Auto"><TextBlock Text="Meine Ausgangsvorlagen" Classes="section"/>
DisplayMemberBinding="{Binding Display}" PlaceholderText="Noch keine Ausgangsvorlage"/> <Button Grid.Column="1" Content="Importieren …" Click="OnImportStarterTemplate"/></Grid>
<TextBlock Text="{Binding SelectedStarterTemplate.Description}" FontSize="12" Opacity="0.7" <ComboBox ItemsSource="{Binding StarterTemplates}" SelectedItem="{Binding SelectedStarterTemplate}"
TextWrapping="Wrap"/> DisplayMemberBinding="{Binding Display}" PlaceholderText="Noch keine Ausgangsvorlage"/>
<Grid ColumnDefinitions="*,8,*,8,*" RowDefinitions="Auto,8,Auto,8,Auto"> <TextBlock Text="{Binding SelectedStarterTemplate.Description}" FontSize="12" Opacity="0.7" TextWrapping="Wrap"/>
<Button Grid.Column="0" Content="Vorschau" Click="OnPreviewStarterTemplate"/> <Grid ColumnDefinitions="*,8,*,8,*" RowDefinitions="Auto,8,Auto,8,Auto">
<Button Grid.Column="2" Content="Bearbeiten" Click="OnEditStarterTemplate"/> <Button Grid.Column="0" Content="Vorschau" Click="OnPreviewStarterTemplate"/>
<Button Grid.Column="4" Content="Als neues Projekt" Click="OnUseStarterTemplate"/> <Button Grid.Column="2" Content="Bearbeiten" Click="OnEditStarterTemplate"/>
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Content="Aktuelles Projekt speichern/aktualisieren" <Button Grid.Column="4" Content="Als neues Projekt" Click="OnUseStarterTemplate"/>
Click="OnSaveStarterTemplate"/> <Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Content="Projekt speichern/aktualisieren" Click="OnSaveStarterTemplate"/>
<Button Grid.Row="2" Grid.Column="4" Content="Duplizieren" Click="OnDuplicateStarterTemplate"/> <Button Grid.Row="2" Grid.Column="4" Content="Duplizieren" Click="OnDuplicateStarterTemplate"/>
<Button Grid.Row="4" Grid.Column="0" Content="Exportieren …" Click="OnExportStarterTemplate"/> <Button Grid.Row="4" Grid.Column="0" Content="Exportieren …" Click="OnExportStarterTemplate"/>
<Button Grid.Row="4" Grid.Column="2" Content="Löschen" Click="OnDeleteStarterTemplate"/> <Button Grid.Row="4" Grid.Column="2" Content="Löschen" Click="OnDeleteStarterTemplate"/>
</Grid>
<TextBlock Text="Ausgangsvorlagen werden lokal gespeichert. Beim Erstellen eines neuen Projekts bleiben Original und Briefkopf unverändert."
FontSize="11" Opacity="0.6" TextWrapping="Wrap"/>
<Separator/>
<TextBlock Text="Paket" Classes="section"/>
<Grid ColumnDefinitions="*,10,*" RowDefinitions="Auto,Auto">
<StackPanel><TextBlock Text="Stabile ID" Classes="label"/><TextBox Text="{Binding TemplateId}"/></StackPanel>
<StackPanel Grid.Column="2"><TextBlock Text="Name" Classes="label"/><TextBox Text="{Binding TemplateName}"/></StackPanel>
<StackPanel Grid.Row="1" Grid.ColumnSpan="3" Margin="0,8,0,0"><TextBlock Text="Beschreibung" Classes="label"/><TextBox Text="{Binding Description}"/></StackPanel>
</Grid>
<Grid ColumnDefinitions="*,8,*,8,*">
<StackPanel><TextBlock Text="Breite" Classes="label"/><NumericUpDown Value="{Binding PageWidth}" Minimum="10" Maximum="2000"/></StackPanel>
<StackPanel Grid.Column="2"><TextBlock Text="Höhe" Classes="label"/><NumericUpDown Value="{Binding PageHeight}" Minimum="10" Maximum="2000"/></StackPanel>
<StackPanel Grid.Column="4"><TextBlock Text="Einheit" Classes="label"/><ComboBox ItemsSource="{Binding Units}" SelectedItem="{Binding Unit}"/></StackPanel>
</Grid>
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Freie Metadaten" Classes="section"/>
<Button Grid.Column="1" Content="" Click="OnAddMetadata"/></Grid>
<DataGrid ItemsSource="{Binding MetadataItems}" SelectedItem="{Binding SelectedMetadata}"
AutoGenerateColumns="False" Height="125" CanUserResizeColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="Wert" Binding="{Binding Value}" Width="1.4*"/>
</DataGrid.Columns>
</DataGrid>
<Grid ColumnDefinitions="Auto,*">
<Button Content="Markierten Eintrag entfernen" Click="OnRemoveMetadata"/>
<TextBlock Grid.Column="1" Margin="10,0,0,0" Text="z. B. language=de-DE · report-type=letter"
FontSize="11" Opacity="0.6" VerticalAlignment="Center" TextWrapping="Wrap"/>
</Grid>
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Platzhalter &amp; Beispieldaten" Classes="section"/>
<Button Grid.Column="1" Content=" Platzhalter" Click="OnAddPlaceholder"/></Grid>
<DataGrid x:Name="PlaceholderGrid" ItemsSource="{Binding Placeholders}" SelectedItem="{Binding SelectedPlaceholder}"
AutoGenerateColumns="False" Height="180" CanUserResizeColumns="True" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="105"/>
<DataGridCheckBoxColumn Header="Pflicht" Binding="{Binding Required}" Width="58"/>
<DataGridTextColumn Header="Beispiel" Binding="{Binding Sample}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
<Border Padding="10" BorderBrush="#CBD5E1" BorderThickness="1" CornerRadius="4">
<StackPanel Spacing="8">
<TextBlock Text="Ausgewählten Platzhalter bearbeiten" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="*,10,*">
<StackPanel><TextBlock Text="Name" Classes="label"/>
<TextBox Text="{Binding SelectedPlaceholder.Name, Mode=TwoWay}" PlaceholderText="z. B. Empfaenger"/></StackPanel>
<StackPanel Grid.Column="2"><TextBlock Text="Datentyp" Classes="label"/>
<ComboBox ItemsSource="{Binding PlaceholderTypes}"
SelectedItem="{Binding SelectedPlaceholder.Type, Mode=TwoWay}"/></StackPanel>
</Grid> </Grid>
<CheckBox Content="Pflichtwert" IsChecked="{Binding SelectedPlaceholder.Required, Mode=TwoWay}"/> <Separator/>
<StackPanel><TextBlock Text="Beispielwert für die Vorschau" Classes="label"/> <TextBlock Text="Paketdaten" Classes="section"/>
<TextBox Text="{Binding SelectedPlaceholder.Sample, Mode=TwoWay}" AcceptsReturn="True" <Grid ColumnDefinitions="*,10,*" RowDefinitions="Auto,Auto">
MinHeight="58" TextWrapping="Wrap" PlaceholderText="Beispieldaten eingeben"/></StackPanel> <StackPanel><TextBlock Text="Stabile ID" Classes="label"/><TextBox Text="{Binding TemplateId}"/></StackPanel>
<Button Content="Ausgewählten Platzhalter entfernen" HorizontalAlignment="Left" <StackPanel Grid.Column="2"><TextBlock Text="Name" Classes="label"/><TextBox Text="{Binding TemplateName}"/></StackPanel>
Click="OnRemovePlaceholder"/> <StackPanel Grid.Row="1" Grid.ColumnSpan="3" Margin="0,8,0,0"><TextBlock Text="Beschreibung" Classes="label"/><TextBox Text="{Binding Description}"/></StackPanel>
</Grid>
<Grid ColumnDefinitions="*,8,*,8,*">
<StackPanel><TextBlock Text="Breite" Classes="label"/><NumericUpDown Value="{Binding PageWidth}" Minimum="10" Maximum="2000"/></StackPanel>
<StackPanel Grid.Column="2"><TextBlock Text="Höhe" Classes="label"/><NumericUpDown Value="{Binding PageHeight}" Minimum="10" Maximum="2000"/></StackPanel>
<StackPanel Grid.Column="4"><TextBlock Text="Einheit" Classes="label"/><ComboBox ItemsSource="{Binding Units}" SelectedItem="{Binding Unit}"/></StackPanel>
</Grid>
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Freie Metadaten" Classes="section"/>
<Button Grid.Column="1" Content="" Click="OnAddMetadata"/></Grid>
<DataGrid ItemsSource="{Binding MetadataItems}" SelectedItem="{Binding SelectedMetadata}"
AutoGenerateColumns="False" Height="150" CanUserResizeColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=TwoWay}" Width="*"/>
<DataGridTextColumn Header="Wert" Binding="{Binding Value, Mode=TwoWay}" Width="1.4*"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Markierten Metadateneintrag entfernen" HorizontalAlignment="Left" Click="OnRemoveMetadata"/>
</StackPanel> </StackPanel>
</Border> </ScrollViewer>
</TabItem>
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Paket-Assets" Classes="section"/> <TabItem Header="Platzhalter">
<Button Grid.Column="1" Content=" Bild importieren …" Click="OnImportAsset"/></Grid> <Grid Margin="8" RowDefinitions="Auto,*,Auto,Auto">
<DataGrid ItemsSource="{Binding AssetItems}" SelectedItem="{Binding SelectedAsset}" <Grid ColumnDefinitions="*,Auto"><TextBlock Text="Platzhalter" Classes="section" VerticalAlignment="Center"/>
AutoGenerateColumns="False" Height="150" IsReadOnly="True" CanUserResizeColumns="True"> <Button Grid.Column="1" Content=" Neu" Click="OnAddPlaceholder"/></Grid>
<DataGrid.Columns> <DataGrid Grid.Row="1" Margin="0,10" ItemsSource="{Binding Placeholders}"
<DataGridTextColumn Header="Datei" Binding="{Binding Name}" Width="*"/> SelectedItem="{Binding SelectedPlaceholder, Mode=TwoWay}" AutoGenerateColumns="False"
<DataGridTextColumn Header="Auflösung" Binding="{Binding Dimensions}" Width="125"/> CanUserResizeColumns="True" IsReadOnly="True">
<DataGridTextColumn Header="Größe" Binding="{Binding FileSize}" Width="70"/> <DataGrid.Columns>
<DataGridTextColumn Header="Verwendung" Binding="{Binding Usage}" Width="105"/> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
</DataGrid.Columns> <DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="105"/>
</DataGrid> <DataGridCheckBoxColumn Header="Pflicht" Binding="{Binding Required}" Width="65"/>
<Grid ColumnDefinitions="*,8,*,8,*"> </DataGrid.Columns>
<Button Grid.Column="0" Content="Als IMG einfügen" Click="OnInsertSelectedAsset"/> </DataGrid>
<Button Grid.Column="2" Content="Ersetzen …" Click="OnReplaceSelectedAsset"/> <TextBlock Grid.Row="2" IsVisible="{Binding HasNoSelectedPlaceholder}"
<Button Grid.Column="4" Content="Entfernen" Click="OnRemoveSelectedAsset"/> Text="Wähle einen Platzhalter aus oder lege einen neuen an." Opacity="0.65"
TextWrapping="Wrap" Margin="0,4,0,10"/>
<Border Grid.Row="3" IsVisible="{Binding HasSelectedPlaceholder}" Padding="12"
BorderBrush="#CBD5E1" BorderThickness="1" CornerRadius="4">
<StackPanel Spacing="8">
<TextBlock Text="Auswahl bearbeiten" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="*,10,*">
<StackPanel><TextBlock Text="Name" Classes="label"/>
<TextBox Text="{Binding SelectedPlaceholder.Name, Mode=TwoWay}" PlaceholderText="z. B. Empfaenger"/></StackPanel>
<StackPanel Grid.Column="2"><TextBlock Text="Datentyp" Classes="label"/>
<ComboBox ItemsSource="{Binding PlaceholderTypes}" SelectedItem="{Binding SelectedPlaceholder.Type, Mode=TwoWay}"/></StackPanel>
</Grid>
<CheckBox Content="Pflichtwert" IsChecked="{Binding SelectedPlaceholder.Required, Mode=TwoWay}"/>
<StackPanel><TextBlock Text="Beispielwert für die Vorschau" Classes="label"/>
<TextBox Text="{Binding SelectedPlaceholder.Sample, Mode=TwoWay}" AcceptsReturn="True"
MinHeight="68" MaxHeight="120" TextWrapping="Wrap" PlaceholderText="Beispieldaten eingeben"/></StackPanel>
<Button Content="Platzhalter entfernen" HorizontalAlignment="Left" Click="OnRemovePlaceholder"/>
</StackPanel>
</Border>
</Grid> </Grid>
</TabItem>
<TextBlock Text="Element hinzufügen" Classes="section"/> <TabItem Header="Bilder">
<ComboBox ItemsSource="{Binding ElementTypes}" SelectedItem="{Binding NewElementType}"/> <Grid Margin="8" RowDefinitions="Auto,*,Auto,Auto">
<Grid ColumnDefinitions="*,6,*,6,*,6,*"> <Grid ColumnDefinitions="*,Auto"><TextBlock Text="Paket-Assets" Classes="section" VerticalAlignment="Center"/>
<StackPanel><TextBlock Text="X" Classes="label"/><TextBox Text="{Binding NewX}"/></StackPanel> <Button Grid.Column="1" Content=" Importieren …" Click="OnImportAsset"/></Grid>
<StackPanel Grid.Column="2"><TextBlock Text="Y" Classes="label"/><TextBox Text="{Binding NewY}"/></StackPanel> <DataGrid Grid.Row="1" Margin="0,10" ItemsSource="{Binding AssetItems}" SelectedItem="{Binding SelectedAsset}"
<StackPanel Grid.Column="4"><TextBlock Text="Breite" Classes="label"/><TextBox Text="{Binding NewWidth}"/></StackPanel> AutoGenerateColumns="False" IsReadOnly="True" CanUserResizeColumns="True">
<StackPanel Grid.Column="6"><TextBlock Text="Höhe" Classes="label"/><TextBox Text="{Binding NewHeight}"/></StackPanel> <DataGrid.Columns>
<DataGridTextColumn Header="Datei" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="Auflösung" Binding="{Binding Dimensions}" Width="110"/>
<DataGridTextColumn Header="Verwendung" Binding="{Binding Usage}" Width="105"/>
</DataGrid.Columns>
</DataGrid>
<Grid Grid.Row="2" ColumnDefinitions="*,8,*,8,*">
<Button Grid.Column="0" Content="Als IMG einfügen" Click="OnInsertSelectedAsset"/>
<Button Grid.Column="2" Content="Ersetzen …" Click="OnReplaceSelectedAsset"/>
<Button Grid.Column="4" Content="Entfernen" Click="OnRemoveSelectedAsset"/>
</Grid>
<StackPanel Grid.Row="3" Margin="0,14,0,0" Spacing="8">
<TextBlock Text="Seitenhintergrund" Classes="section"/>
<Button Content="PNG/JPEG als Hintergrund importieren …" Click="OnImportBackground"/>
<Button Content="PDF als Hintergrund (300 DPI) importieren …" Click="OnImportPdfBackground"/>
</StackPanel>
</Grid> </Grid>
<StackPanel> </TabItem>
<TextBlock Text="IMG-Skalierung in % (nur für IMG)" Classes="label"/>
<TextBox Text="{Binding NewImageScale}" PlaceholderText="100"/> <TabItem Header="Elemente">
</StackPanel> <ScrollViewer Padding="8">
<StackPanel><TextBlock Text="Inhalt / $Platzhalter / Assetpfad" Classes="label"/><TextBox Text="{Binding NewContent}" PlaceholderText="$Brieftext oder ein Literal"/></StackPanel> <StackPanel Spacing="12">
<StackPanel><TextBlock Text="Attribute" Classes="label"/><TextBox Text="{Binding NewAttributes}" PlaceholderText="size=11 bold=true"/></StackPanel> <TextBlock Text="Element hinzufügen" Classes="section"/>
<Button Content="Element ins Layout übernehmen" Click="OnAddElement"/> <StackPanel><TextBlock Text="Elementtyp" Classes="label"/>
<Button Content="PNG/JPEG als Hintergrund importieren …" Click="OnImportBackground"/> <ComboBox ItemsSource="{Binding ElementTypes}" SelectedItem="{Binding NewElementType}"/></StackPanel>
<Button Content="PDF als Hintergrund (300 DPI) importieren …" Click="OnImportPdfBackground"/> <Grid ColumnDefinitions="*,6,*,6,*,6,*">
<TextBlock Text="Bekannte Einschränkung: TEXTBOX-Überlauf wird in v1 nicht automatisch auf Folgeseiten verteilt." <StackPanel><TextBlock Text="X" Classes="label"/><TextBox Text="{Binding NewX}"/></StackPanel>
TextWrapping="Wrap" Foreground="#B45309" FontSize="12"/> <StackPanel Grid.Column="2"><TextBlock Text="Y" Classes="label"/><TextBox Text="{Binding NewY}"/></StackPanel>
</StackPanel> <StackPanel Grid.Column="4"><TextBlock Text="Breite" Classes="label"/><TextBox Text="{Binding NewWidth}"/></StackPanel>
</ScrollViewer> <StackPanel Grid.Column="6"><TextBlock Text="Höhe" Classes="label"/><TextBox Text="{Binding NewHeight}"/></StackPanel>
</Grid>
<StackPanel><TextBlock Text="IMG-Skalierung in % (nur für IMG)" Classes="label"/>
<TextBox Text="{Binding NewImageScale}" PlaceholderText="100"/></StackPanel>
<StackPanel><TextBlock Text="Inhalt / $Platzhalter / Assetpfad" Classes="label"/>
<TextBox Text="{Binding NewContent}" PlaceholderText="$Brieftext oder ein Literal"/></StackPanel>
<StackPanel><TextBlock Text="Attribute" Classes="label"/>
<TextBox Text="{Binding NewAttributes}" PlaceholderText="size=11 bold=true"/></StackPanel>
<Button Content="Element ins Layout übernehmen" Click="OnAddElement"/>
<TextBlock Text="Tipp: Koordinaten können rechts im Messmodus direkt aus der Vorschau übernommen werden."
TextWrapping="Wrap" Opacity="0.65" FontSize="11"/>
<TextBlock Text="Bekannte Einschränkung: TEXTBOX-Überlauf wird in v1 nicht automatisch auf Folgeseiten verteilt."
TextWrapping="Wrap" Foreground="#B45309" FontSize="12"/>
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
<Grid Grid.Column="1" RowDefinitions="Auto,*" Margin="0,18"> <Grid Grid.Column="1" RowDefinitions="Auto,*" Margin="0,18">
<Grid ColumnDefinitions="*,Auto" Margin="12,0,12,10"><TextBlock Text="Layout-DSL" Classes="section"/> <Grid ColumnDefinitions="*,Auto" Margin="12,0,12,10"><TextBlock Text="Layout-DSL" Classes="section"/>
+27
View File
@@ -2329,6 +2329,33 @@ CSV-Exports der Auswertung (6.3.3) über die gemeinsame Export-Infrastruktur aus
welche TickTick-Liste ist die Zielliste (fest in den Einstellungen hinterlegt, analog zur welche TickTick-Liste ist die Zielliste (fest in den Einstellungen hinterlegt, analog zur
WebUntis-URL)? WebUntis-URL)?
- [ ] **6.1.8** E-Mail-Ordner-Triage fürs dienstliche Postfach (Nutzerwunsch, 2026-08-30): Der
Nutzer verwaltet Termine/Bitten/Rundschreiben/Messevorbereitungen aktuell ausschließlich per
Mail (eM Client gegen ein schlecht konfiguriertes Open-Xchange-"Fake-Exchange", echtes
Outlook/EWS funktioniert dort nicht zuverlässig — Open-Xchange spricht aber Standard-IMAP).
Ziel ist **keine** vollwertige Mail-Client-Funktion in LehrerApp (kein Compose/Reply, keine
Ordnerverwaltung, kein Postfach-weites Lesen), sondern eine schmale Brücke: Der Nutzer legt
in eM Client einen Ordner an (z.B. "→LehrerApp") und verschiebt dorthin manuell, was eine
Aktion in der Schule braucht. LehrerApp verbindet sich **nur read-only per IMAP** und
**nur mit diesem einen Ordner** (kein `DELETE`/`STORE`/`MOVE` auf dem Server), zeigt die
Mails dort an (Betreff/Absender/Datum/Textvorschau) und bietet pro Mail "→ Termin anlegen"
/ "→ Aufgabe anlegen" (öffnet die bestehenden Dialoge aus 6.1.2 bzw. Kapitel 4, vorbefüllt
mit Betreff als Titel). Als "bearbeitet" markierte Mails verschwinden nur lokal aus der
LehrerApp-Ansicht (Merkliste bereits gesehener IMAP-UIDs) — das tatsächliche Verschieben
zurück in Posteingang/Archiv bleibt bewusst manuelle Aufgabe des Nutzers in eM Client, damit
LehrerApp ganz ohne Schreibrechte auf dem Postfach auskommt.
- **Architektur-Vorbild:** wie `LehrerApp.WebUntis` ein eigenständiger, serverunabhängiger
Client — neues `LehrerApp.Mail`-Projekt (IMAP-Verbindung + Nachrichtenliste, z.B. via
`MailKit`, Eintrag in `Directory.Packages.props`), Desktop bindet es analog zu
`LehrerApp.WebUntis` direkt ein. Zugangsdaten (IMAP-Host/Port/Nutzer/Passwort, Ordnername)
dürfen wie WebUntis-Zugangsdaten **nicht** über `LehrerApp.Api` laufen — rein lokal, gleiches
Einstellungsfeld-Muster wie die WebUntis-URL (Kapitel 12).
- **Offen:** Passwort-Speicherung (OS-Credential-Store vs. verschlüsselt in den lokalen
Settings, wie es die übrigen gespeicherten Zugangsdaten der App bereits handhaben — dort
nachsehen statt neu entscheiden); Poll-Intervall; ob eine Mail-Vorschau nur Text oder auch
einfaches HTML rendern soll; Verhalten bei Anhängen (zunächst vermutlich ignorieren, nur
Text/Metadaten).
**Nachtrag — Pädagogische Klassen-Aufgaben (Nutzer-Feedback):** Wunsch nach einer zweiten, **Nachtrag — Pädagogische Klassen-Aufgaben (Nutzer-Feedback):** Wunsch nach einer zweiten,
"weniger arbeitszeitrelevant als pädagogisch" gedachten Art von Todo-Item (Beispiele: "weniger arbeitszeitrelevant als pädagogisch" gedachten Art von Todo-Item (Beispiele:
"Ansage an die Klasse", "Etwas zum Stichtag einsammeln/austeilen"), gleichberechtigt im "Ansage an die Klasse", "Etwas zum Stichtag einsammeln/austeilen"), gleichberechtigt im