Final Changes
This commit is contained in:
parent
d18df4600a
commit
146aba1157
@ -1,18 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace ChronoFlow.Model
|
namespace ChronoFlow.Model
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Repräsentiert einen Benutzer im System (Mitarbeiter oder Admin).
|
||||||
|
/// Wird für Authentifizierung, Rechteverwaltung und Zuordnung verwendet.
|
||||||
|
/// </summary>
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Der Benutzername, der zur Anmeldung verwendet wird.
|
||||||
|
/// </summary>
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Das gehashte Passwort des Benutzers.
|
||||||
|
/// </summary>
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rolle des Benutzers, z.B. "Admin" oder "Mitarbeiter".
|
||||||
|
/// </summary>
|
||||||
public string Role { get; set; }
|
public string Role { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interne Mitarbeiternummer, optional.
|
||||||
|
/// </summary>
|
||||||
public string Mitarbeiternummer { get; set; }
|
public string Mitarbeiternummer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Abteilung des Mitarbeiters, z.B. „Produktion“, „IT“, etc.
|
||||||
|
/// </summary>
|
||||||
public string Abteilung { get; set; }
|
public string Abteilung { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Primärschlüssel in der Datenbank.
|
||||||
|
/// </summary>
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string OriginalUsername { get; set; } // wichtig für Updates
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Der ursprüngliche Benutzername – wird z.B. für Updates verwendet,
|
||||||
|
/// um Namensänderungen korrekt zu verarbeiten.
|
||||||
|
/// </summary>
|
||||||
|
public string OriginalUsername { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gibt an, ob der Benutzer beim nächsten Login sein Passwort ändern muss.
|
||||||
|
/// Wird z.B. bei neu erstellten Konten gesetzt.
|
||||||
|
/// </summary>
|
||||||
public bool MussPasswortAendern { get; set; }
|
public bool MussPasswortAendern { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Letzter erfolgreicher Login des Benutzers.
|
||||||
|
/// Dient z.B. zur Änderungsverfolgung von Projekten seit dem letzten Login.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LetzterLogin { get; set; } = DateTime.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Vorheriger Login-Zeitpunkt – wird beim nächsten Login zu LetzterLogin verschoben.
|
||||||
|
/// Ermöglicht die Erkennung von Änderungen zwischen zwei Sessions.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime VorletzterLogin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Konstruktor initialisiert leere Zeichenketten zur Vermeidung von Nullwerten.
|
||||||
|
/// </summary>
|
||||||
public User()
|
public User()
|
||||||
{
|
{
|
||||||
Username = "";
|
Username = "";
|
||||||
@ -22,7 +74,5 @@ namespace ChronoFlow.Model
|
|||||||
Abteilung = "";
|
Abteilung = "";
|
||||||
OriginalUsername = "";
|
OriginalUsername = "";
|
||||||
}
|
}
|
||||||
public DateTime LetzterLogin { get; set; } = DateTime.MinValue;
|
|
||||||
public DateTime VorletzterLogin { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,25 +2,53 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:model1="clr-namespace:ChronoFlow.Model;assembly=ChronoFlow.Model"
|
xmlns:model1="clr-namespace:ChronoFlow.Model;assembly=ChronoFlow.Model"
|
||||||
x:Class="ChronoFlow.View.Admin.AbgeschlosseneProjekteView">
|
x:Class="ChronoFlow.View.Admin.AbgeschlosseneProjekteView">
|
||||||
|
|
||||||
|
<!-- Haupt-Layout mit vier Zeilen: Titel, Suchfeld, Liste, Zurück-Button -->
|
||||||
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="20">
|
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="20">
|
||||||
<TextBlock Grid.Row="0" Text="Abgeschlossene Projekte" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" Margin="0,0,0,10"/>
|
|
||||||
|
|
||||||
<TextBox Grid.Row="1" x:Name="Suchfeld" Watermark="🔍 Nach Projekt oder Mitarbeiter suchen..." KeyUp="Suchfeld_KeyUp" Margin="0,0,0,10"/>
|
<!-- 🔷 Überschrift -->
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Text="Abgeschlossene Projekte"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<!-- 🔍 Suchfeld zum Filtern der Projekte (nach Name oder Mitarbeiter) -->
|
||||||
|
<TextBox Grid.Row="1"
|
||||||
|
x:Name="Suchfeld"
|
||||||
|
Watermark="🔍 Nach Projekt oder Mitarbeiter suchen..."
|
||||||
|
KeyUp="Suchfeld_KeyUp"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<!-- 📋 Scrollbare Liste abgeschlossener Projekte -->
|
||||||
<ScrollViewer Grid.Row="2">
|
<ScrollViewer Grid.Row="2">
|
||||||
<ListBox x:Name="AbgeschlosseneListe">
|
<ListBox x:Name="AbgeschlosseneListe">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
|
<!-- Darstellung jedes abgeschlossenen Zeiteintrags -->
|
||||||
<DataTemplate DataType="{x:Type model1:Zeiteintrag}">
|
<DataTemplate DataType="{x:Type model1:Zeiteintrag}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="10" Margin="5">
|
<StackPanel Orientation="Horizontal" Spacing="10" Margin="5">
|
||||||
|
|
||||||
|
<!-- 📌 Projektname -->
|
||||||
<TextBlock Text="{Binding Projekt}" Width="150"/>
|
<TextBlock Text="{Binding Projekt}" Width="150"/>
|
||||||
|
|
||||||
|
<!-- 👤 Mitarbeitender -->
|
||||||
<TextBlock Text="{Binding Mitarbeiter}" Width="150"/>
|
<TextBlock Text="{Binding Mitarbeiter}" Width="150"/>
|
||||||
|
|
||||||
|
<!-- ⏰ Endzeit -->
|
||||||
<TextBlock Text="{Binding Endzeit}" Width="150"/>
|
<TextBlock Text="{Binding Endzeit}" Width="150"/>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
<Button Grid.Row="3" Content="⬅ Zurück zum Dashboard" Click="ZurueckButton_Click" HorizontalAlignment="Center" Margin="0,10,0,0"/>
|
<!-- 🔙 Zurück-Button zum Admin-Dashboard -->
|
||||||
|
<Button Grid.Row="3"
|
||||||
|
Content="⬅ Zurück zum Dashboard"
|
||||||
|
Click="ZurueckButton_Click"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="0,10,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -99,7 +99,7 @@ namespace ChronoFlow.View.Admin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wird z. B. vom ProjektErstellenView aufgerufen, um die Liste neu zu laden.
|
/// Wird z.B. vom ProjektErstellenView aufgerufen, um die Liste neu zu laden.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AktualisiereLetzteProjekte()
|
public void AktualisiereLetzteProjekte()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,12 +4,30 @@
|
|||||||
Width="400" Height="200"
|
Width="400" Height="200"
|
||||||
Title="Bestätigung">
|
Title="Bestätigung">
|
||||||
|
|
||||||
|
<!-- 📦 Haupt-Layout: vertikale Anordnung mit Abstand -->
|
||||||
<StackPanel Margin="20" Spacing="10">
|
<StackPanel Margin="20" Spacing="10">
|
||||||
<TextBlock x:Name="FrageText" Text="Sind Sie sicher?" FontSize="16" FontWeight="Bold" TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="10">
|
<!-- ❓ Frage-Text (wird im Code gesetzt, z.B. "Möchten Sie das wirklich löschen?") -->
|
||||||
<Button Content="✅ Ja" Width="80" Click="JaButton_Click" />
|
<TextBlock x:Name="FrageText"
|
||||||
<Button Content="❌ Nein" Width="80" Click="NeinButton_Click" />
|
Text="Sind Sie sicher?"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="Bold"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<!-- 🔘 Buttons für Ja / Nein -->
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Spacing="10">
|
||||||
|
|
||||||
|
<!-- ✅ Bestätigen -->
|
||||||
|
<Button Content="✅ Ja"
|
||||||
|
Width="80"
|
||||||
|
Click="JaButton_Click" />
|
||||||
|
|
||||||
|
<!-- ❌ Abbrechen -->
|
||||||
|
<Button Content="❌ Nein"
|
||||||
|
Width="80"
|
||||||
|
Click="NeinButton_Click" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Window>
|
</Window>
|
||||||
@ -2,28 +2,63 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:model1="clr-namespace:ChronoFlow.Model;assembly=ChronoFlow.Model"
|
xmlns:model1="clr-namespace:ChronoFlow.Model;assembly=ChronoFlow.Model"
|
||||||
x:Class="ChronoFlow.View.Admin.MitarbeiterListeView">
|
x:Class="ChronoFlow.View.Admin.MitarbeiterListeView">
|
||||||
|
|
||||||
|
<!-- 🧱 Hauptlayout mit 4 Zeilen: Titel, Suchfeld, Liste, Zurück-Button -->
|
||||||
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="20">
|
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="20">
|
||||||
<TextBlock Grid.Row="0" Text="Alle Mitarbeiter" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" Margin="0,0,0,10"/>
|
|
||||||
|
|
||||||
<TextBox Grid.Row="1" x:Name="Suchfeld" Watermark="🔍 Suchen..." KeyUp="Suchfeld_KeyUp" Margin="0,0,0,10"/>
|
<!-- 🧑🤝🧑 Titel der Ansicht -->
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Text="Alle Mitarbeiter"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<!-- 🔍 Suchfeld zur Live-Suche nach Mitarbeitern (KeyUp-Event im CodeBehind) -->
|
||||||
|
<TextBox Grid.Row="1"
|
||||||
|
x:Name="Suchfeld"
|
||||||
|
Watermark="🔍 Suchen..."
|
||||||
|
KeyUp="Suchfeld_KeyUp"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<!-- 📜 Scrollbarer Bereich für Mitarbeitereinträge -->
|
||||||
<ScrollViewer Grid.Row="2">
|
<ScrollViewer Grid.Row="2">
|
||||||
<ListBox x:Name="MitarbeiterListe">
|
<ListBox x:Name="MitarbeiterListe">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
|
<!-- 🎨 Vorlage für jeden einzelnen Mitarbeiter (aus Model.User) -->
|
||||||
<DataTemplate DataType="{x:Type model1:User}">
|
<DataTemplate DataType="{x:Type model1:User}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="10" Margin="5">
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Spacing="10"
|
||||||
|
Margin="5">
|
||||||
|
|
||||||
|
<!-- 🧾 Benutzerinformationen -->
|
||||||
<TextBlock Text="{Binding Username}" Width="150"/>
|
<TextBlock Text="{Binding Username}" Width="150"/>
|
||||||
<TextBlock Text="{Binding Abteilung}" Width="150"/>
|
<TextBlock Text="{Binding Abteilung}" Width="150"/>
|
||||||
<TextBlock Text="{Binding Mitarbeiternummer}" Width="150"/>
|
<TextBlock Text="{Binding Mitarbeiternummer}" Width="150"/>
|
||||||
<Button Content="🖋 Bearbeiten" Tag="{Binding}" Click="Bearbeiten_Click"/>
|
|
||||||
<Button Content="🗑 Löschen" Tag="{Binding}" Click="Loeschen_Click"/>
|
<!-- ✏ Aktionen für jeden Mitarbeiter -->
|
||||||
<Button Content="🔑 Passwort zurücksetzen" Tag="{Binding}" Click="PasswortReset_Click" />
|
<Button Content="🖋 Bearbeiten"
|
||||||
|
Tag="{Binding}"
|
||||||
|
Click="Bearbeiten_Click"/>
|
||||||
|
|
||||||
|
<Button Content="🗑 Löschen"
|
||||||
|
Tag="{Binding}"
|
||||||
|
Click="Loeschen_Click"/>
|
||||||
|
|
||||||
|
<Button Content="🔑 Passwort zurücksetzen"
|
||||||
|
Tag="{Binding}"
|
||||||
|
Click="PasswortReset_Click"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
<Button Grid.Row="3" Content="⬅ Zurück zum Dashboard" Click="ZurueckButton_Click" HorizontalAlignment="Center" Margin="0,10,0,0"/>
|
<!-- 🔙 Zurück zum Admin-Dashboard -->
|
||||||
|
<Button Grid.Row="3"
|
||||||
|
Content="⬅ Zurück zum Dashboard"
|
||||||
|
Click="ZurueckButton_Click"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="0,10,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -2,70 +2,64 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
x:Class="ChronoFlow.View.Admin.ProjektErstellenView">
|
x:Class="ChronoFlow.View.Admin.ProjektErstellenView">
|
||||||
|
|
||||||
<!-- Scrollfähiger Bereich für kleinere Fenster -->
|
<!-- Gesamter Inhalt ist scrollbar -->
|
||||||
<ScrollViewer>
|
<ScrollViewer Padding="10">
|
||||||
<StackPanel Margin="25" Spacing="15">
|
<StackPanel Margin="25" Spacing="15">
|
||||||
|
|
||||||
<!-- Titelüberschrift -->
|
<!-- Titel -->
|
||||||
<TextBlock Text="Neues Projekt erstellen"
|
<TextBlock Text="Neues Projekt erstellen"
|
||||||
FontSize="20"
|
FontSize="20"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
HorizontalAlignment="Center" />
|
HorizontalAlignment="Center" />
|
||||||
|
|
||||||
<!-- Eingabefeld: Projektname -->
|
<!-- Projektname -->
|
||||||
<TextBlock Text="Projektname:" />
|
<TextBlock Text="Projektname:" />
|
||||||
<TextBox x:Name="ProjektnameBox" />
|
<TextBox x:Name="ProjektnameBox" />
|
||||||
|
|
||||||
<!-- Startdatum auswählen -->
|
<!-- Startzeit -->
|
||||||
<TextBlock Text="Startdatum:" />
|
<TextBlock Text="Startdatum:" />
|
||||||
<DatePicker x:Name="StartdatumPicker" />
|
<DatePicker x:Name="StartdatumPicker" />
|
||||||
|
|
||||||
<!-- Startzeit im Format HH:mm -->
|
|
||||||
<TextBlock Text="Startzeit (HH:mm):" />
|
<TextBlock Text="Startzeit (HH:mm):" />
|
||||||
<TextBox x:Name="StartzeitBox" Watermark="z.B. 09:00" />
|
<TextBox x:Name="StartzeitBox" Watermark="z.B. 09:00" />
|
||||||
|
|
||||||
<!-- Enddatum auswählen -->
|
<!-- Endzeit -->
|
||||||
<TextBlock Text="Enddatum:" />
|
<TextBlock Text="Enddatum:" />
|
||||||
<DatePicker x:Name="EnddatumPicker" />
|
<DatePicker x:Name="EnddatumPicker" />
|
||||||
|
|
||||||
<!-- Endzeit im Format HH:mm -->
|
|
||||||
<TextBlock Text="Endzeit (HH:mm):" />
|
<TextBlock Text="Endzeit (HH:mm):" />
|
||||||
<TextBox x:Name="EndzeitBox" Watermark="z.B. 17:00" />
|
<TextBox x:Name="EndzeitBox" Watermark="z.B. 17:00" />
|
||||||
|
|
||||||
<!-- Projektleiter über Dropdown zuweisen -->
|
<!-- Projektleiter -->
|
||||||
<TextBlock Text="Projektleiter auswählen:" />
|
<TextBlock Text="Projektleiter auswählen:" />
|
||||||
<ComboBox x:Name="ProjektleiterDropdown" />
|
<ComboBox x:Name="ProjektleiterDropdown" />
|
||||||
|
|
||||||
<StackPanel>
|
<!-- Mitarbeiterauswahl -->
|
||||||
<TextBlock Text="Mitarbeiter auswählen:" Margin="0,10,0,5" />
|
<TextBlock Text="Mitarbeitende auswählen:" Margin="0,10,0,5" />
|
||||||
<Expander Header="➕ Mitarbeitende auswählen" Background="#222" Foreground="White">
|
|
||||||
<ListBox x:Name="MitarbeiterListBox"
|
<StackPanel Orientation="Horizontal" Spacing="10" Margin="0,0,0,5">
|
||||||
SelectionMode="Multiple"
|
<Button Content="✅ Alle auswählen" Click="AlleMitarbeiterAuswaehlen_Click" />
|
||||||
Height="250"
|
<Button Content="❌ Keine auswählen" Click="KeineMitarbeiterAuswaehlen_Click" />
|
||||||
MinWidth="300"
|
|
||||||
BorderBrush="Gray"
|
|
||||||
BorderThickness="1"
|
|
||||||
Background="#333"
|
|
||||||
Foreground="White"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Text="{Binding}" Padding="4" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
</Expander>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Kommentarbereich -->
|
<!-- Scrollbarer Kartenbereich mit Checkboxen -->
|
||||||
|
<ScrollViewer Height="240"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
Background="#2a2a2a"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
Padding="10,10,10,-17"
|
||||||
|
CornerRadius="8">
|
||||||
|
<ListBox x:Name="MitarbeiterCheckList"
|
||||||
|
BorderThickness="0"
|
||||||
|
SelectionMode="Multiple" />
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Kommentar -->
|
||||||
<TextBlock Text="Kommentar:" />
|
<TextBlock Text="Kommentar:" />
|
||||||
<TextBox x:Name="KommentarBox" AcceptsReturn="True" Height="80" />
|
<TextBox x:Name="KommentarBox" AcceptsReturn="True" Height="80" />
|
||||||
|
|
||||||
<!-- Hinweise zur Deadline-Farbe -->
|
<!-- Farblegende für Deadlines -->
|
||||||
<Border Background="#111111"
|
<Border Background="#111111" CornerRadius="5" Padding="10" Margin="0,10,0,0">
|
||||||
CornerRadius="5"
|
|
||||||
Padding="10"
|
|
||||||
Margin="0,10,0,0">
|
|
||||||
<StackPanel Spacing="5">
|
<StackPanel Spacing="5">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
<TextBlock Text="🛈" FontWeight="Bold" />
|
<TextBlock Text="🛈" FontWeight="Bold" />
|
||||||
@ -77,28 +71,14 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Aktions-Buttons -->
|
<!-- Aktionsbuttons -->
|
||||||
<StackPanel Orientation="Horizontal"
|
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center" Margin="0,10,0,0">
|
||||||
Spacing="10"
|
<Button Content="✅ Speichern" Click="SpeichernButton_Click" Width="115" Height="36" />
|
||||||
HorizontalAlignment="Center"
|
<Button Content="🧪 3 Demo-Projekte (rot/gelb/grün)" Click="DemoProjekteButton_Click" Width="250" Height="36" />
|
||||||
Margin="0,10,0,0">
|
<Button Content="⬅ Zurück zum Dashboard" Click="ZurueckButton_Click" Width="180" Height="36" />
|
||||||
<Button Content="✅ Speichern"
|
|
||||||
Click="SpeichernButton_Click"
|
|
||||||
Width="115"
|
|
||||||
Height="36" />
|
|
||||||
|
|
||||||
<Button Content="🧪 3 Demo-Projekte (rot/gelb/grün)"
|
|
||||||
Click="DemoProjekteButton_Click"
|
|
||||||
Width="250"
|
|
||||||
Height="36" />
|
|
||||||
|
|
||||||
<Button Content="⬅ Zurück zum Dashboard"
|
|
||||||
Click="ZurueckButton_Click"
|
|
||||||
Width="180"
|
|
||||||
Height="36" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Optionaler Fehler-/Hinweistext -->
|
<!-- Feedbackbereich -->
|
||||||
<TextBlock x:Name="FeedbackText"
|
<TextBlock x:Name="FeedbackText"
|
||||||
Foreground="Red"
|
Foreground="Red"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
@ -16,6 +17,7 @@ namespace ChronoFlow.View.Admin;
|
|||||||
public partial class ProjektErstellenView : UserControl
|
public partial class ProjektErstellenView : UserControl
|
||||||
{
|
{
|
||||||
private readonly ViewManager _viewManager;
|
private readonly ViewManager _viewManager;
|
||||||
|
private readonly Dictionary<string, CheckBox> _mitarbeiterCheckBoxMap = new();
|
||||||
|
|
||||||
public ProjektErstellenView() : this(new ViewManager(new ContentControl()))
|
public ProjektErstellenView() : this(new ViewManager(new ContentControl()))
|
||||||
{
|
{
|
||||||
@ -30,114 +32,153 @@ public partial class ProjektErstellenView : UserControl
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dbService = new SqliteZeiterfassungsService();
|
var dbService = new SqliteZeiterfassungsService();
|
||||||
List<string> mitarbeiter = dbService.LadeAlleMitarbeiterNamen();
|
var mitarbeiter = dbService.LadeAlleMitarbeiterNamen();
|
||||||
|
|
||||||
// 🔍 Konsolenausgabe zur Kontrolle
|
|
||||||
if (mitarbeiter is { Count: > 0 })
|
if (mitarbeiter is { Count: > 0 })
|
||||||
{
|
{
|
||||||
Console.WriteLine("✅ Mitarbeitende erfolgreich geladen:");
|
Console.WriteLine("✅ Mitarbeitende erfolgreich geladen:");
|
||||||
|
foreach (var name in mitarbeiter)
|
||||||
|
Console.WriteLine("- " + name);
|
||||||
|
|
||||||
|
ProjektleiterDropdown.ItemsSource = mitarbeiter;
|
||||||
|
|
||||||
|
// Liste manuell aufbauen und Checkboxen erzeugen
|
||||||
|
_mitarbeiterCheckBoxMap.Clear();
|
||||||
|
var checkBoxen = new List<CheckBox>();
|
||||||
|
|
||||||
foreach (var name in mitarbeiter)
|
foreach (var name in mitarbeiter)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"- {name}");
|
var cb = new CheckBox
|
||||||
|
{
|
||||||
|
Content = name,
|
||||||
|
Foreground = Brushes.White,
|
||||||
|
FontSize = 14,
|
||||||
|
Margin = new Thickness(4)
|
||||||
|
};
|
||||||
|
|
||||||
|
_mitarbeiterCheckBoxMap[name] = cb;
|
||||||
|
checkBoxen.Add(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ItemsSource der ListBox mit den Checkboxen befüllen
|
||||||
|
MitarbeiterCheckList.ItemsSource = checkBoxen;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("⚠️ Achtung: Mitarbeitendenliste ist leer.");
|
Console.WriteLine("⚠️ Achtung: Mitarbeitendenliste ist leer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Items in GUI setzen
|
|
||||||
MitarbeiterListBox!.ItemsSource = mitarbeiter;
|
|
||||||
ProjektleiterDropdown!.ItemsSource = mitarbeiter;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"❌ Fehler beim Laden der Mitarbeiterauswahl: {ex.Message}");
|
Console.WriteLine($"❌ Fehler beim Laden der Mitarbeiterauswahl: {ex.Message}");
|
||||||
FeedbackText!.Text = "⚠ Fehler beim Laden der Mitarbeiter.";
|
FeedbackText.Text = "⚠ Fehler beim Laden der Mitarbeiter.";
|
||||||
FeedbackText.Foreground = Brushes.Red;
|
FeedbackText.Foreground = Brushes.Red;
|
||||||
FeedbackText.IsVisible = true;
|
FeedbackText.IsVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<string> ErmittleAusgewaehlteMitarbeiter()
|
||||||
|
{
|
||||||
|
return _mitarbeiterCheckBoxMap
|
||||||
|
.Where(kvp => kvp.Value.IsChecked == true)
|
||||||
|
.Select(kvp => kvp.Key)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AlleMitarbeiterAuswaehlen_Click(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var cb in _mitarbeiterCheckBoxMap.Values)
|
||||||
|
{
|
||||||
|
cb.IsChecked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KeineMitarbeiterAuswaehlen_Click(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var cb in _mitarbeiterCheckBoxMap.Values)
|
||||||
|
{
|
||||||
|
cb.IsChecked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SpeichernButton_Click(object? sender, RoutedEventArgs e)
|
private void SpeichernButton_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
|
||||||
FeedbackText.IsVisible = false;
|
|
||||||
|
|
||||||
string projektname = ProjektnameBox.Text ?? "";
|
|
||||||
DateTime startdatum = StartdatumPicker.SelectedDate?.Date ?? DateTime.Today;
|
|
||||||
DateTime enddatum = EnddatumPicker.SelectedDate?.Date ?? DateTime.Today;
|
|
||||||
string startzeitText = StartzeitBox.Text ?? "00:00";
|
|
||||||
string endzeitText = EndzeitBox.Text ?? "00:00";
|
|
||||||
string kommentar = KommentarBox.Text ?? "";
|
|
||||||
string projektleiter = ProjektleiterDropdown.SelectedItem?.ToString() ?? "";
|
|
||||||
|
|
||||||
var ausgewaehlteMitarbeiter = MitarbeiterListBox?.SelectedItems?.Cast<string>().ToList() ?? new List<string>();
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(projektname) || ausgewaehlteMitarbeiter.Count == 0 || string.IsNullOrWhiteSpace(projektleiter))
|
|
||||||
{
|
{
|
||||||
FeedbackText.Text = "⚠ Bitte alle Pflichtfelder ausfüllen (Projektname, mindestens ein Mitarbeiter, Projektleiter)!";
|
FeedbackText.IsVisible = false;
|
||||||
FeedbackText.Foreground = Brushes.Red;
|
|
||||||
FeedbackText.IsVisible = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TimeSpan.TryParse(startzeitText, out var startzeit) || !TimeSpan.TryParse(endzeitText, out var endzeit))
|
string projektname = ProjektnameBox.Text ?? "";
|
||||||
{
|
DateTime startdatum = StartdatumPicker.SelectedDate?.Date ?? DateTime.Today;
|
||||||
FeedbackText.Text = "⚠ Ungültige Zeitangaben (Format HH:mm)!";
|
DateTime enddatum = EnddatumPicker.SelectedDate?.Date ?? DateTime.Today;
|
||||||
FeedbackText.Foreground = Brushes.Red;
|
string startzeitText = StartzeitBox.Text ?? "00:00";
|
||||||
FeedbackText.IsVisible = true;
|
string endzeitText = EndzeitBox.Text ?? "00:00";
|
||||||
return;
|
string kommentar = KommentarBox.Text ?? "";
|
||||||
}
|
string projektleiter = ProjektleiterDropdown.SelectedItem?.ToString() ?? "";
|
||||||
|
|
||||||
DateTime startDateTime = startdatum + startzeit;
|
var ausgewaehlteMitarbeiter = ErmittleAusgewaehlteMitarbeiter();
|
||||||
DateTime endDateTime = enddatum + endzeit;
|
|
||||||
|
|
||||||
try
|
if (string.IsNullOrWhiteSpace(projektname) || ausgewaehlteMitarbeiter.Count == 0 || string.IsNullOrWhiteSpace(projektleiter))
|
||||||
{
|
|
||||||
var dbService = new SqliteZeiterfassungsService();
|
|
||||||
|
|
||||||
foreach (var einzelnerMitarbeiter in ausgewaehlteMitarbeiter)
|
|
||||||
{
|
{
|
||||||
dbService.SpeichereEintrag(new Zeiteintrag
|
FeedbackText.Text = "⚠ Bitte alle Pflichtfelder ausfüllen (Projektname, mindestens ein Mitarbeiter, Projektleiter)!";
|
||||||
{
|
FeedbackText.Foreground = Brushes.Red;
|
||||||
Mitarbeiter = einzelnerMitarbeiter,
|
FeedbackText.IsVisible = true;
|
||||||
Projekt = projektname,
|
return;
|
||||||
Startzeit = startDateTime,
|
|
||||||
Endzeit = endDateTime,
|
|
||||||
Kommentar = kommentar,
|
|
||||||
Projektleiter = projektleiter,
|
|
||||||
Erledigt = false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedbackText.Text = "✅ Projekt erfolgreich gespeichert.";
|
if (!TimeSpan.TryParse(startzeitText, out var startzeit) || !TimeSpan.TryParse(endzeitText, out var endzeit))
|
||||||
FeedbackText.Foreground = Brushes.Green;
|
{
|
||||||
FeedbackText.IsVisible = true;
|
FeedbackText.Text = "⚠ Ungültige Zeitangaben (Format HH:mm)!";
|
||||||
|
FeedbackText.Foreground = Brushes.Red;
|
||||||
|
FeedbackText.IsVisible = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ProjektnameBox.Text = "";
|
DateTime startDateTime = startdatum + startzeit;
|
||||||
KommentarBox.Text = "";
|
DateTime endDateTime = enddatum + endzeit;
|
||||||
StartdatumPicker.SelectedDate = DateTime.Today;
|
|
||||||
EnddatumPicker.SelectedDate = DateTime.Today;
|
|
||||||
StartzeitBox.Text = "09:00";
|
|
||||||
EndzeitBox.Text = "17:00";
|
|
||||||
MitarbeiterListBox.SelectedItems?.Clear();
|
|
||||||
MitarbeiterListBox.SelectedIndex = -1;
|
|
||||||
ProjektleiterDropdown.SelectedItem = null;
|
|
||||||
|
|
||||||
if (_viewManager.TryGetView<AdminMainView>("AdminMain", out var adminView) && adminView != null)
|
try
|
||||||
adminView.AktualisiereLetzteProjekte();
|
{
|
||||||
|
var dbService = new SqliteZeiterfassungsService();
|
||||||
|
|
||||||
|
foreach (var name in ausgewaehlteMitarbeiter)
|
||||||
|
{
|
||||||
|
dbService.SpeichereEintrag(new Zeiteintrag
|
||||||
|
{
|
||||||
|
Mitarbeiter = name,
|
||||||
|
Projekt = projektname,
|
||||||
|
Startzeit = startDateTime,
|
||||||
|
Endzeit = endDateTime,
|
||||||
|
Kommentar = kommentar,
|
||||||
|
Projektleiter = projektleiter,
|
||||||
|
Erledigt = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FeedbackText.Text = "✅ Projekt erfolgreich gespeichert.";
|
||||||
|
FeedbackText.Foreground = Brushes.Green;
|
||||||
|
FeedbackText.IsVisible = true;
|
||||||
|
|
||||||
|
ProjektnameBox.Text = "";
|
||||||
|
KommentarBox.Text = "";
|
||||||
|
StartdatumPicker.SelectedDate = DateTime.Today;
|
||||||
|
EnddatumPicker.SelectedDate = DateTime.Today;
|
||||||
|
StartzeitBox.Text = "09:00";
|
||||||
|
EndzeitBox.Text = "17:00";
|
||||||
|
ProjektleiterDropdown.SelectedItem = null;
|
||||||
|
|
||||||
|
foreach (var cb in _mitarbeiterCheckBoxMap.Values)
|
||||||
|
cb.IsChecked = false;
|
||||||
|
|
||||||
|
if (_viewManager.TryGetView<AdminMainView>("AdminMain", out var adminView) && adminView != null)
|
||||||
|
adminView.AktualisiereLetzteProjekte();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
FeedbackText.Text = $"❌ Fehler beim Speichern: {ex.Message}";
|
||||||
|
FeedbackText.Foreground = Brushes.Red;
|
||||||
|
FeedbackText.IsVisible = true;
|
||||||
|
Console.WriteLine("❌ Ausnahme beim Speichern:");
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
FeedbackText.Text = $"❌ Fehler beim Speichern: {ex.Message}";
|
|
||||||
FeedbackText.Foreground = Brushes.Red;
|
|
||||||
FeedbackText.IsVisible = true;
|
|
||||||
Console.WriteLine("❌ Ausnahme beim Speichern:");
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void DemoProjekteButton_Click(object? sender, RoutedEventArgs e)
|
private void DemoProjekteButton_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@ -149,7 +190,9 @@ public partial class ProjektErstellenView : UserControl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MitarbeiterListBox.SelectedItems?.Count == 0)
|
var ausgewaehlteMitarbeiter = ErmittleAusgewaehlteMitarbeiter();
|
||||||
|
|
||||||
|
if (ausgewaehlteMitarbeiter.Count == 0)
|
||||||
{
|
{
|
||||||
FeedbackText.Text = "❗ Bitte wähle mindestens einen Mitarbeiter für die Demo-Projekte.";
|
FeedbackText.Text = "❗ Bitte wähle mindestens einen Mitarbeiter für die Demo-Projekte.";
|
||||||
FeedbackText.Foreground = Brushes.Red;
|
FeedbackText.Foreground = Brushes.Red;
|
||||||
@ -160,8 +203,7 @@ public partial class ProjektErstellenView : UserControl
|
|||||||
var service = new SqliteZeiterfassungsService();
|
var service = new SqliteZeiterfassungsService();
|
||||||
var heute = DateTime.Today;
|
var heute = DateTime.Today;
|
||||||
|
|
||||||
var mitarbeiter = MitarbeiterListBox?.SelectedItems?.Cast<string>().ToList() ?? new List<string>();
|
foreach (var name in ausgewaehlteMitarbeiter)
|
||||||
foreach (var name in mitarbeiter)
|
|
||||||
{
|
{
|
||||||
var projekte = new List<Zeiteintrag>
|
var projekte = new List<Zeiteintrag>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,19 +5,44 @@ using Avalonia.Media;
|
|||||||
|
|
||||||
namespace ChronoFlow.View.Converter
|
namespace ChronoFlow.View.Converter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Konvertiert einen booleschen Wert in eine Farbe (Brush).
|
||||||
|
/// True => Blau, False => Grau.
|
||||||
|
/// Wird z. B. für farbliche Statusanzeigen in der Oberfläche verwendet.
|
||||||
|
/// </summary>
|
||||||
public class BoolToBrushConverter : IValueConverter
|
public class BoolToBrushConverter : IValueConverter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Die Farbe, die bei "true" verwendet wird.
|
||||||
|
/// Kann in XAML überschrieben werden.
|
||||||
|
/// </summary>
|
||||||
public IBrush TrueBrush { get; set; } = Brushes.Blue;
|
public IBrush TrueBrush { get; set; } = Brushes.Blue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Die Farbe, die bei "false" verwendet wird.
|
||||||
|
/// Kann in XAML überschrieben werden.
|
||||||
|
/// </summary>
|
||||||
public IBrush FalseBrush { get; set; } = Brushes.Gray;
|
public IBrush FalseBrush { get; set; } = Brushes.Gray;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Führt die Umwandlung des bool-Wertes in eine Brush durch.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Der Wert, der umgewandelt werden soll (sollte bool sein).</param>
|
||||||
|
/// <param name="targetType">Zieltyp der Bindung (erwartet IBrush).</param>
|
||||||
|
/// <param name="parameter">Optionaler Parameter (nicht verwendet).</param>
|
||||||
|
/// <param name="culture">Kultureinstellungen (nicht verwendet).</param>
|
||||||
|
/// <returns>TrueBrush oder FalseBrush abhängig vom bool-Wert.</returns>
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is bool boolValue && boolValue)
|
if (value is bool boolValue && boolValue)
|
||||||
return Brushes.Blue;
|
return TrueBrush;
|
||||||
|
|
||||||
return Brushes.Gray;
|
return FalseBrush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Umkehrkonvertierung wird hier nicht benötigt.
|
||||||
|
/// </summary>
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException(); // wird i.d.R. nicht benötigt
|
throw new NotImplementedException(); // wird i.d.R. nicht benötigt
|
||||||
|
|||||||
@ -11,14 +11,15 @@ using Microsoft.Data.Sqlite;
|
|||||||
namespace ChronoFlow.View;
|
namespace ChronoFlow.View;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Das Fenster für den Benutzer-Login.
|
/// Das Login-Fenster für Benutzer der Anwendung.
|
||||||
|
/// Es prüft die Anmeldedaten, erzwingt ggf. eine Passwortänderung und startet das Hauptfenster.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class LoginWindow : Window
|
public partial class LoginWindow : Window
|
||||||
{
|
{
|
||||||
private readonly LoginController _loginController;
|
private readonly LoginController _loginController;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Konstruktor – Initialisiert die Oberfläche und legt bei Bedarf einen Standard-Admin an.
|
/// Konstruktor – Initialisiert UI und legt bei Bedarf den Standard-Admin an.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LoginWindow()
|
public LoginWindow()
|
||||||
{
|
{
|
||||||
@ -28,7 +29,7 @@ public partial class LoginWindow : Window
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var service = new SqliteZeiterfassungsService();
|
var service = new SqliteZeiterfassungsService();
|
||||||
service.ErstelleStandardAdmin();
|
service.ErstelleStandardAdmin(); // Falls noch kein Admin vorhanden, wird ein Default-Admin erstellt.
|
||||||
}
|
}
|
||||||
catch (SqliteException ex) when (ex.SqliteErrorCode == 5)
|
catch (SqliteException ex) when (ex.SqliteErrorCode == 5)
|
||||||
{
|
{
|
||||||
@ -43,13 +44,14 @@ public partial class LoginWindow : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wird aufgerufen, wenn der Benutzer auf "Anmelden" klickt.
|
/// Wird aufgerufen, wenn der Benutzer auf „Anmelden“ klickt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async void LoginButton_Click(object? sender, RoutedEventArgs e)
|
private async void LoginButton_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var username = UsernameBox.Text?.Trim();
|
var username = UsernameBox.Text?.Trim();
|
||||||
var password = PasswordBox.Text?.Trim();
|
var password = PasswordBox.Text?.Trim();
|
||||||
|
|
||||||
|
// Validierung: Felder dürfen nicht leer sein
|
||||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||||
{
|
{
|
||||||
ErrorText.Text = "Bitte Benutzername und Passwort eingeben.";
|
ErrorText.Text = "Bitte Benutzername und Passwort eingeben.";
|
||||||
@ -64,13 +66,14 @@ public partial class LoginWindow : Window
|
|||||||
}
|
}
|
||||||
catch (SqliteException ex) when (ex.SqliteErrorCode == 5)
|
catch (SqliteException ex) when (ex.SqliteErrorCode == 5)
|
||||||
{
|
{
|
||||||
ErrorText.Text = "⚠️ Die Datenbank ist gesperrt. Bitte schließen Sie andere Programme (z. B. DB Browser) und versuchen Sie es erneut.";
|
ErrorText.Text = "⚠️ Die Datenbank ist gesperrt. Bitte schließen Sie andere Programme und versuchen Sie es erneut.";
|
||||||
ErrorText.IsVisible = true;
|
ErrorText.IsVisible = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var benutzerListe = service.LadeAlleBenutzer();
|
var benutzerListe = service.LadeAlleBenutzer();
|
||||||
|
|
||||||
|
// Benutzer mit eingegebenem Namen (case-insensitive) finden
|
||||||
var matchingUsers = benutzerListe
|
var matchingUsers = benutzerListe
|
||||||
.Where(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase))
|
.Where(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase))
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -91,6 +94,7 @@ public partial class LoginWindow : Window
|
|||||||
|
|
||||||
var user = matchingUsers.First();
|
var user = matchingUsers.First();
|
||||||
|
|
||||||
|
// Passwort-Überprüfung mit Hashing
|
||||||
if (!PasswordHasher.VerifyPassword(password, user.Password))
|
if (!PasswordHasher.VerifyPassword(password, user.Password))
|
||||||
{
|
{
|
||||||
ErrorText.Text = "Falsches Passwort. Bitte erneut versuchen.";
|
ErrorText.Text = "Falsches Passwort. Bitte erneut versuchen.";
|
||||||
@ -98,7 +102,7 @@ public partial class LoginWindow : Window
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passwort muss geändert werden
|
// Benutzer muss Passwort ändern? → Dialog anzeigen
|
||||||
if (user.MussPasswortAendern)
|
if (user.MussPasswortAendern)
|
||||||
{
|
{
|
||||||
var dialog = new PasswortAendernDialog(user);
|
var dialog = new PasswortAendernDialog(user);
|
||||||
@ -121,13 +125,13 @@ public partial class LoginWindow : Window
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Login-Zeiten aktualisieren
|
// Login-Zeit aktualisieren (für spätere Änderungsbenachrichtigungen)
|
||||||
var vorher = user.LetzterLogin;
|
var vorher = user.LetzterLogin;
|
||||||
user.LetzterLogin = DateTime.Now;
|
user.LetzterLogin = DateTime.Now;
|
||||||
user.VorletzterLogin = vorher;
|
user.VorletzterLogin = vorher;
|
||||||
service.UpdateLoginZeiten(user);
|
service.UpdateLoginZeiten(user);
|
||||||
|
|
||||||
// Hauptfenster öffnen
|
// Hauptfenster öffnen und Login-Fenster schließen
|
||||||
var main = new MainWindow(user);
|
var main = new MainWindow(user);
|
||||||
main.Show();
|
main.Show();
|
||||||
main.Activate();
|
main.Activate();
|
||||||
|
|||||||
@ -91,7 +91,7 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extern aufrufbar für "Zurück zum Dashboard"-Funktion (z. B. in anderen Views).
|
/// Extern aufrufbar für "Zurück zum Dashboard"-Funktion (z.B. in anderen Views).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ShowAdminDashboard()
|
public void ShowAdminDashboard()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,71 +11,86 @@ using ChronoFlow.Controller;
|
|||||||
namespace ChronoFlow.View.Mitarbeiter;
|
namespace ChronoFlow.View.Mitarbeiter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ViewModel für die Mitarbeiter-Aufgabenansicht.
|
/// ViewModel für die Aufgabenansicht eines Mitarbeiters.
|
||||||
/// Enthält alle Aufgaben für den eingeloggten Benutzer und erlaubt das Speichern von Änderungen.
|
/// Lädt relevante Aufgaben (nicht erledigt) und ermöglicht das Speichern von Kommentaren und Bearbeitungsstatus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class EmployeeTasksViewModel : ObservableObject
|
public partial class EmployeeTasksViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly User _benutzer;
|
private readonly User _benutzer; // Der aktuell eingeloggte Benutzer
|
||||||
|
private readonly ZeiterfassungsController controller = new(); // Controller für Datenzugriff
|
||||||
private readonly ZeiterfassungsController controller = new();
|
private readonly string aktuellerBenutzername; // Benutzername (String, z. B. für Vergleich)
|
||||||
|
|
||||||
private readonly string aktuellerBenutzername;
|
|
||||||
|
|
||||||
|
// Liste aller offenen, relevanten Aufgaben für diesen Benutzer
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ObservableCollection<Zeiteintrag> eintraege = new();
|
private ObservableCollection<Zeiteintrag> eintraege = new();
|
||||||
|
|
||||||
|
// Status-Text für Hinweise oder Rückmeldungen an den Benutzer
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? statusText;
|
private string? statusText;
|
||||||
|
|
||||||
|
// Gibt an, ob überhaupt Aufgaben vorhanden sind (z. B. für "Keine Aufgaben"-Hinweis in der View)
|
||||||
public bool HatKeineEintraege => Eintraege.Count == 0;
|
public bool HatKeineEintraege => Eintraege.Count == 0;
|
||||||
|
|
||||||
|
// Wenn sich die Einträge ändern, wird auch das Property `HatKeineEintraege` neu berechnet
|
||||||
partial void OnEintraegeChanged(ObservableCollection<Zeiteintrag>? oldValue, ObservableCollection<Zeiteintrag> newValue)
|
partial void OnEintraegeChanged(ObservableCollection<Zeiteintrag>? oldValue, ObservableCollection<Zeiteintrag> newValue)
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(HatKeineEintraege));
|
OnPropertyChanged(nameof(HatKeineEintraege));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Konstruktor – übernimmt eingeloggten Benutzer, speichert Name für spätere Vergleiche
|
||||||
|
/// und lädt initial die relevanten Einträge.
|
||||||
|
/// </summary>
|
||||||
public EmployeeTasksViewModel(User benutzer)
|
public EmployeeTasksViewModel(User benutzer)
|
||||||
{
|
{
|
||||||
_benutzer = benutzer;
|
_benutzer = benutzer;
|
||||||
aktuellerBenutzername = benutzer.Username;
|
aktuellerBenutzername = benutzer.Username;
|
||||||
|
|
||||||
controller = new ZeiterfassungsController(); // falls du das oben nicht hast
|
controller = new ZeiterfassungsController();
|
||||||
_ = LadeEintraegeAsync();
|
_ = LadeEintraegeAsync(); // asynchrone Initialladung
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lädt alle offenen Aufgaben für diesen Benutzer (oder wenn er Projektleiter ist).
|
||||||
|
/// Sortiert die Aufgaben nach Enddatum.
|
||||||
|
/// Markiert Aufgaben als „geändert“, wenn sie seit dem letzten Login bearbeitet wurden.
|
||||||
|
/// </summary>
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task LadeEintraegeAsync()
|
public async Task LadeEintraegeAsync()
|
||||||
{
|
{
|
||||||
// 🔄 Einträge synchron laden, aber async verpacken
|
// Alle Aufgaben laden (aus Datenbank)
|
||||||
var alleEintraege = await Task.Run(() => controller.LadeAlleEintraege());
|
var alleEintraege = await Task.Run(() => controller.LadeAlleEintraege());
|
||||||
|
|
||||||
|
// Relevante Aufgaben filtern: noch nicht erledigt und betreffen den Benutzer
|
||||||
var relevanteEintraege = alleEintraege
|
var relevanteEintraege = alleEintraege
|
||||||
.Where(e => !e.Erledigt &&
|
.Where(e => !e.Erledigt &&
|
||||||
(e.Mitarbeiter == aktuellerBenutzername || e.Projektleiter == aktuellerBenutzername))
|
(e.Mitarbeiter == aktuellerBenutzername || e.Projektleiter == aktuellerBenutzername))
|
||||||
.OrderBy(e => e.Endzeit)
|
.OrderBy(e => e.Endzeit)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// Prüfung, ob Aufgabe nach letztem Login geändert wurde
|
||||||
foreach (var eintrag in relevanteEintraege)
|
foreach (var eintrag in relevanteEintraege)
|
||||||
{
|
{
|
||||||
eintrag.WurdeSeitLoginBearbeitet = eintrag.LetzteBearbeitung > _benutzer.VorletzterLogin;
|
eintrag.WurdeSeitLoginBearbeitet = eintrag.LetzteBearbeitung > _benutzer.VorletzterLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hinweis anzeigen, wenn Aufgaben seit letztem Login verändert wurden
|
||||||
if (relevanteEintraege.Any(e => e.WurdeSeitLoginBearbeitet))
|
if (relevanteEintraege.Any(e => e.WurdeSeitLoginBearbeitet))
|
||||||
StatusText = "📢 Es wurden Aufgaben seit Ihrem letzten Login geändert.";
|
StatusText = "📢 Es wurden Aufgaben seit Ihrem letzten Login geändert.";
|
||||||
|
|
||||||
|
// Die View wird über `Eintraege` gebunden, daher muss hier neu zugewiesen werden
|
||||||
Eintraege = new ObservableCollection<Zeiteintrag>(relevanteEintraege);
|
Eintraege = new ObservableCollection<Zeiteintrag>(relevanteEintraege);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Speichert alle Änderungen (Kommentar + Erledigt-Status) der aktuellen Aufgaben.
|
||||||
|
/// Danach wird die Liste neu geladen.
|
||||||
|
/// </summary>
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task SpeichereEintraegeAsync()
|
public async Task SpeichereEintraegeAsync()
|
||||||
{
|
{
|
||||||
// Achtung: Hier verwendest du noch das Repository, obwohl du oben mit dem Controller arbeitest.
|
// Achtung: Diese Methode ruft direkt den Service auf.
|
||||||
// Du kannst entweder:
|
// Dies könnte in Zukunft auch über den Controller geschehen.
|
||||||
// A) Das Repository auch im ViewModel übergeben
|
|
||||||
// B) Die Methode UpdateStatusUndKommentarAsync auch in den Controller packen
|
|
||||||
|
|
||||||
foreach (var eintrag in Eintraege)
|
foreach (var eintrag in Eintraege)
|
||||||
{
|
{
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
@ -85,6 +100,7 @@ public partial class EmployeeTasksViewModel : ObservableObject
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rückmeldung anzeigen und Liste aktualisieren
|
||||||
StatusText = "✅ Änderungen gespeichert.";
|
StatusText = "✅ Änderungen gespeichert.";
|
||||||
await LadeEintraegeAsync();
|
await LadeEintraegeAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ public partial class MitarbeiterMainView : UserControl
|
|||||||
private readonly ObservableCollection<string> _notifications = new();
|
private readonly ObservableCollection<string> _notifications = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Öffentlicher, parameterloser Konstruktor – erforderlich für Avalonia Runtime Loader (z. B. Designer).
|
/// Öffentlicher, parameterloser Konstruktor – erforderlich für Avalonia Runtime Loader (z.B. Designer).
|
||||||
/// Startet mit Dummy-Werten.
|
/// Startet mit Dummy-Werten.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MitarbeiterMainView()
|
public MitarbeiterMainView()
|
||||||
|
|||||||
@ -109,33 +109,44 @@ namespace ChronoFlow.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void DemoBenutzerErstellen_Click(object? sender, RoutedEventArgs e)
|
private void DemoBenutzerErstellen_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var namen = new[] { "Max Mustermann", "Lena Schmidt", "Tobias Becker", "Julia Klein", "Nico Weber" };
|
string[] vornamen = { "Max", "Lena", "Tobias", "Julia", "Nico", "Emma", "Finn", "Sophie", "Noah", "Laura" };
|
||||||
var zufallsname = namen[new Random().Next(namen.Length)];
|
string[] nachnamen = { "Mustermann", "Schmidt", "Becker", "Klein", "Weber", "Meier", "Schulz", "Huber", "Lang", "Richter" };
|
||||||
|
|
||||||
var service = new SqliteZeiterfassungsService();
|
var service = new SqliteZeiterfassungsService();
|
||||||
|
var existierendeUsernames = service.LadeAlleBenutzer().Select(u => u.Username).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// Prüfen, ob Name bereits vorhanden ist
|
var rnd = new Random();
|
||||||
if (service.LadeAlleBenutzer().Any(u => u.Username.Equals(zufallsname, StringComparison.OrdinalIgnoreCase)))
|
int versuche = 0;
|
||||||
|
|
||||||
|
while (versuche < 100)
|
||||||
{
|
{
|
||||||
FeedbackText.Text = "⚠ Demo-Mitarbeiter existiert bereits.";
|
string vorname = vornamen[rnd.Next(vornamen.Length)];
|
||||||
FeedbackText.Foreground = Brushes.OrangeRed;
|
string nachname = nachnamen[rnd.Next(nachnamen.Length)];
|
||||||
FeedbackText.IsVisible = true;
|
string name = $"{vorname} {nachname}";
|
||||||
return;
|
|
||||||
|
if (!existierendeUsernames.Contains(name))
|
||||||
|
{
|
||||||
|
var demoUser = new User
|
||||||
|
{
|
||||||
|
Username = name,
|
||||||
|
Password = PasswordHasher.HashPassword("newpassword"),
|
||||||
|
Role = "Mitarbeiter",
|
||||||
|
MussPasswortAendern = true
|
||||||
|
};
|
||||||
|
|
||||||
|
service.ErstelleNeuenBenutzer(demoUser);
|
||||||
|
|
||||||
|
FeedbackText.Text = $"✅ Demo-Mitarbeiter '{name}' erstellt (Passwort: newpassword)";
|
||||||
|
FeedbackText.Foreground = Brushes.Green;
|
||||||
|
FeedbackText.IsVisible = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
versuche++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demo-User erstellen
|
FeedbackText.Text = "❌ Fehler: Konnte keinen eindeutigen Demo-Mitarbeiter generieren.";
|
||||||
var demoUser = new User
|
FeedbackText.Foreground = Brushes.Red;
|
||||||
{
|
|
||||||
Username = zufallsname,
|
|
||||||
Password = PasswordHasher.HashPassword("newpassword"),
|
|
||||||
Role = "Mitarbeiter",
|
|
||||||
MussPasswortAendern = true
|
|
||||||
};
|
|
||||||
|
|
||||||
service.ErstelleNeuenBenutzer(demoUser);
|
|
||||||
|
|
||||||
FeedbackText.Text = $"✅ Demo-Mitarbeiter '{zufallsname}' erstellt (Passwort: newpassword)";
|
|
||||||
FeedbackText.Foreground = Brushes.Green;
|
|
||||||
FeedbackText.IsVisible = true;
|
FeedbackText.IsVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,20 +4,41 @@
|
|||||||
Width="400" Height="250"
|
Width="400" Height="250"
|
||||||
Title="Passwort ändern">
|
Title="Passwort ändern">
|
||||||
|
|
||||||
|
<!-- Haupt-Layout: vertikaler StackPanel für die Anordnung aller Elemente -->
|
||||||
<StackPanel Margin="20" Spacing="10">
|
<StackPanel Margin="20" Spacing="10">
|
||||||
<TextBlock x:Name="UsernameTextBlock" Text="Benutzer: " FontSize="16" FontWeight="Bold" />
|
|
||||||
|
|
||||||
|
<!-- Überschrift: Benutzername (wird zur Laufzeit ergänzt) -->
|
||||||
|
<TextBlock x:Name="UsernameTextBlock"
|
||||||
|
Text="Benutzer: "
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="Bold" />
|
||||||
|
|
||||||
|
<!-- Eingabe neues Passwort -->
|
||||||
<TextBlock Text="Neues Passwort:" />
|
<TextBlock Text="Neues Passwort:" />
|
||||||
<TextBox x:Name="NeuesPasswortBox" PasswordChar="●" />
|
<TextBox x:Name="NeuesPasswortBox"
|
||||||
|
PasswordChar="●" /> <!-- Passwort wird maskiert -->
|
||||||
|
|
||||||
|
<!-- Eingabe Bestätigung des Passworts -->
|
||||||
<TextBlock Text="Passwort bestätigen:" />
|
<TextBlock Text="Passwort bestätigen:" />
|
||||||
<TextBox x:Name="BestaetigenBox" PasswordChar="●" />
|
<TextBox x:Name="BestaetigenBox"
|
||||||
|
PasswordChar="●" /> <!-- Wiederholung zur Sicherheit -->
|
||||||
|
|
||||||
<TextBlock x:Name="FehlerText" Foreground="Red" IsVisible="False" />
|
<!-- Fehlermeldung (standardmäßig ausgeblendet) -->
|
||||||
|
<TextBlock x:Name="FehlerText"
|
||||||
|
Foreground="Red"
|
||||||
|
IsVisible="False" />
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="10" Margin="0,10,0,0">
|
<!-- Aktionsbuttons (zentriert nebeneinander) -->
|
||||||
<Button Content="💾 Speichern" Click="SpeichernButton_Click" Width="100" />
|
<StackPanel Orientation="Horizontal"
|
||||||
<Button Content="❌ Abbrechen" Click="AbbrechenButton_Click" Width="100" />
|
HorizontalAlignment="Center"
|
||||||
|
Spacing="10"
|
||||||
|
Margin="0,10,0,0">
|
||||||
|
<Button Content="💾 Speichern"
|
||||||
|
Click="SpeichernButton_Click"
|
||||||
|
Width="100" />
|
||||||
|
<Button Content="❌ Abbrechen"
|
||||||
|
Click="AbbrechenButton_Click"
|
||||||
|
Width="100" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Window>
|
</Window>
|
||||||
Loading…
Reference in New Issue
Block a user