diverse änderungen

This commit is contained in:
Viperion 2025-04-27 21:28:33 +02:00
parent 176ff2f211
commit dc760febde
13 changed files with 376 additions and 26 deletions

View File

@ -1,20 +1,38 @@
using System.Collections.Generic; using System.Collections.Generic;
using ChronoFlow.Model; using ChronoFlow.Model;
using ChronoFlow.Persistence;
namespace ChronoFlow.Controller namespace ChronoFlow.Controller
{ {
/// <summary>
/// Vermittelt zwischen der View und dem Speichersystem (SQLite)
/// </summary>
public class ZeiterfassungsController public class ZeiterfassungsController
{ {
private readonly List<Zeiteintrag> _eintraege = new(); private readonly SqliteZeiterfassungsService _dbService;
/// <summary>
/// Konstruktor: Initialisiert die Verbindung zum SQLite-Dienst
/// </summary>
public ZeiterfassungsController()
{
_dbService = new SqliteZeiterfassungsService();
}
/// <summary>
/// Speichert einen neuen Zeiteintrag dauerhaft in der SQLite-Datenbank
/// </summary>
public void SpeichereEintrag(Zeiteintrag eintrag) public void SpeichereEintrag(Zeiteintrag eintrag)
{ {
_eintraege.Add(eintrag); _dbService.SpeichereEintrag(eintrag);
} }
/// <summary>
/// Lädt alle vorhandenen Einträge aus der Datenbank
/// </summary>
public List<Zeiteintrag> LadeAlleEintraege() public List<Zeiteintrag> LadeAlleEintraege()
{ {
return new List<Zeiteintrag>(_eintraege); return _dbService.LadeAlleZeiteintraege();
} }
} }
} }

View File

@ -10,5 +10,7 @@ namespace ChronoFlow.Model
public string Username { get; set; } public string Username { get; set; }
public string Password { get; set; } //vorerst im Klartext, wird später geändert public string Password { get; set; } //vorerst im Klartext, wird später geändert
public string Role { get; set; } //"Admin" oder "Mitarbeiter" public string Role { get; set; } //"Admin" oder "Mitarbeiter"
public string Mitarbeiternummer { get; set; } = "";
public string Abteilung { get; set; } = "";
} }
} }

View File

@ -10,4 +10,8 @@
<ProjectReference Include="..\ChronoFlow.Model\ChronoFlow.Model.csproj" /> <ProjectReference Include="..\ChronoFlow.Model\ChronoFlow.Model.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.0-preview.3.25171.6" />
</ItemGroup>
</Project> </Project>

View File

@ -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<Zeiteintrag> LadeAlleZeiteintraege()
{
var eintraege = new List<Zeiteintrag>();
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<string> LadeAlleMitarbeiterNamen()
{
var namen = new List<string>();
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<User> LadeAlleBenutzer()
{
var benutzerListe = new List<User>();
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<string> LadeAlleBenutzernamen()
{
var benutzernamen = new List<string>();
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;
}
}
}

View File

@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using ChronoFlow.View; using ChronoFlow.View;
using ChronoFlow.Persistence;
namespace ChronoFlow; namespace ChronoFlow;
@ -16,6 +17,13 @@ public partial class App : Application
{ {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 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 // Starte das Programm mit dem Login-Fenster
desktop.MainWindow = new LoginWindow(); desktop.MainWindow = new LoginWindow();
} }

View File

@ -1,6 +1,9 @@
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Metadata;
using ChronoFlow.Controller; using ChronoFlow.Controller;
using ChronoFlow.Persistence;
namespace ChronoFlow.View namespace ChronoFlow.View
{ {
@ -15,6 +18,9 @@ namespace ChronoFlow.View
{ {
InitializeComponent(); // Verbindet XAML mit diesem Code InitializeComponent(); // Verbindet XAML mit diesem Code
_loginController = new LoginController(); // Unsere "Logik-Klasse" _loginController = new LoginController(); // Unsere "Logik-Klasse"
var service = new SqliteZeiterfassungsService();
service.ErstelleStandardAdmin();
} }
/// <summary> /// <summary>
@ -22,11 +28,20 @@ namespace ChronoFlow.View
/// </summary> /// </summary>
private void LoginButton_Click(object? sender, RoutedEventArgs e) private void LoginButton_Click(object? sender, RoutedEventArgs e)
{ {
// Holt Benutzername und Passwort aus den Eingabefeldern var username = UsernameBox.Text?.Trim();
string username = UsernameBox?.Text ?? string.Empty; var password = PasswordBox.Text?.Trim();
string password = PasswordBox?.Text ?? string.Empty;
// Übergibt die Eingaben an den LoginController if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
var user = _loginController.Authenticate(username, 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) if (user != null)
{ {

View File

@ -6,13 +6,14 @@
x:Class="ChronoFlow.View.MainWindow" x:Class="ChronoFlow.View.MainWindow"
Title="ChronoFlow.View"> Title="ChronoFlow.View">
<SplitView x:Name="PaneView" DisplayMode="CompactInline" IsPaneOpen="True" CompactPaneLength="40" OpenPaneLength="150"> <SplitView x:Name="PaneView" DisplayMode="CompactInline" IsPaneOpen="True" CompactPaneLength="38" OpenPaneLength="200">
<SplitView.Pane> <SplitView.Pane>
<StackPanel> <StackPanel>
<Button Content="☰" Click="PaneOpenClose_Click"/> <Button Content="☰" Click="PaneOpenClose_Click"/>
<Button Content="⏱ Zeiterfassung" Click="Zeiterfassung_Click"/> <Button Content="⏱ Zeiterfassung" Click="Zeiterfassung_Click"/>
<Button Content="📄 Auswertung"/> <Button Content="📄 Auswertung"/>
<Button Content="👤 Mitarbeiter hinzufügen" Click="MitarbeiterHinzufuegen_Click"/>
<Button Content="⚙ Einstellungen"/> <Button Content="⚙ Einstellungen"/>
</StackPanel> </StackPanel>
</SplitView.Pane> </SplitView.Pane>

View File

@ -22,6 +22,7 @@ public partial class MainWindow : Window
// ✅ Register-Aufruf mit stabiler local variable // ✅ Register-Aufruf mit stabiler local variable
_viewManager.Register("Zeiterfassung", () => new ZeiterfassungView(currentUser)); _viewManager.Register("Zeiterfassung", () => new ZeiterfassungView(currentUser));
_viewManager.Register("MitarbeiterHinzufuegen", () => new MitarbeiterHinzufuegenView());
// Begrüßungsanzeige // Begrüßungsanzeige
ContentArea.Content = new TextBlock ContentArea.Content = new TextBlock
@ -45,4 +46,9 @@ public partial class MainWindow : Window
{ {
_viewManager.Show("Zeiterfassung"); _viewManager.Show("Zeiterfassung");
} }
private void MitarbeiterHinzufuegen_Click(object? sender, RoutedEventArgs e)
{
_viewManager.Show("MitarbeiterHinzufuegen");
}
} }

View File

@ -0,0 +1,22 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ChronoFlow.View.MitarbeiterHinzufuegenView">
<StackPanel Margin="20" Spacing="10">
<TextBlock Text=" Mitarbeiter hinzufügen" FontWeight="Bold" FontSize="20" HorizontalAlignment="Center"/>
<TextBox x:Name="UsernameBox" Watermark="Benutzername"/>
<TextBox x:Name="PasswordBox" Watermark="Passwort"/>
<ComboBox x:Name="RoleBox" PlaceholderText="Rolle auswählen">
<ComboBoxItem Content="Mitarbeiter"/>
<ComboBoxItem Content="Admin"/>
</ComboBox>
<TextBox x:Name="MitarbeiternummerBox" Watermark="Mitarbeiternummer"/>
<TextBox x:Name="AbteilungBox" Watermark="Abteilung"/>
<Button Content="💾 Speichern" Click="SpeichernButton_Click" HorizontalAlignment="Center"/>
<TextBlock x:Name="FeedbackText" Foreground="Green" IsVisible="False" TextAlignment="Center"/>
</StackPanel>
</UserControl>

View File

@ -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());
}
}
}
}

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Microsoft.Data.Sqlite;
namespace ChronoFlow.View namespace ChronoFlow.View
{ {
@ -44,5 +44,6 @@ namespace ChronoFlow.View
{ {
_registieredViews[name] = viewFactory; _registieredViews[name] = viewFactory;
} }
} }
} }

View File

@ -8,7 +8,10 @@
<StackPanel x:Name="EingabePanel" Spacing="10"> <StackPanel x:Name="EingabePanel" Spacing="10">
<TextBlock Text="Zeiterfassung" FontWeight="Bold" FontSize="20" HorizontalAlignment="Center"/> <TextBlock Text="Zeiterfassung" FontWeight="Bold" FontSize="20" HorizontalAlignment="Center"/>
<TextBox x:Name="MitarbeiterBox" Watermark="Mitarbeitername"/> <ComboBox x:Name="MitarbeiterBoxDropdown"
Width="250"
PlaceholderText="Mitarbeitername auswählen"
Margin="0,5"/>
<DatePicker x:Name="DatumPicker"/> <DatePicker x:Name="DatumPicker"/>
<TextBox x:Name="StartzeitBox" Watermark="Startzeit (z.B. 08:00)"/> <TextBox x:Name="StartzeitBox" Watermark="Startzeit (z.B. 08:00)"/>
<TextBox x:Name="EndzeitBox" Watermark="Endzeit (z.B. 16:30)"/> <TextBox x:Name="EndzeitBox" Watermark="Endzeit (z.B. 16:30)"/>

View File

@ -1,44 +1,59 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; // Wichtig für .Where und .Select
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using ChronoFlow.Controller; using ChronoFlow.Controller;
using ChronoFlow.Model; using ChronoFlow.Model;
using ChronoFlow.Persistence; // Wichtig für SqliteZeiterfassungsService
namespace ChronoFlow.View namespace ChronoFlow.View
{ {
public partial class ZeiterfassungView : UserControl public partial class ZeiterfassungView : UserControl
{ {
private readonly ZeiterfassungsController _controller; private readonly ZeiterfassungsController _controller;
private readonly ObservableCollection<Zeiteintrag> _anzeigeEinträge;
private ObservableCollection<Zeiteintrag> _anzeigeEinträge = new();
private readonly User _user; private readonly User _user;
public ZeiterfassungView(User user) public ZeiterfassungView(User user)
{ {
InitializeComponent(); InitializeComponent();
_controller = new ZeiterfassungsController();
_user = user; _user = user;
//ListBox an Collection binden _controller = new ZeiterfassungsController();
_anzeigeEinträge = new ObservableCollection<Zeiteintrag>();
// ✅ 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; Eintragsliste.ItemsSource = _anzeigeEinträge;
//Eingabe-Felder für Nicht-Admins ausblenden // Eingabeformular nur für Admin sichtbar
if (_user.Role != "Admin") if (_user.Role != "Admin" && EingabePanel != null)
{ {
EingabePanel.IsVisible = false; EingabePanel.IsVisible = false;
} }
} }
private void SpeichernButton_Click(object? sender, RoutedEventArgs e) private void SpeichernButton_Click(object? sender, RoutedEventArgs e)
{ {
try try
{ {
string mitarbeiter = MitarbeiterBox.Text ?? string.Empty; string mitarbeiter = MitarbeiterBoxDropdown.SelectedItem?.ToString() ?? "";
DateTime datum = DatumPicker.SelectedDate?.Date ?? DateTime.Today; DateTime datum = DatumPicker.SelectedDate?.Date ?? DateTime.Today;
string startText = StartzeitBox.Text ?? ""; string startText = StartzeitBox.Text ?? "";
string endText = EndzeitBox.Text ?? ""; string endText = EndzeitBox.Text ?? "";
@ -65,11 +80,13 @@ namespace ChronoFlow.View
Startzeit = datum.Date + startZeit, Startzeit = datum.Date + startZeit,
Endzeit = datum.Date + endZeit, Endzeit = datum.Date + endZeit,
Projekt = ProjektBox.Text, Projekt = ProjektBox.Text,
Kommentar = KommentarBox.Text Kommentar = KommentarBox.Text,
Erledigt = false
}; };
_controller.SpeichereEintrag(eintrag); _controller.SpeichereEintrag(eintrag);
_anzeigeEinträge.Add(eintrag); _anzeigeEinträge.Add(eintrag);
FeedbackText.Text = "Eintrag gespeichert."; FeedbackText.Text = "Eintrag gespeichert.";
FeedbackText.Foreground = Brushes.Green; FeedbackText.Foreground = Brushes.Green;
FeedbackText.IsVisible = true; FeedbackText.IsVisible = true;