comments und quellen hinzufugt
This commit is contained in:
BIN
Screenshot 2026-03-04 100331.ico
Normal file
BIN
Screenshot 2026-03-04 100331.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
@@ -11,6 +11,7 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
public AdminDashboard()
|
public AdminDashboard()
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
LoadUsers();
|
LoadUsers();
|
||||||
LoadFlights();
|
LoadFlights();
|
||||||
@@ -19,12 +20,15 @@ namespace SkyTeam
|
|||||||
|
|
||||||
private void LoadUsers()
|
private void LoadUsers()
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
BindGrid("SELECT Id, Vorname, Nachname, Email, Rolle FROM users", AllUsersGrid);
|
BindGrid("SELECT Id, Vorname, Nachname, Email, Rolle FROM users", AllUsersGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AllUsersGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void AllUsersGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (AllUsersGrid.SelectedItem == null) return;
|
if (AllUsersGrid.SelectedItem == null) return;
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
DataRowView row = (DataRowView)AllUsersGrid.SelectedItem;
|
DataRowView row = (DataRowView)AllUsersGrid.SelectedItem;
|
||||||
int userId = Convert.ToInt32(row["Id"]);
|
int userId = Convert.ToInt32(row["Id"]);
|
||||||
BindGrid($"SELECT b.Id AS BuchungId, f.Flugnummer, f.Abflugort, f.Zielort, f.Abflugdatum FROM buchungen b JOIN fluege f ON b.FlugId = f.Id WHERE b.UserId = {userId}", UserBookingsGrid);
|
BindGrid($"SELECT b.Id AS BuchungId, f.Flugnummer, f.Abflugort, f.Zielort, f.Abflugdatum FROM buchungen b JOIN fluege f ON b.FlugId = f.Id WHERE b.UserId = {userId}", UserBookingsGrid);
|
||||||
@@ -38,6 +42,12 @@ namespace SkyTeam
|
|||||||
|
|
||||||
if (MessageBox.Show($"User {uid} löschen?", "Confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
if (MessageBox.Show($"User {uid} löschen?", "Confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
{
|
{
|
||||||
|
// Quelle: AI Assistant (Claude ai)
|
||||||
|
// Idee: Sicherheits Risiko Bewertung (Risk Assessment)
|
||||||
|
// Die KI hat angemerkt, dass String Interpolation ($"DELETE... {uid}") bei SQL Queries
|
||||||
|
// normalerweise ein No Go ist (SQL Injection Gefahr). Da wir hier aber sicherstellen, dass 'uid'
|
||||||
|
// zwingend ein Integer (Convert.ToInt32) aus unserer eigenen Datenquelle ist, ist es in diesem
|
||||||
|
// spezifischen Fall für ein Admin Tool vertretbar und spart Code.
|
||||||
ExecuteSql($"DELETE FROM users WHERE Id={uid}");
|
ExecuteSql($"DELETE FROM users WHERE Id={uid}");
|
||||||
LoadUsers();
|
LoadUsers();
|
||||||
UserBookingsGrid.ItemsSource = null;
|
UserBookingsGrid.ItemsSource = null;
|
||||||
@@ -46,6 +56,7 @@ namespace SkyTeam
|
|||||||
|
|
||||||
private void LoadFlights()
|
private void LoadFlights()
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
string q = @"SELECT f.Id, f.Flugnummer, f.Abflugort, f.Zielort, f.Abflugdatum, f.Preis,
|
string q = @"SELECT f.Id, f.Flugnummer, f.Abflugort, f.Zielort, f.Abflugdatum, f.Preis,
|
||||||
z.Modell AS Plane, CONCAT(p.Vorname, ' ', p.Nachname) AS Pilot
|
z.Modell AS Plane, CONCAT(p.Vorname, ' ', p.Nachname) AS Pilot
|
||||||
FROM fluege f
|
FROM fluege f
|
||||||
@@ -68,7 +79,7 @@ namespace SkyTeam
|
|||||||
|
|
||||||
private void AddFlight_Click(object sender, RoutedEventArgs e)
|
private void AddFlight_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
if (string.IsNullOrWhiteSpace(AddFromCombo.Text) ||
|
if (string.IsNullOrWhiteSpace(AddFromCombo.Text) ||
|
||||||
string.IsNullOrWhiteSpace(AddToCombo.Text) ||
|
string.IsNullOrWhiteSpace(AddToCombo.Text) ||
|
||||||
AddDatePick.SelectedDate == null ||
|
AddDatePick.SelectedDate == null ||
|
||||||
@@ -95,7 +106,12 @@ namespace SkyTeam
|
|||||||
cmd.Parameters.AddWithValue("@from", AddFromCombo.Text);
|
cmd.Parameters.AddWithValue("@from", AddFromCombo.Text);
|
||||||
cmd.Parameters.AddWithValue("@to", AddToCombo.Text);
|
cmd.Parameters.AddWithValue("@to", AddToCombo.Text);
|
||||||
cmd.Parameters.AddWithValue("@date", AddDatePick.SelectedDate.Value);
|
cmd.Parameters.AddWithValue("@date", AddDatePick.SelectedDate.Value);
|
||||||
|
|
||||||
|
// Quelle: Microsoft Learn
|
||||||
|
// Wir nutzen die eingebaute AddHours-Methode von DateTime, um automatisch
|
||||||
|
// ein fiktives Ankunftsdatum zu generieren (Abflug + 4 Stunden)
|
||||||
cmd.Parameters.AddWithValue("@arr", AddDatePick.SelectedDate.Value.AddHours(4));
|
cmd.Parameters.AddWithValue("@arr", AddDatePick.SelectedDate.Value.AddHours(4));
|
||||||
|
|
||||||
cmd.Parameters.AddWithValue("@fnum", flightNum);
|
cmd.Parameters.AddWithValue("@fnum", flightNum);
|
||||||
cmd.Parameters.AddWithValue("@price", AddPriceTxt.Text);
|
cmd.Parameters.AddWithValue("@price", AddPriceTxt.Text);
|
||||||
cmd.Parameters.AddWithValue("@plane", PlaneCombo.SelectedValue);
|
cmd.Parameters.AddWithValue("@plane", PlaneCombo.SelectedValue);
|
||||||
@@ -116,11 +132,19 @@ namespace SkyTeam
|
|||||||
BindComboBox("SELECT Id, CONCAT(Vorname, ' ', Nachname) AS FullName FROM piloten WHERE IstVerfuegbar=1", PilotCombo, "FullName", "Id");
|
BindComboBox("SELECT Id, CONCAT(Vorname, ' ', Nachname) AS FullName FROM piloten WHERE IstVerfuegbar=1", PilotCombo, "FullName", "Id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: AI Assistant (Gemini)
|
||||||
|
// Idee: DRY Prinzip (Don't Repeat Yourself) / Refactoring
|
||||||
|
// Die KI hat vorgeschlagen, die sehr repetitiven SQL-Verbindungs und Adapter Logiken
|
||||||
|
// in universelle Hilfsmethoden (BindGrid, BindComboBox, ExecuteSql) auszulagern.
|
||||||
|
// Das reduziert den Code der Hauptmethoden enorm und macht die Klasse wartbarer.
|
||||||
private void BindGrid(string q, DataGrid g)
|
private void BindGrid(string q, DataGrid g)
|
||||||
{
|
{
|
||||||
try { using (var c = new MySqlConnection(DatenbankServices.GetConnection())) { c.Open(); var a = new MySqlDataAdapter(q, c); var t = new DataTable(); a.Fill(t); g.ItemsSource = t.DefaultView; } } catch { }
|
try { using (var c = new MySqlConnection(DatenbankServices.GetConnection())) { c.Open(); var a = new MySqlDataAdapter(q, c); var t = new DataTable(); a.Fill(t); g.ItemsSource = t.DefaultView; } } catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Microsoft Learn
|
||||||
|
// Hier nutzen wir DisplayMemberPath für den Text, den der User sieht (z.B. das Flugzeugmodell)
|
||||||
|
// und SelectedValuePath für den Wert, der im Hintergrund für die Datenbankabfrage genutzt wird (z.B. die Id).
|
||||||
private void BindComboBox(string q, ComboBox b, string d, string v)
|
private void BindComboBox(string q, ComboBox b, string d, string v)
|
||||||
{
|
{
|
||||||
try { using (var c = new MySqlConnection(DatenbankServices.GetConnection())) { c.Open(); var a = new MySqlDataAdapter(q, c); var t = new DataTable(); a.Fill(t); b.ItemsSource = t.DefaultView; b.DisplayMemberPath = d; b.SelectedValuePath = v; } } catch { }
|
try { using (var c = new MySqlConnection(DatenbankServices.GetConnection())) { c.Open(); var a = new MySqlDataAdapter(q, c); var t = new DataTable(); a.Fill(t); b.ItemsSource = t.DefaultView; b.DisplayMemberPath = d; b.SelectedValuePath = v; } } catch { }
|
||||||
@@ -131,6 +155,7 @@ namespace SkyTeam
|
|||||||
try { using (var c = new MySqlConnection(DatenbankServices.GetConnection())) { c.Open(); new MySqlCommand(s, c).ExecuteNonQuery(); } } catch (Exception ex) { MessageBox.Show(ex.Message); }
|
try { using (var c = new MySqlConnection(DatenbankServices.GetConnection())) { c.Open(); new MySqlCommand(s, c).ExecuteNonQuery(); } } catch (Exception ex) { MessageBox.Show(ex.Message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
private void ShowUsers_Click(object sender, RoutedEventArgs e)
|
private void ShowUsers_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
UserManagementGrid.Visibility = Visibility.Visible;
|
UserManagementGrid.Visibility = Visibility.Visible;
|
||||||
|
|||||||
@@ -9,19 +9,28 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
public partial class AdminLoginPage : Page
|
public partial class AdminLoginPage : Page
|
||||||
{
|
{
|
||||||
|
// Quelle: Microsoft Learn
|
||||||
|
// Nutzung eines "Expression bodied members" (=>) für den Konstruktor
|
||||||
|
// Das macht Methoden oder Konstruktoren, die nur aus einer einzigen Zeile bestehen,
|
||||||
|
// deutlich kompakter und besser lesbar
|
||||||
public AdminLoginPage() => InitializeComponent();
|
public AdminLoginPage() => InitializeComponent();
|
||||||
|
|
||||||
private void AdminLogin_Click(object sender, RoutedEventArgs e)
|
private void AdminLogin_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
|
||||||
string query = "SELECT Id, PasswortHash FROM users WHERE Email = @email AND Vorname = @user AND Rolle = @role";
|
string query = "SELECT Id, PasswortHash FROM users WHERE Email = @email AND Vorname = @user AND Rolle = @role";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection()))
|
using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection()))
|
||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
using (MySqlCommand cmd = new MySqlCommand(query, conn))
|
using (MySqlCommand cmd = new MySqlCommand(query, conn))
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
|
||||||
cmd.Parameters.AddWithValue("@email", AdminEmailBox.Text);
|
cmd.Parameters.AddWithValue("@email", AdminEmailBox.Text);
|
||||||
cmd.Parameters.AddWithValue("@user", AdminUserBox.Text);
|
cmd.Parameters.AddWithValue("@user", AdminUserBox.Text);
|
||||||
cmd.Parameters.AddWithValue("@role", AdminRoleBox.Text);
|
cmd.Parameters.AddWithValue("@role", AdminRoleBox.Text);
|
||||||
@@ -33,8 +42,16 @@ namespace SkyTeam
|
|||||||
string storedHash = reader.GetString("PasswortHash");
|
string storedHash = reader.GetString("PasswortHash");
|
||||||
int dbId = reader.GetInt32("Id");
|
int dbId = reader.GetInt32("Id");
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow
|
||||||
|
// Genau wie beim normalen User-Login prüfen wir das Passwort lokal
|
||||||
|
// über die BCrypt-Bibliothek. Ein direkter Abgleich in der SQL-Datenbank
|
||||||
|
// (WHERE PasswortHash = @hash) ist unmöglich, da BCrypt dynamische Salts verwendet
|
||||||
if (BCrypt.Net.BCrypt.Verify(AdminPassBox.Password, storedHash))
|
if (BCrypt.Net.BCrypt.Verify(AdminPassBox.Password, storedHash))
|
||||||
{
|
{
|
||||||
|
// Quelle: AI Assistant (Gemini)
|
||||||
|
// Konsistentes State Management
|
||||||
|
// Kommentar: Wir nutzen wieder den statischen SessionManager, den die KI für das
|
||||||
|
// Haupt Login vorgeschlagen hatte. So weiß das AdminDashboard sofort, wer eingeloggt ist
|
||||||
SessionManager.CurrentUserId = dbId;
|
SessionManager.CurrentUserId = dbId;
|
||||||
SessionManager.CurrentUserName = AdminUserBox.Text;
|
SessionManager.CurrentUserName = AdminUserBox.Text;
|
||||||
|
|
||||||
@@ -60,6 +77,7 @@ namespace SkyTeam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
private void Back_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new LogInPage());
|
private void Back_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new LogInPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,29 @@
|
|||||||
using BCrypt.Net;
|
using BCrypt.Net;
|
||||||
using MySql.Data.MySqlClient;
|
using MySql.Data.MySqlClient;
|
||||||
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Reflection;
|
using System.Threading;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
namespace SkyTeam
|
namespace SkyTeam
|
||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
// Beim Start der Anwendung einen Standard Admin-Benutzer erstellen, falls keiner existiert
|
// Beim Start der Anwendung einen Standard Admin Benutzer erstellen, falls keiner existiert
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
|
// Quelle: Stack Overflow
|
||||||
|
// Durch das Setzen der CurrentUICulture direkt im App-Konstruktor stellen wir sicher,
|
||||||
|
// dass die gesamte Anwendung (alle Pages und Windows) von Anfang an die gleiche Spracheinstellung
|
||||||
|
// nutzt. Das verhindert Inkonsistenzen beim Laden der ersten Seite.
|
||||||
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
|
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
|
||||||
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
|
|
||||||
|
// Quelle: AI Assistant (Gemini)
|
||||||
|
// Idee: Code-Bereinigung / Refactoring
|
||||||
|
// Die Zuweisung der Culture stand hier ursprünglich doppelt. Die KI hat beim
|
||||||
|
// Code Review darauf hingewiesen, dass eine einmalige Zuweisung ausreicht, um Redundanzen
|
||||||
|
// zu vermeiden. Die zweite Zeile wurde entfernt.
|
||||||
|
|
||||||
CreateDefaultAdmin();
|
CreateDefaultAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,18 +37,26 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow
|
||||||
|
// Dieses Architektur-Muster nennt sich "Database Seeding". Es stellt sicher,
|
||||||
|
// dass das System nach einer Neuinstallation sofort nutzbar ist, da automatisch ein
|
||||||
|
// Root-Account existiert, ohne dass manuelle SQL Eingriffe nötig sind.
|
||||||
string checkQuery = "SELECT COUNT(*) FROM users WHERE Rolle = 'Admin'";
|
string checkQuery = "SELECT COUNT(*) FROM users WHERE Rolle = 'Admin'";
|
||||||
MySqlCommand checkCmd = new MySqlCommand(checkQuery, conn);
|
MySqlCommand checkCmd = new MySqlCommand(checkQuery, conn);
|
||||||
long count = (long)checkCmd.ExecuteScalar();
|
long count = (long)checkCmd.ExecuteScalar();
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
{
|
{
|
||||||
|
// Quelle: Reddit
|
||||||
|
|
||||||
string hashedPassword = BCrypt.Net.BCrypt.HashPassword("admin");
|
string hashedPassword = BCrypt.Net.BCrypt.HashPassword("admin");
|
||||||
|
|
||||||
string insertQuery = @"
|
string insertQuery = @"
|
||||||
INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, CreatedAt)
|
INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, CreatedAt)
|
||||||
VALUES ('System', 'Root', 'admin@skyteam.com', @hash, 'Admin', 'HQ', NOW())";
|
VALUES ('System', 'Root', 'admin@skyteam.com', @hash, 'Admin', 'HQ', NOW())";
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
|
||||||
MySqlCommand insertCmd = new MySqlCommand(insertQuery, conn);
|
MySqlCommand insertCmd = new MySqlCommand(insertQuery, conn);
|
||||||
insertCmd.Parameters.AddWithValue("@hash", hashedPassword);
|
insertCmd.Parameters.AddWithValue("@hash", hashedPassword);
|
||||||
insertCmd.ExecuteNonQuery();
|
insertCmd.ExecuteNonQuery();
|
||||||
@@ -46,8 +65,8 @@ namespace SkyTeam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception ex) {
|
catch (Exception ex)
|
||||||
|
{
|
||||||
MessageBox.Show("Fehler beim Erstellen des Standard Admins: " + ex.Message);
|
MessageBox.Show("Fehler beim Erstellen des Standard Admins: " + ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
static class DatenbankServices
|
static class DatenbankServices
|
||||||
{
|
{
|
||||||
private static readonly string connectionString = File.ReadAllText("connectionstring.txt");
|
private static readonly string connectionString = "Server=mysql.pb.bib.de;uid=pbt3h24akh;pwd=Dd3dwQgPeNxW;database=pbt3h24akh_SkyTeam;";
|
||||||
|
|
||||||
public static string GetConnection()
|
public static string GetConnection()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Windows.Controls;
|
|||||||
|
|
||||||
namespace SkyTeam
|
namespace SkyTeam
|
||||||
{
|
{
|
||||||
|
// in unterricht schonmal gemacht , hier nochmal
|
||||||
public partial class NavigationPage : Page
|
public partial class NavigationPage : Page
|
||||||
{
|
{
|
||||||
public NavigationPage()
|
public NavigationPage()
|
||||||
|
|||||||
@@ -11,23 +11,27 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
public LogInPage()
|
public LogInPage()
|
||||||
{
|
{
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AdminLink_Click(object sender, RoutedEventArgs e)
|
private void AdminLink_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
|
||||||
if (Application.Current.MainWindow is MainWindow mainWindow)
|
if (Application.Current.MainWindow is MainWindow mainWindow)
|
||||||
{
|
{
|
||||||
mainWindow.MainFrame.Navigate(new AdminLoginPage());
|
mainWindow.MainFrame.Navigate(new AdminLoginPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void LogInButton_Click(object sender, RoutedEventArgs e)
|
private void LogInButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
string email = BenutzernameTextBox.Text;
|
string email = BenutzernameTextBox.Text;
|
||||||
string password = PasswortTextBox.Password;
|
string password = PasswortTextBox.Password;
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Basis Validierung auf leere Felder
|
||||||
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
|
||||||
{
|
{
|
||||||
MessageBox.Show("Bitte Email und Passwort eingeben.");
|
MessageBox.Show("Bitte Email und Passwort eingeben.");
|
||||||
@@ -57,6 +61,11 @@ namespace SkyTeam
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow "How to verify a BCrypt hash"
|
||||||
|
// Man kann gehashte Passwörter NICHT direkt im SQL Query vergleichen
|
||||||
|
// (z.B. WHERE Hash = @hash), da BCrypt jedes Mal einen neuen, zufälligen Salt generiert.
|
||||||
|
// Wir müssen erst den gespeicherten Hash aus der DB laden und dann die Verify Methode
|
||||||
|
// der BCrypt-Bibliothek nutzen, um das Klartext passwort damit zu prüfen
|
||||||
string storedHash = reader.GetString("PasswortHash");
|
string storedHash = reader.GetString("PasswortHash");
|
||||||
|
|
||||||
if (!BCrypt.Net.BCrypt.Verify(password, storedHash))
|
if (!BCrypt.Net.BCrypt.Verify(password, storedHash))
|
||||||
@@ -65,6 +74,11 @@ namespace SkyTeam
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: AI Assistant (chat gpt)
|
||||||
|
// Idee: Globales State-Management über eine statische Klasse (SessionManager)
|
||||||
|
// Kommentar: Anstatt die User ID mühsam über jeden Seitenaufruf hinweg in den Konstruktoren
|
||||||
|
// weiterzureichen, hat die KI vorgeschlagen, eine statische SessionManager Klasse zu nutzen.
|
||||||
|
// So sind User-ID, Name und Rolle global für die gesamte Laufzeit abrufbar.
|
||||||
SessionManager.CurrentUserId = reader.GetInt32("Id");
|
SessionManager.CurrentUserId = reader.GetInt32("Id");
|
||||||
SessionManager.CurrentUserName = reader.GetString("Vorname");
|
SessionManager.CurrentUserName = reader.GetString("Vorname");
|
||||||
SessionManager.Role = reader.GetString("Rolle");
|
SessionManager.Role = reader.GetString("Rolle");
|
||||||
@@ -83,17 +97,24 @@ namespace SkyTeam
|
|||||||
|
|
||||||
private void anmeldungsButton_Click(object sender, RoutedEventArgs e)
|
private void anmeldungsButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
((MainWindow)Application.Current.MainWindow)
|
((MainWindow)Application.Current.MainWindow)
|
||||||
.MainFrame.Navigate(new RegistrationPage());
|
.MainFrame.Navigate(new RegistrationPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Setzt den Cursor direkt beim Laden der Seite ins Benutzernamen-Feld.
|
||||||
BenutzernameTextBox.Focus();
|
BenutzernameTextBox.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BenutzernameTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
|
private void BenutzernameTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Stack Overflow "WPF Move focus on enter key"
|
||||||
|
// Kommentar: Ein UX-Feature (User Experience). Wenn der User im Textfeld 'Enter' oder die 'Pfeil Runter' Taste
|
||||||
|
// drückt, generieren wir einen TraversalRequest. Dadurch springt der Fokus automatisch ins nächste UI-Element
|
||||||
|
// (das Passwort Feld), ohne dass der User die Maus benutzen muss.
|
||||||
if (e.Key == Key.Down || e.Key == Key.Enter)
|
if (e.Key == Key.Down || e.Key == Key.Enter)
|
||||||
{
|
{
|
||||||
TraversalRequest request =
|
TraversalRequest request =
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
WindowState="Maximized">
|
WindowState="Maximized">
|
||||||
|
|
||||||
|
|
||||||
<Window.Effect>
|
<Window.Effect>
|
||||||
<DropShadowEffect/>
|
<DropShadowEffect/>
|
||||||
</Window.Effect>
|
</Window.Effect>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
// nichts besonderes, hier wird nur die LoginPage als erstes angezeigt
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|||||||
@@ -11,11 +11,15 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
public RegistrationPage()
|
public RegistrationPage()
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Standard-Initialisierung der WPF Komponenten
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterButton_Click(object sender, RoutedEventArgs e)
|
private void RegisterButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Grundlegende Validierung, ob die Pflichtfelder ausgefüllt wurden
|
||||||
if (string.IsNullOrWhiteSpace(EmailTextBox.Text) || string.IsNullOrWhiteSpace(PasswordBox.Password))
|
if (string.IsNullOrWhiteSpace(EmailTextBox.Text) || string.IsNullOrWhiteSpace(PasswordBox.Password))
|
||||||
{
|
{
|
||||||
MessageBox.Show("Bitte geben Sie Email und Passwort ein.");
|
MessageBox.Show("Bitte geben Sie Email und Passwort ein.");
|
||||||
@@ -36,6 +40,10 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow - "Check if a row exists with a specific value"
|
||||||
|
// Link: https://stackoverflow.com/questions/2788543/check-if-a-row-exists-with-a-specific-value-in-a-database
|
||||||
|
// Wir nutzen ExecuteScalar() anstelle eines Readers, da wir nur wissen wollen,
|
||||||
|
// ob die Email bereits existiert (COUNT > 0). Das ist wesentlich performanter
|
||||||
string checkQuery = "SELECT COUNT(*) FROM users WHERE Email = @email";
|
string checkQuery = "SELECT COUNT(*) FROM users WHERE Email = @email";
|
||||||
using (MySqlCommand checkCmd = new MySqlCommand(checkQuery, conn))
|
using (MySqlCommand checkCmd = new MySqlCommand(checkQuery, conn))
|
||||||
{
|
{
|
||||||
@@ -50,6 +58,10 @@ namespace SkyTeam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Reddit - r/csharp "How should I store passwords in my database?"
|
||||||
|
// Link: https://www.reddit.com/r/csharp/comments/7qcx8f/how_should_i_store_passwords_in_my_database/
|
||||||
|
// Laut Community Konsens sollten Passwörter niemals im Klartext gespeichert werden
|
||||||
|
// Wir nutzen die BCrypt Bibliothek, die automatisch Salting und Hashing übernimmt , das ist schon mal in Unterricht behandelt worden
|
||||||
string hashedPassword = BCrypt.Net.BCrypt.HashPassword(PasswordBox.Password);
|
string hashedPassword = BCrypt.Net.BCrypt.HashPassword(PasswordBox.Password);
|
||||||
string insertQuery = "INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, Anrede, Geburtsdatum) " +
|
string insertQuery = "INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, Anrede, Geburtsdatum) " +
|
||||||
"VALUES (@vorname, @nachname, @email, @password, 'User', @stadt, @anrede, @geburtsdatum)";
|
"VALUES (@vorname, @nachname, @email, @password, 'User', @stadt, @anrede, @geburtsdatum)";
|
||||||
@@ -59,11 +71,19 @@ namespace SkyTeam
|
|||||||
string selectedAnrede = (SalutationComboBox.SelectedItem as ComboBoxItem)?.Content.ToString();
|
string selectedAnrede = (SalutationComboBox.SelectedItem as ComboBoxItem)?.Content.ToString();
|
||||||
DateTime? selectedDate = BirthDatePicker.SelectedDate;
|
DateTime? selectedDate = BirthDatePicker.SelectedDate;
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Kommentar: Standard Parameter-Binding zum Schutz vor SQL-Injection
|
||||||
cmd.Parameters.AddWithValue("@vorname", FirstNameTextBox.Text);
|
cmd.Parameters.AddWithValue("@vorname", FirstNameTextBox.Text);
|
||||||
cmd.Parameters.AddWithValue("@nachname", LastNameTextBox.Text);
|
cmd.Parameters.AddWithValue("@nachname", LastNameTextBox.Text);
|
||||||
cmd.Parameters.AddWithValue("@email", emailToCheck);
|
cmd.Parameters.AddWithValue("@email", emailToCheck);
|
||||||
cmd.Parameters.AddWithValue("@password", hashedPassword);
|
cmd.Parameters.AddWithValue("@password", hashedPassword);
|
||||||
cmd.Parameters.AddWithValue("@stadt", CityTextBox.Text);
|
cmd.Parameters.AddWithValue("@stadt", CityTextBox.Text);
|
||||||
|
|
||||||
|
// Quelle: Microsoft Learn - "DBNull.Value Field"
|
||||||
|
// Link: https://learn.microsoft.com/en-us/dotnet/api/system.dbnull.value
|
||||||
|
// Wenn optionale Felder (wie Anrede oder Geburtsdatum) leer bleiben,
|
||||||
|
// können wir nicht einfach 'null' in C# übergeben. Die Datenbank erwartet explizit
|
||||||
|
// das Objekt 'DBNull.Value', damit die Spalte korrekt als NULL markiert wird
|
||||||
cmd.Parameters.AddWithValue("@anrede", selectedAnrede ?? (object)DBNull.Value);
|
cmd.Parameters.AddWithValue("@anrede", selectedAnrede ?? (object)DBNull.Value);
|
||||||
cmd.Parameters.AddWithValue("@geburtsdatum", selectedDate.HasValue ? selectedDate.Value : (object)DBNull.Value);
|
cmd.Parameters.AddWithValue("@geburtsdatum", selectedDate.HasValue ? selectedDate.Value : (object)DBNull.Value);
|
||||||
|
|
||||||
@@ -73,6 +93,11 @@ namespace SkyTeam
|
|||||||
|
|
||||||
MessageBox.Show("Dein Konto wurde erfolgreich angelegt!");
|
MessageBox.Show("Dein Konto wurde erfolgreich angelegt!");
|
||||||
|
|
||||||
|
// Quelle: AI Assistant (Gemini)
|
||||||
|
// Idee: Navigation aus einer Page heraus, die in einem Frame gehostet wird.
|
||||||
|
// Kommentar: Die KI hat darauf hingewiesen, dass eine Page (wie die RegistrationPage)
|
||||||
|
// nicht direkt navigieren sollte, wenn sie im MainFrame des MainWindows liegt.
|
||||||
|
// Wir müssen erst auf das MainWindow zugreifen, um dessen Frame für die Navigation zu nutzen.
|
||||||
if (Application.Current.MainWindow is MainWindow mainWindow)
|
if (Application.Current.MainWindow is MainWindow mainWindow)
|
||||||
{
|
{
|
||||||
mainWindow.MainFrame.Navigate(new LogInPage());
|
mainWindow.MainFrame.Navigate(new LogInPage());
|
||||||
@@ -99,6 +124,11 @@ namespace SkyTeam
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Quelle: Microsoft Learn - "How to verify that strings are in valid email format"
|
||||||
|
// Link: https://learn.microsoft.com/en-us/dotnet/standard/base-types/how-to-verify-that-strings-are-in-valid-email-format
|
||||||
|
// Kommentar: Wir nutzen den offiziell empfohlenen Regex-Ausdruck von Microsoft,
|
||||||
|
// kombiniert mit einem Timeout (250ms), um Denial-of-Service-Angriffe durch
|
||||||
|
// extrem lange oder fehlerhafte Strings (ReDoS) zu verhindern.
|
||||||
return Regex.IsMatch(email,
|
return Regex.IsMatch(email,
|
||||||
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
|
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
|
||||||
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
||||||
|
|||||||
BIN
SkyTeam/Screenshot 2026-03-04 100331.ico
Normal file
BIN
SkyTeam/Screenshot 2026-03-04 100331.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
@@ -15,6 +15,9 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow "How to compare SolidColorBrush in WPF"
|
||||||
|
// Wir lesen die globale Ressource aus und prüfen die Farbe, um sicherzustellen,
|
||||||
|
// dass der Toggle Switch beim Wechseln der Seite den richtigen Zustand (An/Aus) anzeigt
|
||||||
var bgBrush = Application.Current.Resources["PageBackground"] as SolidColorBrush;
|
var bgBrush = Application.Current.Resources["PageBackground"] as SolidColorBrush;
|
||||||
if (bgBrush != null && bgBrush.Color == Color.FromRgb(30, 30, 30))
|
if (bgBrush != null && bgBrush.Color == Color.FromRgb(30, 30, 30))
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,10 @@ namespace SkyTeam
|
|||||||
DarkModeToggle.Content = "Aus";
|
DarkModeToggle.Content = "Aus";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: AI Assistant (chat gpt)
|
||||||
|
// Idee: Refactoring / DRY-Prinzip (Don't Repeat Yourself)
|
||||||
|
// Die KI hat vorgeschlagen, das Zuweisen der Application.Current.Resources in eine
|
||||||
|
// separate Hilfsmethode auszulagern, um den Code in den Checked/Unchecked Events sauberer zu halten.
|
||||||
private void SetRes(string key, Color color)
|
private void SetRes(string key, Color color)
|
||||||
{
|
{
|
||||||
Application.Current.Resources[key] = new SolidColorBrush(color);
|
Application.Current.Resources[key] = new SolidColorBrush(color);
|
||||||
@@ -63,8 +70,15 @@ namespace SkyTeam
|
|||||||
using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection()))
|
using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection()))
|
||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
|
// Quelle: Reddit - r/csharp "Best way to delete user with related data?"
|
||||||
|
// Aufgrund von Foreign Key Constraints in der Datenbank
|
||||||
|
// müssen zwingend erst die Buchungen des Users gelöscht werden, bevor der User
|
||||||
|
// selbst gelöscht werden darf. Sonst wirft die Datenbank einen Fehler
|
||||||
string deleteBookings = "DELETE FROM buchungen WHERE UserId = @uid";
|
string deleteBookings = "DELETE FROM buchungen WHERE UserId = @uid";
|
||||||
MySqlCommand cmd1 = new MySqlCommand(deleteBookings, conn);
|
MySqlCommand cmd1 = new MySqlCommand(deleteBookings, conn);
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
cmd1.Parameters.AddWithValue("@uid", SessionManager.CurrentUserId);
|
cmd1.Parameters.AddWithValue("@uid", SessionManager.CurrentUserId);
|
||||||
cmd1.ExecuteNonQuery();
|
cmd1.ExecuteNonQuery();
|
||||||
|
|
||||||
@@ -85,9 +99,12 @@ namespace SkyTeam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Lambda-Ausdrücke (=>) für simple Seitenwechsel über den NavigationService
|
||||||
private void HomeButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new NavigationPage());
|
private void HomeButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new NavigationPage());
|
||||||
private void BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
|
private void BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
|
||||||
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
|
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
|
||||||
|
|
||||||
private void LogoutButton_Click(object sender, RoutedEventArgs e)
|
private void LogoutButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SessionManager.CurrentUserId = 0;
|
SessionManager.CurrentUserId = 0;
|
||||||
@@ -98,6 +115,11 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
if (LanguageComboBox.SelectedItem is ComboBoxItem selectedItem)
|
if (LanguageComboBox.SelectedItem is ComboBoxItem selectedItem)
|
||||||
{
|
{
|
||||||
|
// Quelle: Microsoft Learn "FlowDirection Enumeration" & Stack Overflow "WPF RTL Support"
|
||||||
|
// Link : https://learn.microsoft.com/en-us/dotnet/api/system.windows.flowdirection
|
||||||
|
// Wir setzen nicht nur die UI Culture auf die ausgewählte Sprache, sondern
|
||||||
|
// passen für Arabisch ("ar") auch dynamisch die 'FlowDirection' auf Right-To-Left an,
|
||||||
|
// damit das Layout der Seite korrekt gespiegelt wird
|
||||||
string culture = selectedItem.Tag.ToString();
|
string culture = selectedItem.Tag.ToString();
|
||||||
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
|
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
|
||||||
|
|
||||||
@@ -106,6 +128,9 @@ namespace SkyTeam
|
|||||||
else
|
else
|
||||||
this.FlowDirection = FlowDirection.LeftToRight;
|
this.FlowDirection = FlowDirection.LeftToRight;
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow "How to refresh WPF page after changing culture?"
|
||||||
|
// Durch das erneute Navigieren auf die gleiche Seite (SettingsPage) wird
|
||||||
|
// das UI gezwungen, sich mit der neu gesetzten Sprache und FlowDirection neu zu rendern
|
||||||
NavigationService.Navigate(new SettingsPage());
|
NavigationService.Navigate(new SettingsPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Screenshot 2026-03-04 100331.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="MahApps.Metro.IconPacks" Version="6.2.1" />
|
<PackageReference Include="MahApps.Metro.IconPacks" Version="6.2.1" />
|
||||||
|
|||||||
@@ -9,19 +9,27 @@ namespace SkyTeam
|
|||||||
{
|
{
|
||||||
public ReservierungssuchePage()
|
public ReservierungssuchePage()
|
||||||
{
|
{
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SearchFlights_Click(object sender, RoutedEventArgs e)
|
private void SearchFlights_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Simples Auslesen der Benutzereingaben aus den Textboxen und dem DatePicker
|
||||||
string from = FromBox.Text;
|
string from = FromBox.Text;
|
||||||
string to = ToBox.Text;
|
string to = ToBox.Text;
|
||||||
DateTime? date = DateBox.SelectedDate;
|
DateTime? date = DateBox.SelectedDate;
|
||||||
|
|
||||||
|
// Quelle: Stack Overflow "Passing parameters between pages in WPF"
|
||||||
|
// Um Daten (wie Suchkriterien) sicher an die nächste Seite zu übergeben, nutzen wir hier
|
||||||
|
// den überladenen Konstruktor der Ziel-Page ("verfuegbareFluge"). Laut Community-Konsens ist das
|
||||||
|
// die sauberste und direkteste Methode für einfache Datenübergaben in Standard-WPF-Anwendungen.
|
||||||
NavigationService.Navigate(new verfuegbareFluge(from, to, date));
|
NavigationService.Navigate(new verfuegbareFluge(from, to, date));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quelle: Im Unterricht gemacht
|
||||||
|
// Lambda-Ausdrücke (=>) für kurze und übersichtliche Seitenwechsel über den NavigationService
|
||||||
private void HomeButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new NavigationPage());
|
private void HomeButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new NavigationPage());
|
||||||
private void BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
|
private void BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
|
||||||
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
|
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace SkyTeam
|
|||||||
|
|
||||||
private void LoadFlights()
|
private void LoadFlights()
|
||||||
{
|
{
|
||||||
|
// Source: Stack Overflow "What is the purpose of using WHERE 1=1 in SQL statements?"
|
||||||
|
// Link: https://stackoverflow.com/questions/1264681/what-is-the-purpose-of-using-where-1-1-in-sql-statements
|
||||||
string query = @"SELECT f.Id, f.Flugnummer, f.Abflugort AS 'From', f.Zielort AS 'To',
|
string query = @"SELECT f.Id, f.Flugnummer, f.Abflugort AS 'From', f.Zielort AS 'To',
|
||||||
z.Modell AS Plane, f.Abflugdatum AS Date
|
z.Modell AS Plane, f.Abflugdatum AS Date
|
||||||
FROM fluege f
|
FROM fluege f
|
||||||
@@ -86,9 +88,13 @@ namespace SkyTeam
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source: Stack Overflow "Get selected row item in DataGrid WPF"
|
||||||
|
// Link: https://stackoverflow.com/questions/3913580/get-selected-row-item-in-datagrid-wpf
|
||||||
|
|
||||||
DataRowView row = (DataRowView)AvailableFlightsDataGrid.SelectedItem;
|
DataRowView row = (DataRowView)AvailableFlightsDataGrid.SelectedItem;
|
||||||
int flightId = Convert.ToInt32(row["Id"]);
|
int flightId = Convert.ToInt32(row["Id"]);
|
||||||
|
|
||||||
|
|
||||||
if (SessionManager.CurrentUserId == 0)
|
if (SessionManager.CurrentUserId == 0)
|
||||||
{
|
{
|
||||||
MessageBox.Show("Fehler: Nicht eingeloggt.");
|
MessageBox.Show("Fehler: Nicht eingeloggt.");
|
||||||
@@ -117,6 +123,8 @@ namespace SkyTeam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// quelle: Microsoft Learn "NavigationService.Navigate Method"
|
||||||
|
|
||||||
private void HomeButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new NavigationPage());
|
private void HomeButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new NavigationPage());
|
||||||
private void BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
|
private void BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
|
||||||
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
|
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
|
||||||
|
|||||||
Reference in New Issue
Block a user