diff --git a/ChronoFlow.Controller/ZeiterfassungsController.cs b/ChronoFlow.Controller/ZeiterfassungsController.cs
index c7de1ec..42f4a35 100644
--- a/ChronoFlow.Controller/ZeiterfassungsController.cs
+++ b/ChronoFlow.Controller/ZeiterfassungsController.cs
@@ -1,20 +1,38 @@
using System.Collections.Generic;
using ChronoFlow.Model;
+using ChronoFlow.Persistence;
namespace ChronoFlow.Controller
{
+ ///
+ /// Vermittelt zwischen der View und dem Speichersystem (SQLite)
+ ///
public class ZeiterfassungsController
{
- private readonly List _eintraege = new();
+ private readonly SqliteZeiterfassungsService _dbService;
+ ///
+ /// Konstruktor: Initialisiert die Verbindung zum SQLite-Dienst
+ ///
+ public ZeiterfassungsController()
+ {
+ _dbService = new SqliteZeiterfassungsService();
+ }
+
+ ///
+ /// Speichert einen neuen Zeiteintrag dauerhaft in der SQLite-Datenbank
+ ///
public void SpeichereEintrag(Zeiteintrag eintrag)
{
- _eintraege.Add(eintrag);
+ _dbService.SpeichereEintrag(eintrag);
}
+ ///
+ /// Lädt alle vorhandenen Einträge aus der Datenbank
+ ///
public List LadeAlleEintraege()
{
- return new List(_eintraege);
+ return _dbService.LadeAlleZeiteintraege();
}
}
-}
+}
\ No newline at end of file
diff --git a/ChronoFlow.Model/User.cs b/ChronoFlow.Model/User.cs
index 27c19ca..a2247f6 100644
--- a/ChronoFlow.Model/User.cs
+++ b/ChronoFlow.Model/User.cs
@@ -10,5 +10,7 @@ namespace ChronoFlow.Model
public string Username { get; set; }
public string Password { get; set; } //vorerst im Klartext, wird später geändert
public string Role { get; set; } //"Admin" oder "Mitarbeiter"
+ public string Mitarbeiternummer { get; set; } = "";
+ public string Abteilung { get; set; } = "";
}
}
\ No newline at end of file
diff --git a/ChronoFlow.Persistence/ChronoFlow.Persistence.csproj b/ChronoFlow.Persistence/ChronoFlow.Persistence.csproj
index 7474d79..30dbf86 100644
--- a/ChronoFlow.Persistence/ChronoFlow.Persistence.csproj
+++ b/ChronoFlow.Persistence/ChronoFlow.Persistence.csproj
@@ -10,4 +10,8 @@
+
+
+
+
diff --git a/ChronoFlow.Persistence/SqliteZeiterfassungsService.cs b/ChronoFlow.Persistence/SqliteZeiterfassungsService.cs
new file mode 100644
index 0000000..c5147f4
--- /dev/null
+++ b/ChronoFlow.Persistence/SqliteZeiterfassungsService.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using Microsoft.Data.Sqlite;
+using ChronoFlow.Model;
+
+namespace ChronoFlow.Persistence
+{
+ public class SqliteZeiterfassungsService
+ {
+ private readonly string _dbPath = "chrono_data_v2.sb";
+
+ public SqliteZeiterfassungsService()
+ {
+ // Prüfe, ob die DB existiert, sonst erstelle sie
+ if (!File.Exists(_dbPath))
+ ErstelleDatenbank();
+ }
+
+ private void ErstelleDatenbank()
+ {
+ Console.WriteLine("🛠️ ErstelleDatenbank wurde aufgerufen!");
+
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ var cmd1 = connection.CreateCommand();
+ cmd1.CommandText = @"
+ CREATE TABLE IF NOT EXISTS Zeiteintraege (
+ Id INTEGER PRIMARY KEY AUTOINCREMENT,
+ Mitarbeiter TEXT NOT NULL,
+ Startzeit TEXT NOT NULL,
+ Endzeit TEXT NOT NULL,
+ Projekt TEXT,
+ Kommentar TEXT,
+ Erledigt INTEGER,
+ MitarbeiterKommentar TEXT
+ );";
+ cmd1.ExecuteNonQuery();
+
+ var cmd2 = connection.CreateCommand();
+ cmd2.CommandText = @"
+ CREATE TABLE IF NOT EXISTS Benutzer (
+ Id INTEGER PRIMARY KEY AUTOINCREMENT,
+ Username TEXT NOT NULL,
+ Password TEXT NOT NULL,
+ Role TEXT NOT NULL,
+ Mitarbeiternummer TEXT,
+ Abteilung TEXT
+ );";
+ cmd2.ExecuteNonQuery();
+ }
+
+
+ public void SpeichereEintrag(Zeiteintrag eintrag)
+ {
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ var cmd = connection.CreateCommand();
+ cmd.CommandText = @"
+ INSERT INTO Zeiteintraege
+ (Mitarbeiter, Startzeit, Endzeit, Projekt, Kommentar, Erledigt, MitarbeiterKommentar)
+ VALUES ($Mitarbeiter, $Startzeit, $Endzeit, $Projekt, $Kommentar, $Erledigt, $MitarbeiterKommentar);
+ ";
+
+ cmd.Parameters.AddWithValue("$Mitarbeiter", eintrag.Mitarbeiter);
+ cmd.Parameters.AddWithValue("$Startzeit", eintrag.Startzeit.ToString("o"));
+ cmd.Parameters.AddWithValue("$Endzeit", eintrag.Endzeit.ToString("o"));
+ cmd.Parameters.AddWithValue("$Projekt", eintrag.Projekt ?? "");
+ cmd.Parameters.AddWithValue("$Kommentar", eintrag.Kommentar ?? "");
+ cmd.Parameters.AddWithValue("$Erledigt", eintrag.Erledigt ? 1 : 0);
+ cmd.Parameters.AddWithValue("$MitarbeiterKommentar", eintrag.MitarbeiterKommentar ?? "");
+
+ cmd.ExecuteNonQuery();
+ }
+
+ public List LadeAlleZeiteintraege()
+ {
+ var eintraege = new List();
+
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ var cmd = connection.CreateCommand();
+ cmd.CommandText = "SELECT * FROM Zeiteintraege;";
+
+ using var reader = cmd.ExecuteReader();
+ while (reader.Read())
+ {
+ eintraege.Add(new Zeiteintrag
+ {
+ Mitarbeiter = reader.GetString(1),
+ Startzeit = DateTime.Parse(reader.GetString(2)),
+ Endzeit = DateTime.Parse(reader.GetString(3)),
+ Projekt = reader.GetString(4),
+ Kommentar = reader.GetString(5),
+ Erledigt = Convert.ToInt32(reader["Erledigt"]) == 1,
+ MitarbeiterKommentar = reader.GetString(7)
+ });
+ }
+
+ return eintraege;
+ }
+ public List LadeAlleMitarbeiterNamen()
+ {
+ var namen = new List();
+
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ var cmd = connection.CreateCommand();
+ cmd.CommandText = "SELECT Username From Benutzer Where Role = 'Mitarbeiter';";
+
+ using var reader = cmd.ExecuteReader();
+ while (reader.Read())
+ {
+ namen.Add(reader.GetString(0));
+ }
+ return namen;
+ }
+ public List LadeAlleBenutzer()
+ {
+ var benutzerListe = new List();
+
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ var cmd = connection.CreateCommand();
+ cmd.CommandText = "SELECT Username, Password, Role, Mitarbeiternummer, Abteilung FROM Benutzer;";
+
+ using var reader = cmd.ExecuteReader();
+ while (reader.Read())
+ {
+ benutzerListe.Add(new User
+ {
+ Username = reader.GetString(0),
+ Password = reader.GetString(1),
+ Role = reader.GetString(2),
+ Mitarbeiternummer = reader.IsDBNull(3) ? "" : reader.GetString(3),
+ Abteilung = reader.IsDBNull(4) ? "" : reader.GetString(4)
+ });
+ }
+
+ return benutzerListe;
+ }
+
+ public void ErstelleStandardAdmin()
+ {
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ var cmd = connection.CreateCommand();
+ cmd.CommandText = @"
+ INSERT INTO Benutzer (Username, Password, Role, Mitarbeiternummer, Abteilung)
+ VALUES ('admin', 'admin', 'Admin', '0001', 'IT');
+ ";
+
+ cmd.ExecuteNonQuery();
+
+ Console.WriteLine("✅ Standard-Admin erfolgreich eingefügt.");
+ }
+
+ public List LadeAlleBenutzernamen()
+ {
+ var benutzernamen = new List();
+
+ using var connection = new SqliteConnection($"Data Source={_dbPath}");
+ connection.Open();
+
+ using var cmd = connection.CreateCommand();//<--- Wir erstellen cmd
+ cmd.CommandText = "SELECT Username From Benutzer;";
+
+ using var reader = cmd.ExecuteReader();
+ while (reader.Read())
+ {
+ benutzernamen.Add(reader.GetString(0));
+ }
+ return benutzernamen;
+ }
+
+ }
+}
diff --git a/ChronoFlow.View/App.axaml.cs b/ChronoFlow.View/App.axaml.cs
index e27c051..930c6d5 100644
--- a/ChronoFlow.View/App.axaml.cs
+++ b/ChronoFlow.View/App.axaml.cs
@@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using ChronoFlow.View;
+using ChronoFlow.Persistence;
namespace ChronoFlow;
@@ -16,6 +17,13 @@ public partial class App : Application
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
+ // ⬇ Initialisiert den SQLite-Service beim Programmstart,
+ // um sicherzustellen, dass die Datenbank-Datei und alle benötigten Tabellen
+ // (z.B. Zeiteintraege, Benutzer) vorhanden sind.
+ // Ohne diesen Aufruf würden neue Tabellen nicht angelegt werden,
+ // falls die Datei chrono_data.sb noch nicht existiert.
+ var dbInit = new SqliteZeiterfassungsService();
+
// Starte das Programm mit dem Login-Fenster
desktop.MainWindow = new LoginWindow();
}
diff --git a/ChronoFlow.View/LoginWindow.axaml.cs b/ChronoFlow.View/LoginWindow.axaml.cs
index d732ab1..4520a55 100644
--- a/ChronoFlow.View/LoginWindow.axaml.cs
+++ b/ChronoFlow.View/LoginWindow.axaml.cs
@@ -1,6 +1,9 @@
+using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
+using Avalonia.Metadata;
using ChronoFlow.Controller;
+using ChronoFlow.Persistence;
namespace ChronoFlow.View
{
@@ -15,6 +18,9 @@ namespace ChronoFlow.View
{
InitializeComponent(); // Verbindet XAML mit diesem Code
_loginController = new LoginController(); // Unsere "Logik-Klasse"
+ var service = new SqliteZeiterfassungsService();
+ service.ErstelleStandardAdmin();
+
}
///
@@ -22,11 +28,20 @@ namespace ChronoFlow.View
///
private void LoginButton_Click(object? sender, RoutedEventArgs e)
{
- // Holt Benutzername und Passwort aus den Eingabefeldern
- string username = UsernameBox?.Text ?? string.Empty;
- string password = PasswordBox?.Text ?? string.Empty;
- // Übergibt die Eingaben an den LoginController
- var user = _loginController.Authenticate(username, password);
+ var username = UsernameBox.Text?.Trim();
+ var password = PasswordBox.Text?.Trim();
+
+ if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
+ {
+ ErrorText.Text = "Bitte Benutzername und Passwort eingeben";
+ ErrorText.IsVisible = true;
+ return;
+ }
+
+ var service = new SqliteZeiterfassungsService();
+ var benutzerListe = service.LadeAlleBenutzer();
+
+ var user = benutzerListe.FirstOrDefault(u => u.Username == username && u.Password == password);
if (user != null)
{
diff --git a/ChronoFlow.View/MainWindow.axaml b/ChronoFlow.View/MainWindow.axaml
index 89be531..76ba89e 100644
--- a/ChronoFlow.View/MainWindow.axaml
+++ b/ChronoFlow.View/MainWindow.axaml
@@ -6,13 +6,14 @@
x:Class="ChronoFlow.View.MainWindow"
Title="ChronoFlow.View">
-
+
+
diff --git a/ChronoFlow.View/MainWindow.axaml.cs b/ChronoFlow.View/MainWindow.axaml.cs
index ebde96a..ea79175 100644
--- a/ChronoFlow.View/MainWindow.axaml.cs
+++ b/ChronoFlow.View/MainWindow.axaml.cs
@@ -22,6 +22,7 @@ public partial class MainWindow : Window
// ✅ Register-Aufruf mit stabiler local variable
_viewManager.Register("Zeiterfassung", () => new ZeiterfassungView(currentUser));
+ _viewManager.Register("MitarbeiterHinzufuegen", () => new MitarbeiterHinzufuegenView());
// Begrüßungsanzeige
ContentArea.Content = new TextBlock
@@ -45,4 +46,9 @@ public partial class MainWindow : Window
{
_viewManager.Show("Zeiterfassung");
}
+
+ private void MitarbeiterHinzufuegen_Click(object? sender, RoutedEventArgs e)
+ {
+ _viewManager.Show("MitarbeiterHinzufuegen");
+ }
}
\ No newline at end of file
diff --git a/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml b/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml
new file mode 100644
index 0000000..e50d99f
--- /dev/null
+++ b/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs b/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs
new file mode 100644
index 0000000..799fdc1
--- /dev/null
+++ b/ChronoFlow.View/MitarbeiterHinzufuegenView.axaml.cs
@@ -0,0 +1,69 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+using ChronoFlow.Model;
+using Microsoft.Data.Sqlite;
+using ChronoFlow.Persistence;
+
+namespace ChronoFlow.View
+{
+ public partial class MitarbeiterHinzufuegenView : UserControl
+ {
+ public MitarbeiterHinzufuegenView()
+ {
+ InitializeComponent();
+ }
+
+ private void SpeichernButton_Click(object? sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var service = new SqliteZeiterfassungsService();
+
+ string username = UsernameBox.Text ?? "";
+ string password = PasswordBox.Text ?? "";
+ string rolle = (RoleBox.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "";
+ string mitarbeiternummer = MitarbeiternummerBox.Text ?? "";
+ string abteilung = AbteilungBox.Text ?? "";
+
+ if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(rolle))
+ {
+ FeedbackText.Text = "⚠ Bitte alle Pflichtfelder ausfüllen!";
+ FeedbackText.Foreground = Brushes.Red;
+ FeedbackText.IsVisible = true;
+ return;
+ }
+
+ using var connection = new SqliteConnection("Data Source=chrono_data.sb");
+ connection.Open();
+
+ var cmd = connection.CreateCommand();
+ cmd.CommandText = @"
+ INSERT INTO Benutzer (Username, Password, Role, Mitarbeiternummer, Abteilung)
+ VALUES ($Username, $Password, $Role, $Mitarbeiternummer, $Abteilung);";
+
+ cmd.Parameters.AddWithValue("$Username", username);
+ cmd.Parameters.AddWithValue("$Password", password);
+ cmd.Parameters.AddWithValue("$Role", rolle);
+ cmd.Parameters.AddWithValue("$Mitarbeiternummer", mitarbeiternummer);
+ cmd.Parameters.AddWithValue("$Abteilung", abteilung);
+ cmd.ExecuteNonQuery();
+
+ FeedbackText.Text = "✅ Mitarbeiter erfolgreich gespeichert.";
+ FeedbackText.Foreground = Brushes.Green;
+ FeedbackText.IsVisible = true;
+ }
+ catch (Exception ex)
+ {
+ FeedbackText.Text = $"❌ Fehler: {ex.Message}";
+ FeedbackText.Foreground = Brushes.Red;
+ FeedbackText.IsVisible = true;
+
+ Console.WriteLine("❌ Ausnahme beim Speichern:");
+ Console.WriteLine(ex.ToString());
+ }
+ }
+
+ }
+}
diff --git a/ChronoFlow.View/ViewManager.cs b/ChronoFlow.View/ViewManager.cs
index 3d06aff..5508b30 100644
--- a/ChronoFlow.View/ViewManager.cs
+++ b/ChronoFlow.View/ViewManager.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-using Avalonia.Collections;
using Avalonia.Controls;
+using Microsoft.Data.Sqlite;
namespace ChronoFlow.View
{
@@ -44,5 +44,6 @@ namespace ChronoFlow.View
{
_registieredViews[name] = viewFactory;
}
+
}
}
\ No newline at end of file
diff --git a/ChronoFlow.View/ZeiterfassungView.axaml b/ChronoFlow.View/ZeiterfassungView.axaml
index e8b7158..9575ad7 100644
--- a/ChronoFlow.View/ZeiterfassungView.axaml
+++ b/ChronoFlow.View/ZeiterfassungView.axaml
@@ -8,7 +8,10 @@
-
+
diff --git a/ChronoFlow.View/ZeiterfassungView.axaml.cs b/ChronoFlow.View/ZeiterfassungView.axaml.cs
index 91e0431..878144d 100644
--- a/ChronoFlow.View/ZeiterfassungView.axaml.cs
+++ b/ChronoFlow.View/ZeiterfassungView.axaml.cs
@@ -1,44 +1,59 @@
using System;
using System.Collections.ObjectModel;
+using System.Linq; // Wichtig für .Where und .Select
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using ChronoFlow.Controller;
using ChronoFlow.Model;
-
+using ChronoFlow.Persistence; // Wichtig für SqliteZeiterfassungsService
namespace ChronoFlow.View
{
public partial class ZeiterfassungView : UserControl
{
private readonly ZeiterfassungsController _controller;
-
- private ObservableCollection _anzeigeEinträge = new();
-
-
+ private readonly ObservableCollection _anzeigeEinträge;
private readonly User _user;
+
public ZeiterfassungView(User user)
{
InitializeComponent();
- _controller = new ZeiterfassungsController();
_user = user;
-
- //ListBox an Collection binden
+
+ _controller = new ZeiterfassungsController();
+ _anzeigeEinträge = new ObservableCollection();
+
+ // ✅ Benutzer aus Datenbank laden und Dropdown füllen
+ var benutzer = new SqliteZeiterfassungsService().LadeAlleBenutzer();
+ var nurMitarbeiter = benutzer
+ .Where(b => b.Role == "Mitarbeiter")
+ .Select(b => b.Username)
+ .ToList();
+
+ MitarbeiterBoxDropdown.ItemsSource = nurMitarbeiter;
+
+ // Einträge aus SQLite laden
+ var geladeneEintraege = _controller.LadeAlleEintraege();
+ foreach (var eintrag in geladeneEintraege)
+ {
+ _anzeigeEinträge.Add(eintrag);
+ }
+
Eintragsliste.ItemsSource = _anzeigeEinträge;
-
- //Eingabe-Felder für Nicht-Admins ausblenden
- if (_user.Role != "Admin")
+
+ // Eingabeformular nur für Admin sichtbar
+ if (_user.Role != "Admin" && EingabePanel != null)
{
EingabePanel.IsVisible = false;
}
-
}
private void SpeichernButton_Click(object? sender, RoutedEventArgs e)
{
try
{
- string mitarbeiter = MitarbeiterBox.Text ?? string.Empty;
+ string mitarbeiter = MitarbeiterBoxDropdown.SelectedItem?.ToString() ?? "";
DateTime datum = DatumPicker.SelectedDate?.Date ?? DateTime.Today;
string startText = StartzeitBox.Text ?? "";
string endText = EndzeitBox.Text ?? "";
@@ -65,11 +80,13 @@ namespace ChronoFlow.View
Startzeit = datum.Date + startZeit,
Endzeit = datum.Date + endZeit,
Projekt = ProjektBox.Text,
- Kommentar = KommentarBox.Text
+ Kommentar = KommentarBox.Text,
+ Erledigt = false
};
_controller.SpeichereEintrag(eintrag);
_anzeigeEinträge.Add(eintrag);
+
FeedbackText.Text = "Eintrag gespeichert.";
FeedbackText.Foreground = Brushes.Green;
FeedbackText.IsVisible = true;