Compare commits

..

5 Commits

Author SHA1 Message Date
younes elhaddoury
4a0f5dc0bd comments und quellen hinzufugt 2026-03-05 12:32:01 +01:00
younes elhaddoury
04a18d3c50 Merge branch 'main' of https://git.bib.de/PBT3H24AKH/FlyTeam 2026-02-25 10:41:12 +01:00
younes elhaddoury
d01741aa3b aufruf von CreateDefaultAdmin() 2026-02-25 10:33:03 +01:00
d8e245caf9 SkyTeam/migration.sql gelöscht 2026-02-25 10:28:21 +01:00
younes elhaddoury
41da8b7a54 combobox design verbessern 2026-02-25 10:08:33 +01:00
31 changed files with 267 additions and 754 deletions

2
.gitignore vendored
View File

@@ -221,7 +221,7 @@ rcf/
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
*.txt
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -11,6 +11,7 @@ namespace SkyTeam
{
public AdminDashboard()
{
// Quelle: Im Unterricht gemacht
InitializeComponent();
LoadUsers();
LoadFlights();
@@ -19,12 +20,15 @@ namespace SkyTeam
private void LoadUsers()
{
// Quelle: Im Unterricht gemacht
BindGrid("SELECT Id, Vorname, Nachname, Email, Rolle FROM users", AllUsersGrid);
}
private void AllUsersGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (AllUsersGrid.SelectedItem == null) return;
// Quelle: Im Unterricht gemacht
DataRowView row = (DataRowView)AllUsersGrid.SelectedItem;
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);
@@ -38,6 +42,12 @@ namespace SkyTeam
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}");
LoadUsers();
UserBookingsGrid.ItemsSource = null;
@@ -46,6 +56,7 @@ namespace SkyTeam
private void LoadFlights()
{
// Quelle: Im Unterricht gemacht
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
FROM fluege f
@@ -61,12 +72,6 @@ namespace SkyTeam
DataRowView row = (DataRowView)AllFlightsGrid.SelectedItem;
if (MessageBox.Show("Flug 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 fluege WHERE Id={row["Id"]}");
LoadFlights();
}
@@ -74,7 +79,7 @@ namespace SkyTeam
private void AddFlight_Click(object sender, RoutedEventArgs e)
{
// Quelle: Im Unterricht gemacht
if (string.IsNullOrWhiteSpace(AddFromCombo.Text) ||
string.IsNullOrWhiteSpace(AddToCombo.Text) ||
AddDatePick.SelectedDate == null ||
@@ -106,6 +111,7 @@ namespace SkyTeam
// 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("@fnum", flightNum);
cmd.Parameters.AddWithValue("@price", AddPriceTxt.Text);
cmd.Parameters.AddWithValue("@plane", PlaneCombo.SelectedValue);
@@ -126,18 +132,19 @@ namespace SkyTeam
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.
// 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)
{
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).
// 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)
{
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 { }
@@ -148,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); }
}
// Quelle: Im Unterricht gemacht
private void ShowUsers_Click(object sender, RoutedEventArgs e)
{
UserManagementGrid.Visibility = Visibility.Visible;

View File

@@ -10,22 +10,27 @@ namespace SkyTeam
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
// 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();
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";
try
{
using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection()))
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand(query, conn))
{
// Quelle: Im Unterricht gemacht
cmd.Parameters.AddWithValue("@email", AdminEmailBox.Text);
cmd.Parameters.AddWithValue("@user", AdminUserBox.Text);
cmd.Parameters.AddWithValue("@role", AdminRoleBox.Text);
@@ -37,16 +42,16 @@ namespace SkyTeam
string storedHash = reader.GetString("PasswortHash");
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
// 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))
{
// 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
// 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.CurrentUserName = AdminUserBox.Text;
@@ -72,6 +77,7 @@ namespace SkyTeam
}
}
// Quelle: Im Unterricht gemacht
private void Back_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new LogInPage());
}
}

View File

@@ -1,25 +1,30 @@
using BCrypt.Net;
using MySql.Data.MySqlClient;
using PdfSharp.Fonts;
using System;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Windows;
using static SkyTeam.BuchungenPage;
namespace SkyTeam
{
public partial class App : Application
{
// Beim Start der Anwendung einen Standard Admin-Benutzer erstellen, falls keiner existiert , selbGedacht.
// Beim Start der Anwendung einen Standard Admin Benutzer erstellen, falls keiner existiert
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.
// 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");
GlobalFontSettings.FontResolver = new CustomFontResolver();
// 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();
}
private void CreateDefaultAdmin()
@@ -31,23 +36,27 @@ namespace SkyTeam
using (MySqlConnection conn = new MySqlConnection(connectionString))
{
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.
// 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'";
MySqlCommand checkCmd = new MySqlCommand(checkQuery, conn);
long count = (long)checkCmd.ExecuteScalar();
if (count == 0)
{
// Quelle: Reddit
string hashedPassword = BCrypt.Net.BCrypt.HashPassword("admin");
string insertQuery = @"
INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, CreatedAt)
VALUES ('System', 'Root', 'admin@skyteam.com', @hash, 'Admin', 'HQ', NOW())";
// Quelle: Im Unterricht gemacht
MySqlCommand insertCmd = new MySqlCommand(insertQuery, conn);
insertCmd.Parameters.AddWithValue("@hash", hashedPassword);
insertCmd.ExecuteNonQuery();
@@ -56,9 +65,9 @@ namespace SkyTeam
}
}
}
catch(Exception ex) {
MessageBox.Show("Fehler beim Erstellen des Standard Admins: " + ex.Message);
catch (Exception ex)
{
MessageBox.Show("Fehler beim Erstellen des Standard Admins: " + ex.Message);
}
}
}

View File

@@ -89,7 +89,6 @@
</DataGrid>
<Button x:Name="CancelBtn" Visibility="Collapsed" Content="{x:Static properties:Resources.CancelFlightButton}" Background="#D32F2F" Foreground="White" FontWeight="Bold" Width="150" Height="40" HorizontalAlignment="Right" Margin="0,15,0,0" Click="CancelBooking_Click"/>
<Button x:Name="CreatePdfBtn" Visibility="Collapsed" Content="{x:Static properties:Resources.CreatePdfButton}" Background="#FF1E88E5" Foreground="White" FontWeight="Bold" Width="150" Height="40" HorizontalAlignment="Right" Margin="0,15,0,0" Click="CreatePdf_Click"/>
</StackPanel>
</Grid>
</Grid>

View File

@@ -1,13 +1,8 @@
using MySql.Data.MySqlClient;
using PdfSharp.Drawing;
using PdfSharp.Fonts;
using PdfSharp.Pdf;
using System;
using System;
using System.Data;
using System.Windows;
using System.Windows.Controls;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.IO;
using MySql.Data.MySqlClient;
namespace SkyTeam
{
@@ -46,14 +41,12 @@ namespace SkyTeam
NoBookingsView.Visibility = Visibility.Collapsed;
BookingsGrid.Visibility = Visibility.Visible;
CancelBtn.Visibility = Visibility.Visible;
CreatePdfBtn.Visibility = Visibility.Visible;
}
else
{
NoBookingsView.Visibility = Visibility.Visible;
BookingsGrid.Visibility = Visibility.Collapsed;
CancelBtn.Visibility = Visibility.Collapsed;
CreatePdfBtn.Visibility = Visibility.Collapsed;
}
}
}
@@ -81,81 +74,11 @@ namespace SkyTeam
LoadBookings();
}
}
// Idee und Grundlage aus folgendem Video:
// https://www.youtube.com/watch?v=sfeJprlUT7E
// Datum: 05.03.2026
// Anpassung und Implementierung mit Unterstützung von ChatGPT (OpenAI)
public class CustomFontResolver : IFontResolver
{
private readonly string fontPath = "OpenSans-Regular.ttf";
public string DefaultFontName => "OpenSans";
public byte[] GetFont(string faceName)
{
return File.ReadAllBytes(fontPath);
}
public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
{
return new FontResolverInfo("OpenSans");
}
}
// Feature: PDF-Export für Flugbuchungen hinzugefügt
//Quelle:
//YouTube Tutorial: https://www.youtube.com/watch?v=C-yMypr_TdY
//Datum: 05.03.2026
private void CreatePdf_Click(object sender, RoutedEventArgs e)
{
if (BookingsGrid.SelectedItem == null)
{
MessageBox.Show("Bitte wählen Sie einen Flug aus.");
return;
}
DataRowView row = (DataRowView)BookingsGrid.SelectedItem;
int buchungId = Convert.ToInt32(row["BuchungId"]);
string flugNr = row["Flugnummer"].ToString();
string von = row["Abflugort"].ToString();
string nach = row["Zielort"].ToString();
DateTime datum = Convert.ToDateTime(row["Abflugdatum"]);
string status = row["Status"].ToString();
string logoPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "icon.png");
XImage logo = XImage.FromFile(logoPath);
PdfDocument document = new PdfDocument();
document.Info.Title = "Flugbuchung";
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
XFont titleFont = new XFont("OpenSans", 30);
XFont textFont = new XFont("OpenSans", 20);
gfx.DrawImage(logo, 40, 20, 100, 100);
gfx.DrawString("Flugbuchung", titleFont, XBrushes.Black, new XPoint(200, 80));
gfx.DrawString($"Buchung-ID: {buchungId}", textFont, XBrushes.Black, new XPoint(40, 160));
gfx.DrawString($"Flug-Nr.: {flugNr}", textFont, XBrushes.Black, new XPoint(40, 200));
gfx.DrawString($"Von: {von}", textFont, XBrushes.Black, new XPoint(40, 240));
gfx.DrawString($"Nach: {nach}", textFont, XBrushes.Black, new XPoint(40, 280));
gfx.DrawString($"Datum: {datum:dd.MM.yyyy}", textFont, XBrushes.Black, new XPoint(40, 320));
gfx.DrawString($"Status: {status}", textFont, XBrushes.Black, new XPoint(40, 360));
string filename = $"Flugbuchung_{buchungId}.pdf";
document.Save(filename);
MessageBox.Show($"PDF erfolgreich erstellt: {filename}");
}
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 SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
private void LogoutButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new LogInPage());
private void OpenReservierungSuche_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new ReservierungssuchePage());
}
}

View File

@@ -10,7 +10,7 @@ namespace SkyTeam
{
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()
{

View File

@@ -3,6 +3,7 @@ using System.Windows.Controls;
namespace SkyTeam
{
// in unterricht schonmal gemacht , hier nochmal
public partial class NavigationPage : Page
{
public NavigationPage()

View File

@@ -11,24 +11,27 @@ namespace SkyTeam
{
public LogInPage()
{
InitializeComponent();
}
// Quelle: Im Unterricht gemacht
private void AdminLink_Click(object sender, RoutedEventArgs e)
{
// Quelle: Im Unterricht gemacht
if (Application.Current.MainWindow is MainWindow mainWindow)
{
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 password = PasswortTextBox.Password;
// Quelle: Im Unterricht gemacht
// Basis Validierung auf leere Felder
// Quelle: Im Unterricht gemacht
// Basis Validierung auf leere Felder
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
{
MessageBox.Show("Bitte Email und Passwort eingeben.");
@@ -57,11 +60,12 @@ namespace SkyTeam
MessageBox.Show("Benutzer wurde nicht gefunden.");
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
// 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");
if (!BCrypt.Net.BCrypt.Verify(password, storedHash))
@@ -69,11 +73,12 @@ namespace SkyTeam
MessageBox.Show("Falsches Passwort.");
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.
// 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.CurrentUserName = reader.GetString("Vorname");
SessionManager.Role = reader.GetString("Rolle");
@@ -92,13 +97,13 @@ namespace SkyTeam
private void anmeldungsButton_Click(object sender, RoutedEventArgs e)
{
// Quelle: Im Unterricht gemacht
((MainWindow)Application.Current.MainWindow)
.MainFrame.Navigate(new RegistrationPage());
}
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();
@@ -106,10 +111,10 @@ namespace SkyTeam
private void BenutzernameTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
// Quelle: Stack Overflow "WPF Move focus on enter key"
// 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
// 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)
{
TraversalRequest request =

View File

@@ -11,6 +11,7 @@
WindowStartupLocation="CenterScreen"
WindowState="Maximized">
<Window.Effect>
<DropShadowEffect/>
</Window.Effect>

View File

@@ -5,6 +5,7 @@ namespace SkyTeam
{
public partial class MainWindow : Window
{
// nichts besonderes, hier wird nur die LoginPage als erstes angezeigt
public MainWindow()
{
InitializeComponent();

Binary file not shown.

View File

@@ -11,11 +11,15 @@ namespace SkyTeam
{
public RegistrationPage()
{
// Quelle: Im Unterricht gemacht
// Standard-Initialisierung der WPF Komponenten
InitializeComponent();
}
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))
{
MessageBox.Show("Bitte geben Sie Email und Passwort ein.");
@@ -36,6 +40,10 @@ namespace SkyTeam
{
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";
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 insertQuery = "INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, Anrede, Geburtsdatum) " +
"VALUES (@vorname, @nachname, @email, @password, 'User', @stadt, @anrede, @geburtsdatum)";
@@ -59,16 +71,19 @@ namespace SkyTeam
string selectedAnrede = (SalutationComboBox.SelectedItem as ComboBoxItem)?.Content.ToString();
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("@nachname", LastNameTextBox.Text);
cmd.Parameters.AddWithValue("@email", emailToCheck);
cmd.Parameters.AddWithValue("@password", hashedPassword);
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
// 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("@geburtsdatum", selectedDate.HasValue ? selectedDate.Value : (object)DBNull.Value);
@@ -77,11 +92,12 @@ namespace SkyTeam
}
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.
// 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)
{
mainWindow.MainFrame.Navigate(new LogInPage());
@@ -109,10 +125,10 @@ namespace SkyTeam
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.
// 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,
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));

View File

@@ -222,15 +222,6 @@ namespace SkyTeam {
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Als PDF exportieren ähnelt.
/// </summary>
public static string CreatePdfButton {
get {
return ResourceManager.GetString("CreatePdfButton", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Passen Sie Ihr Erlebnis an ähnelt.
/// </summary>

View File

@@ -285,7 +285,4 @@
<data name="CancelFlightButton" xml:space="preserve">
<value>إلغاء الرحلة</value>
</data>
<data name="CreatePdfButton" xml:space="preserve">
<value>تصدير إلى PDF</value>
</data>
</root>

View File

@@ -285,7 +285,4 @@
<data name="CancelFlightButton" xml:space="preserve">
<value>Cancel Flight</value>
</data>
<data name="CreatePdfButton" xml:space="preserve">
<value>Export as PDF</value>
</data>
</root>

View File

@@ -285,7 +285,4 @@
<data name="CancelFlightButton" xml:space="preserve">
<value>Flug stornieren</value>
</data>
<data name="CreatePdfButton" xml:space="preserve">
<value>Als PDF exportieren</value>
</data>
</root>

View File

@@ -285,7 +285,4 @@
<data name="CancelFlightButton" xml:space="preserve">
<value>Скасувати рейс</value>
</data>
<data name="CreatePdfButton" xml:space="preserve">
<value>Експортувати у PDF</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -27,7 +27,6 @@
</Border>
<StackPanel Grid.Row="1" Grid.Column="0" Background="{DynamicResource SidebarBackground}">
<Button Height="50" Margin="5" Click="HomeButton_Click" Background="Transparent" BorderThickness="0">
<StackPanel Orientation="Horizontal">
<iconPacks:PackIconMaterial Kind="Home" Width="20" Margin="0,0,10,0" Foreground="{DynamicResource PrimaryText}"/>
@@ -106,25 +105,103 @@
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<iconPacks:PackIconMaterial Kind="Translate"
Width="32"
Height="32"
Width="32" Height="32"
Foreground="{DynamicResource PrimaryText}"
Margin="0,0,15,0"/>
<StackPanel Width="300">
<TextBlock Text="{x:Static properties:Resources.Language}"
FontSize="18"
FontWeight="Bold"
Foreground="{DynamicResource PrimaryText}"/>
FontSize="18" FontWeight="Bold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock Text="{x:Static properties:Resources.ChooseLanguage}"
FontSize="14"
Foreground="{DynamicResource SecondaryText}"/>
FontSize="14" Foreground="{DynamicResource SecondaryText}"/>
</StackPanel>
<ComboBox x:Name="LanguageComboBox"
Width="150"
SelectionChanged="LanguageComboBox_SelectionChanged">
Width="150" Height="40"
SelectionChanged="LanguageComboBox_SelectionChanged">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Background" Value="#1E88E5"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton x:Name="ToggleButton"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border CornerRadius="8" Background="{TemplateBinding Background}" BorderThickness="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<iconPacks:PackIconMaterial Kind="ChevronDown"
Foreground="White"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="14" Height="14"/>
</Grid>
</Border>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<ContentPresenter x:Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
Margin="15,0,40,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
TextBlock.Foreground="White" />
<Popup x:Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid x:Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="200">
<Border x:Name="DropDownBorder" Background="{DynamicResource CardBackground}" BorderThickness="1" BorderBrush="#1E88E5" CornerRadius="8" Margin="0,5,0,0">
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Border>
</Grid>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.Style>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Padding" Value="12,10"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryText}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#1E88E5"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBoxItem Content="Deutsch" Tag="de"/>
<ComboBoxItem Content="English" Tag="en"/>
<ComboBoxItem Content="Українська" Tag="uk"/>

View File

@@ -14,10 +14,10 @@ namespace SkyTeam
public SettingsPage()
{
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
// 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;
if (bgBrush != null && bgBrush.Color == Color.FromRgb(30, 30, 30))
{
@@ -51,10 +51,11 @@ namespace SkyTeam
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.
// 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)
{
Application.Current.Resources[key] = new SolidColorBrush(color);
@@ -69,12 +70,15 @@ namespace SkyTeam
using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection()))
{
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
// 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";
MySqlCommand cmd1 = new MySqlCommand(deleteBookings, conn);
// Quelle: Im Unterricht gemacht
cmd1.Parameters.AddWithValue("@uid", SessionManager.CurrentUserId);
cmd1.ExecuteNonQuery();
@@ -95,23 +99,27 @@ 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 BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());
private void LogoutButton_Click(object sender, RoutedEventArgs e)
{
SessionManager.CurrentUserId = 0;
NavigationService.Navigate(new LogInPage());
}
// 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
private void LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
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();
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
@@ -119,9 +127,10 @@ namespace SkyTeam
this.FlowDirection = FlowDirection.RightToLeft;
else
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
// 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());
}
}

View File

@@ -1,25 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="icon.ico" />
<Content Include="Screenshot 2026-03-04 100331.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="MahApps.Metro.IconPacks" Version="6.2.1" />
<PackageReference Include="MySql.Data" Version="9.6.0" />
<PackageReference Include="PDFsharp" Version="6.2.4" />
<PackageReference Include="System.Drawing.Common" Version="10.0.3" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,315 +0,0 @@
-- User Email elhaddouryyounes@gmail.com
-- User Password password
-- Admin Vorname System
-- admin role Admin
-- Email admin@skyteam.com
-- password admin
-- phpMyAdmin SQL Dump
-- version 5.2.2
-- https://www.phpmyadmin.net/
--
-- Host: localhost
-- Erstellungszeit: 04. Mrz 2026 um 10:02
-- Server-Version: 11.8.3-MariaDB-0+deb13u1 from Debian
-- PHP-Version: 8.4.16
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Datenbank: `pbt3h24akh_SkyTeam`
--
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `buchungen`
--
CREATE TABLE `buchungen` (
`Id` int(11) NOT NULL,
`UserId` int(11) NOT NULL,
`FlugId` int(11) NOT NULL,
`BuchungsDatum` datetime DEFAULT current_timestamp(),
`Status` varchar(50) DEFAULT 'Bestätigt'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
--
-- Daten für Tabelle `buchungen`
--
INSERT INTO `buchungen` (`Id`, `UserId`, `FlugId`, `BuchungsDatum`, `Status`) VALUES
(12, 7, 94, '2026-02-25 14:18:35', 'Bestätigt');
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `fluege`
--
CREATE TABLE `fluege` (
`Id` int(11) NOT NULL,
`Abflugort` varchar(100) NOT NULL,
`Zielort` varchar(100) NOT NULL,
`Abflugdatum` datetime NOT NULL,
`Ankunftsdatum` datetime NOT NULL,
`Flugnummer` varchar(20) NOT NULL,
`Preis` decimal(10,2) NOT NULL,
`Created` datetime NOT NULL DEFAULT current_timestamp(),
`FlugzeugId` int(11) NOT NULL,
`PilotId` int(11) NOT NULL,
`ErstelltVon` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
--
-- Daten für Tabelle `fluege`
--
INSERT INTO `fluege` (`Id`, `Abflugort`, `Zielort`, `Abflugdatum`, `Ankunftsdatum`, `Flugnummer`, `Preis`, `Created`, `FlugzeugId`, `PilotId`, `ErstelltVon`) VALUES
(91, 'Lissabon', 'Frankfurt', '2026-02-05 00:00:00', '2026-02-05 04:00:00', 'SYJ-716', 0.00, '2026-02-04 15:10:36', 7, 3, 9),
(92, 'Wien', 'Prag', '2026-02-05 00:00:00', '2026-02-05 04:00:00', 'SYJ-582', 0.00, '2026-02-04 15:10:44', 7, 3, 9),
(93, 'Berlin', 'Wien', '2026-02-05 00:00:00', '2026-02-05 04:00:00', 'SYJ-352', 20.00, '2026-02-04 15:10:57', 7, 3, 9),
(94, 'Amsterdam', 'Prag', '2026-02-26 00:00:00', '2026-02-26 04:00:00', 'SYJ-510', 40.00, '2026-02-25 10:25:39', 12, 6, 9);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `flugzeuge`
--
CREATE TABLE `flugzeuge` (
`Id` int(11) NOT NULL,
`Modell` varchar(100) NOT NULL,
`Plaetze` int(11) NOT NULL,
`Lagerflaeche` float NOT NULL,
`Gewicht` float NOT NULL,
`Kerosinverbrauch` float NOT NULL,
`Stundengeschwindigkeit` float NOT NULL,
`Stundenstand` float NOT NULL,
`Herstellungsdatum` date NOT NULL,
`IstDefekt` tinyint(1) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
--
-- Daten für Tabelle `flugzeuge`
--
INSERT INTO `flugzeuge` (`Id`, `Modell`, `Plaetze`, `Lagerflaeche`, `Gewicht`, `Kerosinverbrauch`, `Stundengeschwindigkeit`, `Stundenstand`, `Herstellungsdatum`, `IstDefekt`) VALUES
(1, 'Bombardier Global 7500', 19, 20.5, 23000, 1100, 950, 450.5, '2021-03-15', 0),
(2, 'Cessna Citation X', 12, 10, 16000, 950, 970, 1200, '2018-06-20', 0),
(3, 'Gulfstream G650ER', 18, 18.5, 25000, 1200, 960, 800.2, '2020-01-10', 0),
(4, 'Embraer Praetor 600', 12, 12, 14000, 850, 890, 300, '2022-11-05', 0),
(5, 'Boeing 737-800', 189, 45, 41000, 2400, 840, 15000.5, '2015-08-12', 0),
(6, 'Airbus A320neo', 180, 42, 42000, 2200, 840, 5000, '2019-04-22', 0),
(7, 'Dassault Falcon 8X', 16, 15, 18000, 1050, 920, 650, '2021-09-30', 0),
(8, 'Bombardier Challenger 350', 10, 8.5, 11000, 900, 870, 2100, '2017-02-14', 0),
(9, 'HondaJet Elite', 6, 4, 4800, 450, 780, 150, '2023-01-01', 0),
(10, 'Pilatus PC-24', 10, 6, 8000, 600, 815, 900, '2019-12-12', 0),
(11, 'Boeing 787 Dreamliner', 290, 120, 115000, 4800, 903, 8000, '2016-07-04', 1),
(12, 'Airbus A350-900', 325, 130, 135000, 5000, 910, 4500, '2018-10-20', 0),
(13, 'Embraer Phenom 300', 9, 5, 8000, 550, 830, 2200.5, '2017-05-15', 0),
(14, 'Learjet 75 Liberty', 8, 4.5, 7500, 600, 860, 1800, '2018-08-01', 0),
(15, 'Beechcraft King Air 350', 11, 6, 6800, 400, 580, 5400, '2012-03-30', 0);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `mitarbeiter`
--
CREATE TABLE `mitarbeiter` (
`Id` int(11) NOT NULL,
`Vorname` varchar(100) NOT NULL,
`Nachname` varchar(100) NOT NULL,
`Position` varchar(100) NOT NULL,
`MitarbeiterAlter` int(11) NOT NULL,
`ArbeitsstundenProWoche` float NOT NULL,
`IstVerfuegbar` tinyint(1) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `piloten`
--
CREATE TABLE `piloten` (
`Id` int(11) NOT NULL,
`Vorname` varchar(100) NOT NULL,
`Nachname` varchar(100) NOT NULL,
`Flugerfahrung` float NOT NULL,
`Groesse` float NOT NULL,
`Bewertung` float NOT NULL,
`Pilotalter` date NOT NULL,
`Gender` varchar(10) DEFAULT NULL,
`IstVerfuegbar` tinyint(1) NOT NULL DEFAULT 1,
`Sprachen` text DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
--
-- Daten für Tabelle `piloten`
--
INSERT INTO `piloten` (`Id`, `Vorname`, `Nachname`, `Flugerfahrung`, `Groesse`, `Bewertung`, `Pilotalter`, `Gender`, `IstVerfuegbar`, `Sprachen`) VALUES
(1, 'Markus', 'Weber', 8500, 1.82, 4.8, '1980-05-15', 'M', 1, 'Deutsch, Englisch'),
(2, 'Julia', 'Müller', 4200, 1.7, 4.9, '1992-11-02', 'F', 1, 'Deutsch, Englisch, Französisch'),
(3, 'James', 'Smith', 12000, 1.78, 5, '1975-03-22', 'M', 1, 'Englisch, Spanisch'),
(4, 'Sophie', 'Dubois', 3500, 1.68, 4.5, '1995-07-14', 'F', 1, 'Französisch, Englisch'),
(5, 'Alessandro', 'Rossi', 6000, 1.85, 4.7, '1988-09-09', 'M', 1, 'Italienisch, Englisch, Deutsch'),
(6, 'Yuki', 'Tanaka', 5500, 1.72, 4.8, '1990-01-30', 'M', 1, 'Japanisch, Englisch'),
(7, 'Sarah', 'Connor', 9800, 1.75, 5, '1982-08-12', 'F', 0, 'Englisch, Deutsch'),
(8, 'Thomas', 'Schneider', 1500, 1.8, 4.2, '1998-04-05', 'M', 1, 'Deutsch, Englisch'),
(9, 'Elena', 'Popova', 7200, 1.69, 4.6, '1985-12-25', 'F', 1, 'Russisch, Englisch, Deutsch'),
(10, 'Carlos', 'Mendez', 11000, 1.76, 4.9, '1978-06-18', 'M', 1, 'Spanisch, Portugiesisch, Englisch'),
(11, 'Emma', 'Wilson', 2900, 1.65, 4.4, '1996-02-14', 'F', 1, 'Englisch'),
(12, 'Lukas', 'Hofer', 4800, 1.88, 4.7, '1991-10-31', 'M', 1, 'Deutsch, Italienisch'),
(13, 'Anna', 'Kovalenko', 6500, 1.73, 4.8, '1987-03-08', 'F', 1, 'Ukrainisch, Englisch, Polnisch'),
(14, 'David', 'Brown', 13500, 1.81, 5, '1970-11-20', 'M', 0, 'Englisch, Französisch'),
(15, 'Maria', 'Garcia', 5100, 1.67, 4.6, '1993-05-05', 'F', 1, 'Spanisch, Englisch');
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `users`
--
CREATE TABLE `users` (
`Id` int(11) NOT NULL,
`Vorname` varchar(100) NOT NULL,
`Nachname` varchar(100) NOT NULL,
`Email` varchar(100) NOT NULL,
`PasswortHash` varchar(255) NOT NULL,
`Rolle` varchar(50) NOT NULL,
`Stadt` varchar(100) DEFAULT NULL,
`Anrede` varchar(20) DEFAULT NULL,
`Geburtsdatum` date DEFAULT NULL,
`CreatedAt` datetime NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
--
-- Daten für Tabelle `users`
--
INSERT INTO `users` (`Id`, `Vorname`, `Nachname`, `Email`, `PasswortHash`, `Rolle`, `Stadt`, `Anrede`, `Geburtsdatum`, `CreatedAt`) VALUES
(5, 'Stas', 'Kharchenko', 'Sayatguvancho@gmail.com', '$2a$11$3H50ugTZozarAor71LXA0OPTJc26znTwFR9M/ijge4.OPmyxbvKBa', 'User', 'Paderborn', 'Herr', '2026-02-01', '2026-02-03 13:57:17'),
(7, 'younes', 'el haddoury', 'elhaddouryyounes@gmail.com', '$2a$11$b31F.IK7S6q8sGhkUuVtp.egmIq/9nBYcBFIMEgONWSl9zJ18NCcm', 'User', 'paderborn', 'Herr', '2005-05-17', '2026-02-04 14:17:11'),
(9, 'System', 'Root', 'admin@skyteam.com', '$2a$11$3rjo3TbH7GTderZp1ZsdreWnVmSjGhAgeEAH7HiWkTenGrchrEaou', 'Admin', 'HQ', NULL, NULL, '2026-02-04 14:56:26');
--
-- Indizes der exportierten Tabellen
--
--
-- Indizes für die Tabelle `buchungen`
--
ALTER TABLE `buchungen`
ADD PRIMARY KEY (`Id`),
ADD KEY `UserId` (`UserId`),
ADD KEY `FlugId` (`FlugId`);
--
-- Indizes für die Tabelle `fluege`
--
ALTER TABLE `fluege`
ADD PRIMARY KEY (`Id`),
ADD KEY `FlugzeugId` (`FlugzeugId`),
ADD KEY `PilotId` (`PilotId`),
ADD KEY `ErstelltVon` (`ErstelltVon`);
--
-- Indizes für die Tabelle `flugzeuge`
--
ALTER TABLE `flugzeuge`
ADD PRIMARY KEY (`Id`);
--
-- Indizes für die Tabelle `mitarbeiter`
--
ALTER TABLE `mitarbeiter`
ADD PRIMARY KEY (`Id`);
--
-- Indizes für die Tabelle `piloten`
--
ALTER TABLE `piloten`
ADD PRIMARY KEY (`Id`);
--
-- Indizes für die Tabelle `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`Id`);
--
-- AUTO_INCREMENT für exportierte Tabellen
--
--
-- AUTO_INCREMENT für Tabelle `buchungen`
--
ALTER TABLE `buchungen`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13;
--
-- AUTO_INCREMENT für Tabelle `fluege`
--
ALTER TABLE `fluege`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=95;
--
-- AUTO_INCREMENT für Tabelle `flugzeuge`
--
ALTER TABLE `flugzeuge`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=16;
--
-- AUTO_INCREMENT für Tabelle `mitarbeiter`
--
ALTER TABLE `mitarbeiter`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `piloten`
--
ALTER TABLE `piloten`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=16;
--
-- AUTO_INCREMENT für Tabelle `users`
--
ALTER TABLE `users`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10;
--
-- Constraints der exportierten Tabellen
--
--
-- Constraints der Tabelle `buchungen`
--
ALTER TABLE `buchungen`
ADD CONSTRAINT `buchungen_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`) ON DELETE CASCADE,
ADD CONSTRAINT `buchungen_ibfk_2` FOREIGN KEY (`FlugId`) REFERENCES `fluege` (`Id`) ON DELETE CASCADE;
--
-- Constraints der Tabelle `fluege`
--
ALTER TABLE `fluege`
ADD CONSTRAINT `fluege_ibfk_1` FOREIGN KEY (`FlugzeugId`) REFERENCES `flugzeuge` (`Id`) ON DELETE CASCADE,
ADD CONSTRAINT `fluege_ibfk_2` FOREIGN KEY (`PilotId`) REFERENCES `piloten` (`Id`) ON DELETE CASCADE,
ADD CONSTRAINT `fluege_ibfk_3` FOREIGN KEY (`ErstelltVon`) REFERENCES `users` (`Id`) ON DELETE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,209 +0,0 @@
-- ==========================================================
-- 1. SETUP & CLEANUP
-- ==========================================================
SET FOREIGN_KEY_CHECKS = 0;
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
-- Drop tables if they exist to ensure a clean slate
DROP TABLE IF EXISTS buchungen;
DROP TABLE IF EXISTS fluege;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS piloten;
DROP TABLE IF EXISTS flugzeuge;
DROP TABLE IF EXISTS mitarbeiter;
-- ==========================================================
-- 2. TABLE CREATION
-- ==========================================================
-- USERS TABLE
CREATE TABLE users (
Id int(11) NOT NULL AUTO_INCREMENT,
Vorname varchar(100) NOT NULL,
Nachname varchar(100) NOT NULL,
Email varchar(100) NOT NULL,
PasswortHash varchar(255) NOT NULL,
Rolle varchar(50) NOT NULL DEFAULT 'User', -- 'User' or 'Admin'
Stadt varchar(100) DEFAULT NULL,
Anrede varchar(20) DEFAULT NULL,
Geburtsdatum date DEFAULT NULL,
CreatedAt datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (Id),
UNIQUE KEY Email (Email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- AIRPLANES TABLE
CREATE TABLE flugzeuge (
Id int(11) NOT NULL AUTO_INCREMENT,
Modell varchar(100) NOT NULL,
Plaetze int(11) NOT NULL,
Lagerflaeche float NOT NULL,
Gewicht float NOT NULL,
Kerosinverbrauch float NOT NULL,
Stundengeschwindigkeit float NOT NULL,
Stundenstand float NOT NULL,
Herstellungsdatum date NOT NULL,
IstDefekt tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- PILOTS TABLE
CREATE TABLE piloten (
Id int(11) NOT NULL AUTO_INCREMENT,
Vorname varchar(100) NOT NULL,
Nachname varchar(100) NOT NULL,
Flugerfahrung float NOT NULL,
Groesse float NOT NULL,
Bewertung float NOT NULL,
Pilotalter date NOT NULL,
Gender varchar(10) DEFAULT NULL,
IstVerfuegbar tinyint(1) NOT NULL DEFAULT 1,
Sprachen text DEFAULT NULL,
PRIMARY KEY (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- FLIGHTS TABLE
CREATE TABLE fluege (
Id int(11) NOT NULL AUTO_INCREMENT,
Abflugort varchar(100) NOT NULL,
Zielort varchar(100) NOT NULL,
Abflugdatum datetime NOT NULL,
Ankunftsdatum datetime NOT NULL,
Flugnummer varchar(20) NOT NULL,
Preis decimal(10,2) NOT NULL,
Created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
FlugzeugId int(11) NOT NULL,
PilotId int(11) NOT NULL,
ErstelltVon int(11) NOT NULL,
PRIMARY KEY (Id),
KEY FlugzeugId (FlugzeugId),
KEY PilotId (PilotId),
KEY ErstelltVon (ErstelltVon),
CONSTRAINT fluege_ibfk_1 FOREIGN KEY (FlugzeugId) REFERENCES flugzeuge (Id) ON DELETE CASCADE,
CONSTRAINT fluege_ibfk_2 FOREIGN KEY (PilotId) REFERENCES piloten (Id) ON DELETE CASCADE,
CONSTRAINT fluege_ibfk_3 FOREIGN KEY (ErstelltVon) REFERENCES users (Id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- BOOKINGS TABLE
CREATE TABLE buchungen (
Id int(11) NOT NULL AUTO_INCREMENT,
UserId int(11) NOT NULL,
FlugId int(11) NOT NULL,
BuchungsDatum datetime DEFAULT CURRENT_TIMESTAMP,
Status varchar(50) DEFAULT 'Bestätigt',
PRIMARY KEY (Id),
KEY UserId (UserId),
KEY FlugId (FlugId),
CONSTRAINT buchungen_ibfk_1 FOREIGN KEY (UserId) REFERENCES users (Id) ON DELETE CASCADE,
CONSTRAINT buchungen_ibfk_2 FOREIGN KEY (FlugId) REFERENCES fluege (Id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ==========================================================
-- 3. DATA INJECTION
-- ==========================================================
-- A. ADMIN ACCOUNT (ID 1)
-- Credentials: admin@skyteam.com / admin
-- Hash generated via BCrypt for "admin"
INSERT INTO users (Id, Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, Anrede, CreatedAt) VALUES
(1, 'System', 'Admin', 'admin@skyteam.com', '$2a$11$s/l.wH4/vG.9TjF3.uz.CO4.d.z.y.t.x.w.v.u.s.r.q.p.o', 'Admin', 'Headquarters', 'Herr', NOW());
-- B. PASSENGERS (Users)
INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, Anrede, CreatedAt) VALUES
('Lisa', 'Schmidt', 'lisa@test.com', '$2a$11$dummyhash', 'User', 'Hamburg', 'Frau', NOW()),
('Tony', 'Stark', 'ironman@avengers.com', '$2a$11$dummyhash', 'User', 'Malibu', 'Herr', NOW()),
('Peter', 'Parker', 'spidey@queens.com', '$2a$11$dummyhash', 'User', 'New York', 'Herr', NOW()),
('Natasha', 'Romanoff', 'widow@shield.com', '$2a$11$dummyhash', 'User', 'Budapest', 'Frau', NOW()),
('Bruce', 'Wayne', 'batman@gotham.com', '$2a$11$dummyhash', 'User', 'Gotham', 'Herr', NOW()),
('Clark', 'Kent', 'superman@daily.com', '$2a$11$dummyhash', 'User', 'Metropolis', 'Herr', NOW()),
('Diana', 'Prince', 'wonder@amazon.com', '$2a$11$dummyhash', 'User', 'Themyscira', 'Frau', NOW()),
('Han', 'Solo', 'han@falcon.com', '$2a$11$dummyhash', 'User', 'Space', 'Herr', NOW()),
('Luke', 'Skywalker', 'luke@jedi.com', '$2a$11$dummyhash', 'User', 'Tatooine', 'Herr', NOW()),
('Leia', 'Organa', 'leia@rebel.com', '$2a$11$dummyhash', 'User', 'Alderaan', 'Frau', NOW());
-- C. PLANES (15 Units)
INSERT INTO flugzeuge (Modell, Plaetze, Lagerflaeche, Gewicht, Kerosinverbrauch, Stundengeschwindigkeit, Stundenstand, Herstellungsdatum, IstDefekt) VALUES
('Bombardier Global 7500', 19, 20.5, 23000, 1100, 950, 450.5, '2021-03-15', 0),
('Cessna Citation X', 12, 10.0, 16000, 950, 970, 1200.0, '2018-06-20', 0),
('Gulfstream G650ER', 18, 18.5, 25000, 1200, 960, 800.2, '2020-01-10', 0),
('Embraer Praetor 600', 12, 12.0, 14000, 850, 890, 300.0, '2022-11-05', 0),
('Boeing 737-800', 189, 45.0, 41000, 2400, 840, 15000.5, '2015-08-12', 0),
('Airbus A320neo', 180, 42.0, 42000, 2200, 840, 5000.0, '2019-04-22', 0),
('Dassault Falcon 8X', 16, 15.0, 18000, 1050, 920, 650.0, '2021-09-30', 0),
('Bombardier Challenger 350', 10, 8.5, 11000, 900, 870, 2100.0, '2017-02-14', 0),
('HondaJet Elite', 6, 4.0, 4800, 450, 780, 150.0, '2023-01-01', 0),
('Pilatus PC-24', 10, 6.0, 8000, 600, 815, 900.0, '2019-12-12', 0),
('Boeing 787 Dreamliner', 290, 120.0, 115000, 4800, 903, 8000.0, '2016-07-04', 1),
('Airbus A350-900', 325, 130.0, 135000, 5000, 910, 4500.0, '2018-10-20', 0),
('Embraer Phenom 300', 9, 5.0, 8000, 550, 830, 2200.5, '2017-05-15', 0),
('Learjet 75 Liberty', 8, 4.5, 7500, 600, 860, 1800.0, '2018-08-01', 0),
('Beechcraft King Air 350', 11, 6.0, 6800, 400, 580, 5400.0, '2012-03-30', 0);
-- D. PILOTS (15 Persons)
INSERT INTO piloten (Vorname, Nachname, Flugerfahrung, Groesse, Bewertung, Pilotalter, Gender, IstVerfuegbar, Sprachen) VALUES
('Markus', 'Weber', 8500, 1.82, 4.8, '1980-05-15', 'M', 1, 'Deutsch, Englisch'),
('Julia', 'Müller', 4200, 1.70, 4.9, '1992-11-02', 'F', 1, 'Deutsch, Englisch, Französisch'),
('James', 'Smith', 12000, 1.78, 5.0, '1975-03-22', 'M', 1, 'Englisch, Spanisch'),
('Sophie', 'Dubois', 3500, 1.68, 4.5, '1995-07-14', 'F', 1, 'Französisch, Englisch'),
('Alessandro', 'Rossi', 6000, 1.85, 4.7, '1988-09-09', 'M', 1, 'Italienisch, Englisch, Deutsch'),
('Yuki', 'Tanaka', 5500, 1.72, 4.8, '1990-01-30', 'M', 1, 'Japanisch, Englisch'),
('Sarah', 'Connor', 9800, 1.75, 5.0, '1982-08-12', 'F', 0, 'Englisch, Deutsch'),
('Thomas', 'Schneider', 1500, 1.80, 4.2, '1998-04-05', 'M', 1, 'Deutsch, Englisch'),
('Elena', 'Popova', 7200, 1.69, 4.6, '1985-12-25', 'F', 1, 'Russisch, Englisch, Deutsch'),
('Carlos', 'Mendez', 11000, 1.76, 4.9, '1978-06-18', 'M', 1, 'Spanisch, Portugiesisch, Englisch'),
('Emma', 'Wilson', 2900, 1.65, 4.4, '1996-02-14', 'F', 1, 'Englisch'),
('Lukas', 'Hofer', 4800, 1.88, 4.7, '1991-10-31', 'M', 1, 'Deutsch, Italienisch'),
('Anna', 'Kovalenko', 6500, 1.73, 4.8, '1987-03-08', 'F', 1, 'Ukrainisch, Englisch, Polnisch'),
('David', 'Brown', 13500, 1.81, 5.0, '1970-11-20', 'M', 0, 'Englisch, Französisch'),
('Maria', 'Garcia', 5100, 1.67, 4.6, '1993-05-05', 'F', 1, 'Spanisch, Englisch');
-- E. FLIGHTS (30 Records)
-- Ensure 'ErstelltVon' is 1 (The Admin)
INSERT INTO fluege (Abflugort, Zielort, Abflugdatum, Ankunftsdatum, Flugnummer, Preis, FlugzeugId, PilotId, ErstelltVon) VALUES
('Berlin', 'Tokio', '2025-05-01 08:00:00', '2025-05-01 22:00:00', 'SKY-501', 1200.00, 1, 1, 1),
('München', 'Dubai', '2025-05-02 14:00:00', '2025-05-02 23:00:00', 'SKY-502', 850.50, 2, 2, 1),
('Frankfurt', 'New York', '2025-06-10 10:00:00', '2025-06-10 18:00:00', 'SKY-503', 600.00, 3, 3, 1),
('Hamburg', 'Mallorca', '2025-06-12 06:00:00', '2025-06-12 09:00:00', 'SKY-504', 150.00, 4, 4, 1),
('London', 'Sydney', '2025-07-01 22:00:00', '2025-07-03 06:00:00', 'SKY-505', 2100.00, 5, 5, 1),
('Paris', 'Berlin', '2025-07-05 09:00:00', '2025-07-05 10:30:00', 'SKY-506', 120.00, 6, 6, 1),
('Madrid', 'Rom', '2025-07-10 11:00:00', '2025-07-10 13:30:00', 'SKY-507', 99.90, 7, 7, 1),
('Lissabon', 'Berlin', '2025-08-01 15:00:00', '2025-08-01 19:00:00', 'SKY-508', 230.00, 8, 8, 1),
('Wien', 'Zürich', '2025-08-05 08:30:00', '2025-08-05 09:45:00', 'SKY-509', 180.00, 9, 9, 1),
('Amsterdam', 'Kapstadt', '2025-09-01 20:00:00', '2025-09-02 08:00:00', 'SKY-510', 950.00, 10, 10, 1),
('Berlin', 'Istanbul', '2025-09-15 12:00:00', '2025-09-15 16:00:00', 'SKY-511', 300.00, 11, 11, 1),
('Dubai', 'Singapur', '2025-10-01 02:00:00', '2025-10-01 10:00:00', 'SKY-512', 780.00, 12, 12, 1),
('Los Angeles', 'Las Vegas', '2025-10-05 18:00:00', '2025-10-05 19:00:00', 'SKY-513', 80.00, 13, 13, 1),
('Miami', 'Cancun', '2025-11-01 10:00:00', '2025-11-01 12:00:00', 'SKY-514', 250.00, 14, 14, 1),
('Rio de Janeiro', 'Buenos Aires', '2025-11-15 14:00:00', '2025-11-15 17:00:00', 'SKY-515', 320.00, 15, 15, 1),
('Toronto', 'Vancouver', '2025-12-01 07:00:00', '2025-12-01 12:00:00', 'SKY-516', 400.00, 1, 2, 1),
('Paderborn', 'München', '2025-12-05 09:00:00', '2025-12-05 10:00:00', 'SKY-517', 150.00, 2, 3, 1),
('Dortmund', 'Kattowitz', '2025-12-10 16:00:00', '2025-12-10 18:00:00', 'SKY-518', 60.00, 3, 4, 1),
('Köln', 'London', '2026-01-01 10:00:00', '2026-01-01 11:30:00', 'SKY-519', 110.00, 4, 5, 1),
('Düsseldorf', 'Mallorca', '2026-01-15 06:00:00', '2026-01-15 08:30:00', 'SKY-520', 190.00, 5, 6, 1),
('Stuttgart', 'Berlin', '2026-02-01 08:00:00', '2026-02-01 09:15:00', 'SKY-521', 140.00, 6, 7, 1),
('Hannover', 'Paris', '2026-02-14 14:00:00', '2026-02-14 16:00:00', 'SKY-522', 210.00, 7, 8, 1),
('Nürnberg', 'Antalya', '2026-03-01 11:00:00', '2026-03-01 15:00:00', 'SKY-523', 250.00, 8, 9, 1),
('Leipzig', 'Wien', '2026-03-15 09:00:00', '2026-03-15 10:30:00', 'SKY-524', 130.00, 9, 10, 1),
('Bremen', 'Zürich', '2026-04-01 13:00:00', '2026-04-01 14:30:00', 'SKY-525', 175.00, 10, 11, 1),
('Berlin', 'Bangkok', '2026-04-20 22:00:00', '2026-04-21 14:00:00', 'SKY-526', 900.00, 11, 12, 1),
('Frankfurt', 'Hongkong', '2026-05-01 10:00:00', '2026-05-02 06:00:00', 'SKY-527', 950.00, 12, 13, 1),
('München', 'San Francisco', '2026-05-15 12:00:00', '2026-05-15 15:00:00', 'SKY-528', 1100.00, 13, 14, 1),
('Hamburg', 'Reykjavik', '2026-06-01 14:00:00', '2026-06-01 17:00:00', 'SKY-529', 350.00, 14, 15, 1),
('Berlin', 'Kopenhagen', '2026-06-10 09:00:00', '2026-06-10 10:00:00', 'SKY-530', 120.00, 15, 1, 1);
-- F. BOOKINGS (Connecting Users to Flights)
INSERT INTO buchungen (UserId, FlugId, Status, BuchungsDatum) VALUES
((SELECT Id FROM users WHERE Email='ironman@avengers.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-501'), 'Bestätigt', NOW()),
((SELECT Id FROM users WHERE Email='ironman@avengers.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-528'), 'Bestätigt', NOW()),
((SELECT Id FROM users WHERE Email='spidey@queens.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-513'), 'Bestätigt', NOW()),
((SELECT Id FROM users WHERE Email='batman@gotham.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-527'), 'Bestätigt', NOW()),
((SELECT Id FROM users WHERE Email='batman@gotham.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-502'), 'Storniert', NOW()),
((SELECT Id FROM users WHERE Email='lisa@test.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-504'), 'Bestätigt', NOW()),
((SELECT Id FROM users WHERE Email='han@falcon.com'), (SELECT Id FROM fluege WHERE Flugnummer='SKY-529'), 'Bestätigt', NOW());
-- ==========================================================
-- FINALIZATION
-- ==========================================================
SET FOREIGN_KEY_CHECKS = 1;
COMMIT;

View File

@@ -6,12 +6,9 @@
Title="ReservierungssuchePage"
Background="{DynamicResource PageBackground}">
<!--Implementierung mit Unterstützung durch ChatGPT
Datum: 17.02.2026
Prompt sinngemäß: Как реализовать многоязычный Placeholder для DatePickerTextBox в WPF с использованием ресурсов (.resx) и привязки к текущей CultureInfo, чтобы текст обновлялся при смене языка приложения?-->
<Page.Resources>
<!-- Style für Innertext DatePickerTextBox -->
<!-- Стиль для внутреннего DatePickerTextBox -->
<Style TargetType="DatePickerTextBox">
<Setter Property="Template">
<Setter.Value>
@@ -19,7 +16,7 @@
<Grid>
<ScrollViewer x:Name="PART_ContentHost"/>
<!-- placeholder -->
<!-- Наш placeholder -->
<TextBlock
Text="{x:Static properties:Resources.SelectDate}"
Foreground="Gray"

View File

@@ -9,12 +9,14 @@ namespace SkyTeam
{
public ReservierungssuchePage()
{
InitializeComponent();
}
private void SearchFlights_Click(object sender, RoutedEventArgs e)
{
// Simples Auslesen der Benutzereingaben aus den Textboxen und dem DatePicker
string from = FromBox.Text;
string to = ToBox.Text;
DateTime? date = DateBox.SelectedDate;
@@ -26,6 +28,8 @@ namespace SkyTeam
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 BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());

View File

@@ -26,6 +26,8 @@ namespace SkyTeam
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',
z.Modell AS Plane, f.Abflugdatum AS Date
FROM fluege f
@@ -87,9 +89,12 @@ namespace SkyTeam
}
// 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;
int flightId = Convert.ToInt32(row["Id"]);
if (SessionManager.CurrentUserId == 0)
{
MessageBox.Show("Fehler: Nicht eingeloggt.");
@@ -118,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 BookingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new BuchungenPage());
private void SettingsButton_Click(object sender, RoutedEventArgs e) => NavigationService.Navigate(new SettingsPage());

Binary file not shown.

View File

@@ -1 +0,0 @@
Server=mysql.pb.bib.de;uid=pbt3h24akh;pwd=Dd3dwQgPeNxW;database=pbt3h24akh_SkyTeam;