diff --git a/ChronoFlow.Model/User.cs b/ChronoFlow.Model/User.cs
index c72a780..1b9819d 100644
--- a/ChronoFlow.Model/User.cs
+++ b/ChronoFlow.Model/User.cs
@@ -1,18 +1,70 @@
+using System;
+
namespace ChronoFlow.Model
{
+ ///
+ /// Repräsentiert einen Benutzer im System (Mitarbeiter oder Admin).
+ /// Wird für Authentifizierung, Rechteverwaltung und Zuordnung verwendet.
+ ///
public class User
{
+ ///
+ /// Der Benutzername, der zur Anmeldung verwendet wird.
+ ///
public string Username { get; set; }
+
+ ///
+ /// Das gehashte Passwort des Benutzers.
+ ///
public string Password { get; set; }
+
+ ///
+ /// Rolle des Benutzers, z.B. "Admin" oder "Mitarbeiter".
+ ///
public string Role { get; set; }
+
+ ///
+ /// Interne Mitarbeiternummer, optional.
+ ///
public string Mitarbeiternummer { get; set; }
+
+ ///
+ /// Abteilung des Mitarbeiters, z.B. „Produktion“, „IT“, etc.
+ ///
public string Abteilung { get; set; }
-
+
+ ///
+ /// Primärschlüssel in der Datenbank.
+ ///
public int Id { get; set; }
- public string OriginalUsername { get; set; } // wichtig für Updates
-
+
+ ///
+ /// Der ursprüngliche Benutzername – wird z.B. für Updates verwendet,
+ /// um Namensänderungen korrekt zu verarbeiten.
+ ///
+ public string OriginalUsername { get; set; }
+
+ ///
+ /// Gibt an, ob der Benutzer beim nächsten Login sein Passwort ändern muss.
+ /// Wird z.B. bei neu erstellten Konten gesetzt.
+ ///
public bool MussPasswortAendern { get; set; }
+ ///
+ /// Letzter erfolgreicher Login des Benutzers.
+ /// Dient z.B. zur Änderungsverfolgung von Projekten seit dem letzten Login.
+ ///
+ public DateTime LetzterLogin { get; set; } = DateTime.MinValue;
+
+ ///
+ /// Vorheriger Login-Zeitpunkt – wird beim nächsten Login zu LetzterLogin verschoben.
+ /// Ermöglicht die Erkennung von Änderungen zwischen zwei Sessions.
+ ///
+ public DateTime VorletzterLogin { get; set; }
+
+ ///
+ /// Konstruktor initialisiert leere Zeichenketten zur Vermeidung von Nullwerten.
+ ///
public User()
{
Username = "";
@@ -22,7 +74,5 @@ namespace ChronoFlow.Model
Abteilung = "";
OriginalUsername = "";
}
- public DateTime LetzterLogin { get; set; } = DateTime.MinValue;
- public DateTime VorletzterLogin { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/ChronoFlow.View/Admin/AbgeschlosseneProjekteView.axaml b/ChronoFlow.View/Admin/AbgeschlosseneProjekteView.axaml
index 3d9ea3e..947768b 100644
--- a/ChronoFlow.View/Admin/AbgeschlosseneProjekteView.axaml
+++ b/ChronoFlow.View/Admin/AbgeschlosseneProjekteView.axaml
@@ -2,25 +2,53 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model1="clr-namespace:ChronoFlow.Model;assembly=ChronoFlow.Model"
x:Class="ChronoFlow.View.Admin.AbgeschlosseneProjekteView">
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
\ No newline at end of file
+
diff --git a/ChronoFlow.View/Admin/AdminMainView.axaml.cs b/ChronoFlow.View/Admin/AdminMainView.axaml.cs
index 7ef619a..a3aade0 100644
--- a/ChronoFlow.View/Admin/AdminMainView.axaml.cs
+++ b/ChronoFlow.View/Admin/AdminMainView.axaml.cs
@@ -99,7 +99,7 @@ namespace ChronoFlow.View.Admin
}
///
- /// Wird z. B. vom ProjektErstellenView aufgerufen, um die Liste neu zu laden.
+ /// Wird z.B. vom ProjektErstellenView aufgerufen, um die Liste neu zu laden.
///
public void AktualisiereLetzteProjekte()
{
diff --git a/ChronoFlow.View/Admin/ConfirmDialog.axaml b/ChronoFlow.View/Admin/ConfirmDialog.axaml
index 9238ae5..bbcc5e3 100644
--- a/ChronoFlow.View/Admin/ConfirmDialog.axaml
+++ b/ChronoFlow.View/Admin/ConfirmDialog.axaml
@@ -4,12 +4,30 @@
Width="400" Height="200"
Title="Bestätigung">
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ChronoFlow.View/Admin/MitarbeiterListeView.axaml b/ChronoFlow.View/Admin/MitarbeiterListeView.axaml
index 33c0e61..e71efaa 100644
--- a/ChronoFlow.View/Admin/MitarbeiterListeView.axaml
+++ b/ChronoFlow.View/Admin/MitarbeiterListeView.axaml
@@ -2,28 +2,63 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model1="clr-namespace:ChronoFlow.Model;assembly=ChronoFlow.Model"
x:Class="ChronoFlow.View.Admin.MitarbeiterListeView">
+
+
-
-
+
+
+
+
+
+
+
-
+
+
+
-
-
-
+
+
+
+
+
+
+
-
+
+
-
\ No newline at end of file
+
diff --git a/ChronoFlow.View/Admin/ProjektErstellenView.axaml b/ChronoFlow.View/Admin/ProjektErstellenView.axaml
index 1fb5f3e..1ba8701 100644
--- a/ChronoFlow.View/Admin/ProjektErstellenView.axaml
+++ b/ChronoFlow.View/Admin/ProjektErstellenView.axaml
@@ -2,70 +2,64 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ChronoFlow.View.Admin.ProjektErstellenView">
-
-
+
+
-
+
-
+
-
+
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
-
+
+
@@ -77,28 +71,14 @@
-
-
-
-
-
-
-
+
+
+
+
+
-
+
_mitarbeiterCheckBoxMap = new();
public ProjektErstellenView() : this(new ViewManager(new ContentControl()))
{
@@ -30,114 +32,153 @@ public partial class ProjektErstellenView : UserControl
try
{
var dbService = new SqliteZeiterfassungsService();
- List mitarbeiter = dbService.LadeAlleMitarbeiterNamen();
+ var mitarbeiter = dbService.LadeAlleMitarbeiterNamen();
- // 🔍 Konsolenausgabe zur Kontrolle
if (mitarbeiter is { Count: > 0 })
{
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();
+
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
{
Console.WriteLine("⚠️ Achtung: Mitarbeitendenliste ist leer.");
}
-
- // Items in GUI setzen
- MitarbeiterListBox!.ItemsSource = mitarbeiter;
- ProjektleiterDropdown!.ItemsSource = mitarbeiter;
}
catch (Exception ex)
{
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.IsVisible = true;
}
}
+ private List 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)
-{
- 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().ToList() ?? new List();
-
- if (string.IsNullOrWhiteSpace(projektname) || ausgewaehlteMitarbeiter.Count == 0 || string.IsNullOrWhiteSpace(projektleiter))
{
- FeedbackText.Text = "⚠ Bitte alle Pflichtfelder ausfüllen (Projektname, mindestens ein Mitarbeiter, Projektleiter)!";
- FeedbackText.Foreground = Brushes.Red;
- FeedbackText.IsVisible = true;
- return;
- }
+ FeedbackText.IsVisible = false;
- if (!TimeSpan.TryParse(startzeitText, out var startzeit) || !TimeSpan.TryParse(endzeitText, out var endzeit))
- {
- FeedbackText.Text = "⚠ Ungültige Zeitangaben (Format HH:mm)!";
- FeedbackText.Foreground = Brushes.Red;
- FeedbackText.IsVisible = true;
- return;
- }
+ 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() ?? "";
- DateTime startDateTime = startdatum + startzeit;
- DateTime endDateTime = enddatum + endzeit;
+ var ausgewaehlteMitarbeiter = ErmittleAusgewaehlteMitarbeiter();
- try
- {
- var dbService = new SqliteZeiterfassungsService();
-
- foreach (var einzelnerMitarbeiter in ausgewaehlteMitarbeiter)
+ if (string.IsNullOrWhiteSpace(projektname) || ausgewaehlteMitarbeiter.Count == 0 || string.IsNullOrWhiteSpace(projektleiter))
{
- dbService.SpeichereEintrag(new Zeiteintrag
- {
- Mitarbeiter = einzelnerMitarbeiter,
- Projekt = projektname,
- Startzeit = startDateTime,
- Endzeit = endDateTime,
- Kommentar = kommentar,
- Projektleiter = projektleiter,
- Erledigt = false
- });
+ FeedbackText.Text = "⚠ Bitte alle Pflichtfelder ausfüllen (Projektname, mindestens ein Mitarbeiter, Projektleiter)!";
+ FeedbackText.Foreground = Brushes.Red;
+ FeedbackText.IsVisible = true;
+ return;
}
- FeedbackText.Text = "✅ Projekt erfolgreich gespeichert.";
- FeedbackText.Foreground = Brushes.Green;
- FeedbackText.IsVisible = true;
+ if (!TimeSpan.TryParse(startzeitText, out var startzeit) || !TimeSpan.TryParse(endzeitText, out var endzeit))
+ {
+ FeedbackText.Text = "⚠ Ungültige Zeitangaben (Format HH:mm)!";
+ FeedbackText.Foreground = Brushes.Red;
+ FeedbackText.IsVisible = true;
+ return;
+ }
- ProjektnameBox.Text = "";
- KommentarBox.Text = "";
- 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;
+ DateTime startDateTime = startdatum + startzeit;
+ DateTime endDateTime = enddatum + endzeit;
- if (_viewManager.TryGetView("AdminMain", out var adminView) && adminView != null)
- adminView.AktualisiereLetzteProjekte();
+ try
+ {
+ 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("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)
{
@@ -149,7 +190,9 @@ public partial class ProjektErstellenView : UserControl
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.Foreground = Brushes.Red;
@@ -160,8 +203,7 @@ public partial class ProjektErstellenView : UserControl
var service = new SqliteZeiterfassungsService();
var heute = DateTime.Today;
- var mitarbeiter = MitarbeiterListBox?.SelectedItems?.Cast().ToList() ?? new List();
- foreach (var name in mitarbeiter)
+ foreach (var name in ausgewaehlteMitarbeiter)
{
var projekte = new List
{
diff --git a/ChronoFlow.View/Converter/BoolToBrushConverter.cs b/ChronoFlow.View/Converter/BoolToBrushConverter.cs
index 46f3592..abeda18 100644
--- a/ChronoFlow.View/Converter/BoolToBrushConverter.cs
+++ b/ChronoFlow.View/Converter/BoolToBrushConverter.cs
@@ -5,19 +5,44 @@ using Avalonia.Media;
namespace ChronoFlow.View.Converter
{
+ ///
+ /// Konvertiert einen booleschen Wert in eine Farbe (Brush).
+ /// True => Blau, False => Grau.
+ /// Wird z. B. für farbliche Statusanzeigen in der Oberfläche verwendet.
+ ///
public class BoolToBrushConverter : IValueConverter
{
+ ///
+ /// Die Farbe, die bei "true" verwendet wird.
+ /// Kann in XAML überschrieben werden.
+ ///
public IBrush TrueBrush { get; set; } = Brushes.Blue;
+
+ ///
+ /// Die Farbe, die bei "false" verwendet wird.
+ /// Kann in XAML überschrieben werden.
+ ///
public IBrush FalseBrush { get; set; } = Brushes.Gray;
+ ///
+ /// Führt die Umwandlung des bool-Wertes in eine Brush durch.
+ ///
+ /// Der Wert, der umgewandelt werden soll (sollte bool sein).
+ /// Zieltyp der Bindung (erwartet IBrush).
+ /// Optionaler Parameter (nicht verwendet).
+ /// Kultureinstellungen (nicht verwendet).
+ /// TrueBrush oder FalseBrush abhängig vom bool-Wert.
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is bool boolValue && boolValue)
- return Brushes.Blue;
+ return TrueBrush;
- return Brushes.Gray;
+ return FalseBrush;
}
+ ///
+ /// Umkehrkonvertierung wird hier nicht benötigt.
+ ///
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException(); // wird i.d.R. nicht benötigt
diff --git a/ChronoFlow.View/LoginWindow.axaml.cs b/ChronoFlow.View/LoginWindow.axaml.cs
index c270df4..cae9537 100644
--- a/ChronoFlow.View/LoginWindow.axaml.cs
+++ b/ChronoFlow.View/LoginWindow.axaml.cs
@@ -11,14 +11,15 @@ using Microsoft.Data.Sqlite;
namespace ChronoFlow.View;
///
-/// 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.
///
public partial class LoginWindow : Window
{
private readonly LoginController _loginController;
///
- /// Konstruktor – Initialisiert die Oberfläche und legt bei Bedarf einen Standard-Admin an.
+ /// Konstruktor – Initialisiert UI und legt bei Bedarf den Standard-Admin an.
///
public LoginWindow()
{
@@ -28,7 +29,7 @@ public partial class LoginWindow : Window
try
{
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)
{
@@ -43,13 +44,14 @@ public partial class LoginWindow : Window
}
///
- /// Wird aufgerufen, wenn der Benutzer auf "Anmelden" klickt.
+ /// Wird aufgerufen, wenn der Benutzer auf „Anmelden“ klickt.
///
private async void LoginButton_Click(object? sender, RoutedEventArgs e)
{
var username = UsernameBox.Text?.Trim();
var password = PasswordBox.Text?.Trim();
+ // Validierung: Felder dürfen nicht leer sein
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
ErrorText.Text = "Bitte Benutzername und Passwort eingeben.";
@@ -64,13 +66,14 @@ public partial class LoginWindow : Window
}
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;
return;
}
var benutzerListe = service.LadeAlleBenutzer();
+ // Benutzer mit eingegebenem Namen (case-insensitive) finden
var matchingUsers = benutzerListe
.Where(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase))
.ToList();
@@ -91,6 +94,7 @@ public partial class LoginWindow : Window
var user = matchingUsers.First();
+ // Passwort-Überprüfung mit Hashing
if (!PasswordHasher.VerifyPassword(password, user.Password))
{
ErrorText.Text = "Falsches Passwort. Bitte erneut versuchen.";
@@ -98,7 +102,7 @@ public partial class LoginWindow : Window
return;
}
- // Passwort muss geändert werden
+ // Benutzer muss Passwort ändern? → Dialog anzeigen
if (user.MussPasswortAendern)
{
var dialog = new PasswortAendernDialog(user);
@@ -121,13 +125,13 @@ public partial class LoginWindow : Window
try
{
- // Login-Zeiten aktualisieren
+ // Login-Zeit aktualisieren (für spätere Änderungsbenachrichtigungen)
var vorher = user.LetzterLogin;
user.LetzterLogin = DateTime.Now;
user.VorletzterLogin = vorher;
service.UpdateLoginZeiten(user);
- // Hauptfenster öffnen
+ // Hauptfenster öffnen und Login-Fenster schließen
var main = new MainWindow(user);
main.Show();
main.Activate();
diff --git a/ChronoFlow.View/MainWindow.axaml.cs b/ChronoFlow.View/MainWindow.axaml.cs
index 2d9a287..5631f09 100644
--- a/ChronoFlow.View/MainWindow.axaml.cs
+++ b/ChronoFlow.View/MainWindow.axaml.cs
@@ -91,7 +91,7 @@ public partial class MainWindow : Window
}
///
- /// 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).
///
public void ShowAdminDashboard()
{
diff --git a/ChronoFlow.View/Mitarbeiter/EmployeeTasksViewModel.cs b/ChronoFlow.View/Mitarbeiter/EmployeeTasksViewModel.cs
index 0370f66..71fc2e7 100644
--- a/ChronoFlow.View/Mitarbeiter/EmployeeTasksViewModel.cs
+++ b/ChronoFlow.View/Mitarbeiter/EmployeeTasksViewModel.cs
@@ -11,71 +11,86 @@ using ChronoFlow.Controller;
namespace ChronoFlow.View.Mitarbeiter;
///
-/// ViewModel für die Mitarbeiter-Aufgabenansicht.
-/// Enthält alle Aufgaben für den eingeloggten Benutzer und erlaubt das Speichern von Änderungen.
+/// ViewModel für die Aufgabenansicht eines Mitarbeiters.
+/// Lädt relevante Aufgaben (nicht erledigt) und ermöglicht das Speichern von Kommentaren und Bearbeitungsstatus.
///
public partial class EmployeeTasksViewModel : ObservableObject
{
- private readonly User _benutzer;
-
- private readonly ZeiterfassungsController controller = new();
-
- private readonly string aktuellerBenutzername;
+ private readonly User _benutzer; // Der aktuell eingeloggte Benutzer
+ private readonly ZeiterfassungsController controller = new(); // Controller für Datenzugriff
+ private readonly string aktuellerBenutzername; // Benutzername (String, z. B. für Vergleich)
+ // Liste aller offenen, relevanten Aufgaben für diesen Benutzer
[ObservableProperty]
private ObservableCollection eintraege = new();
+ // Status-Text für Hinweise oder Rückmeldungen an den Benutzer
[ObservableProperty]
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;
+ // Wenn sich die Einträge ändern, wird auch das Property `HatKeineEintraege` neu berechnet
partial void OnEintraegeChanged(ObservableCollection? oldValue, ObservableCollection newValue)
{
OnPropertyChanged(nameof(HatKeineEintraege));
}
+ ///
+ /// Konstruktor – übernimmt eingeloggten Benutzer, speichert Name für spätere Vergleiche
+ /// und lädt initial die relevanten Einträge.
+ ///
public EmployeeTasksViewModel(User benutzer)
{
_benutzer = benutzer;
aktuellerBenutzername = benutzer.Username;
- controller = new ZeiterfassungsController(); // falls du das oben nicht hast
- _ = LadeEintraegeAsync();
+ controller = new ZeiterfassungsController();
+ _ = LadeEintraegeAsync(); // asynchrone Initialladung
}
-
+ ///
+ /// 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.
+ ///
[RelayCommand]
public async Task LadeEintraegeAsync()
{
- // 🔄 Einträge synchron laden, aber async verpacken
+ // Alle Aufgaben laden (aus Datenbank)
var alleEintraege = await Task.Run(() => controller.LadeAlleEintraege());
+ // Relevante Aufgaben filtern: noch nicht erledigt und betreffen den Benutzer
var relevanteEintraege = alleEintraege
.Where(e => !e.Erledigt &&
(e.Mitarbeiter == aktuellerBenutzername || e.Projektleiter == aktuellerBenutzername))
.OrderBy(e => e.Endzeit)
.ToList();
+ // Prüfung, ob Aufgabe nach letztem Login geändert wurde
foreach (var eintrag in relevanteEintraege)
{
eintrag.WurdeSeitLoginBearbeitet = eintrag.LetzteBearbeitung > _benutzer.VorletzterLogin;
}
+ // Hinweis anzeigen, wenn Aufgaben seit letztem Login verändert wurden
if (relevanteEintraege.Any(e => e.WurdeSeitLoginBearbeitet))
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(relevanteEintraege);
}
+ ///
+ /// Speichert alle Änderungen (Kommentar + Erledigt-Status) der aktuellen Aufgaben.
+ /// Danach wird die Liste neu geladen.
+ ///
[RelayCommand]
public async Task SpeichereEintraegeAsync()
{
- // Achtung: Hier verwendest du noch das Repository, obwohl du oben mit dem Controller arbeitest.
- // Du kannst entweder:
- // A) Das Repository auch im ViewModel übergeben
- // B) Die Methode UpdateStatusUndKommentarAsync auch in den Controller packen
-
+ // Achtung: Diese Methode ruft direkt den Service auf.
+ // Dies könnte in Zukunft auch über den Controller geschehen.
foreach (var eintrag in Eintraege)
{
await Task.Run(() =>
@@ -85,6 +100,7 @@ public partial class EmployeeTasksViewModel : ObservableObject
});
}
+ // Rückmeldung anzeigen und Liste aktualisieren
StatusText = "✅ Änderungen gespeichert.";
await LadeEintraegeAsync();
}
diff --git a/ChronoFlow.View/Mitarbeiter/MitarbeiterMainView.axaml.cs b/ChronoFlow.View/Mitarbeiter/MitarbeiterMainView.axaml.cs
index f8649f0..4d291d2 100644
--- a/ChronoFlow.View/Mitarbeiter/MitarbeiterMainView.axaml.cs
+++ b/ChronoFlow.View/Mitarbeiter/MitarbeiterMainView.axaml.cs
@@ -17,7 +17,7 @@ public partial class MitarbeiterMainView : UserControl
private readonly ObservableCollection _notifications = new();
///
- /// Ö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.
///
public MitarbeiterMainView()
diff --git a/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs b/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs
index 1a0c312..81e3f7e 100644
--- a/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs
+++ b/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs
@@ -109,33 +109,44 @@ namespace ChronoFlow.View
///
private void DemoBenutzerErstellen_Click(object? sender, RoutedEventArgs e)
{
- var namen = new[] { "Max Mustermann", "Lena Schmidt", "Tobias Becker", "Julia Klein", "Nico Weber" };
- var zufallsname = namen[new Random().Next(namen.Length)];
+ string[] vornamen = { "Max", "Lena", "Tobias", "Julia", "Nico", "Emma", "Finn", "Sophie", "Noah", "Laura" };
+ string[] nachnamen = { "Mustermann", "Schmidt", "Becker", "Klein", "Weber", "Meier", "Schulz", "Huber", "Lang", "Richter" };
var service = new SqliteZeiterfassungsService();
+ var existierendeUsernames = service.LadeAlleBenutzer().Select(u => u.Username).ToHashSet(StringComparer.OrdinalIgnoreCase);
- // Prüfen, ob Name bereits vorhanden ist
- if (service.LadeAlleBenutzer().Any(u => u.Username.Equals(zufallsname, StringComparison.OrdinalIgnoreCase)))
+ var rnd = new Random();
+ int versuche = 0;
+
+ while (versuche < 100)
{
- FeedbackText.Text = "⚠ Demo-Mitarbeiter existiert bereits.";
- FeedbackText.Foreground = Brushes.OrangeRed;
- FeedbackText.IsVisible = true;
- return;
+ string vorname = vornamen[rnd.Next(vornamen.Length)];
+ string nachname = nachnamen[rnd.Next(nachnamen.Length)];
+ string name = $"{vorname} {nachname}";
+
+ 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
- var demoUser = new User
- {
- 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.Text = "❌ Fehler: Konnte keinen eindeutigen Demo-Mitarbeiter generieren.";
+ FeedbackText.Foreground = Brushes.Red;
FeedbackText.IsVisible = true;
}
}
diff --git a/ChronoFlow.View/Security/PasswortAendernDialog.axaml b/ChronoFlow.View/Security/PasswortAendernDialog.axaml
index 6a8f430..b2af92b 100644
--- a/ChronoFlow.View/Security/PasswortAendernDialog.axaml
+++ b/ChronoFlow.View/Security/PasswortAendernDialog.axaml
@@ -4,20 +4,41 @@
Width="400" Height="250"
Title="Passwort ändern">
+
-
+
+
+
+
-
+
+
-
+
-
+
+
-
-
-
+
+
+
+
\ No newline at end of file