CineBook Projekt hinzugefügt

This commit is contained in:
2026-03-12 14:28:52 +01:00
commit b6357c9b8a
33 changed files with 2857 additions and 0 deletions

71
Views/AdminPanelView.xaml Normal file
View File

@@ -0,0 +1,71 @@
<UserControl x:Class="CineBook.Views.AdminPanelView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#0A0A0A">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="🛠 ADMIN-PANEL — BENUTZERVERWALTUNG"
Style="{StaticResource RetroHeader}" Margin="0,0,0,16"/>
<!-- TOOLBAR -->
<Border Grid.Row="1" Background="#111" BorderBrush="#222" BorderThickness="1" Padding="12" Margin="0,0,0,12">
<DockPanel>
<TextBlock Text="BENUTZERLISTE" Style="{StaticResource RetroLabel}" VerticalAlignment="Center"/>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="[ + BENUTZER ANLEGEN ]" Style="{StaticResource RetroButtonGreen}"
Height="30" Click="AddUserClick"/>
<Button x:Name="BtnEditUser" Content="[ ✎ BEARBEITEN ]" Style="{StaticResource RetroButton}"
Margin="6,0" Height="30" Click="EditUserClick" IsEnabled="False"/>
<Button x:Name="BtnDeleteUser" Content="[ ✕ LÖSCHEN ]" Style="{StaticResource RetroButtonRed}"
Height="30" Click="DeleteUserClick" IsEnabled="False"/>
<Button Content="[ ↺ AKTUALISIEREN ]" Style="{StaticResource RetroButtonGray}"
Margin="6,0,0,0" Height="30" Click="RefreshClick"/>
</StackPanel>
</DockPanel>
</Border>
<DataGrid x:Name="UsersGrid" Grid.Row="2" Style="{StaticResource RetroDataGrid}"
SelectionChanged="GridSelectionChanged">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Foreground" Value="#F5C518"/>
<Setter Property="FontFamily" Value="Courier New"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="BorderBrush" Value="#333"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#CCC"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#1E1A00"/>
<Setter Property="Foreground" Value="#F5C518"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding UserID}" Width="50"/>
<DataGridTextColumn Header="BENUTZERNAME" Binding="{Binding Username}" Width="160"/>
<DataGridTextColumn Header="ROLLE" Binding="{Binding Role}" Width="100"/>
<DataGridTextColumn Header="E-MAIL" Binding="{Binding Email}" Width="*"/>
<DataGridTextColumn Header="ERSTELLT AM" Binding="{Binding CreatedAt, StringFormat='dd.MM.yyyy'}" Width="120"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="StatusText" Grid.Row="3" Style="{StaticResource RetroLabel}"
Foreground="#666" Margin="0,8,0,0"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,92 @@
using CineBook.Data;
using CineBook.Helpers;
using CineBook.Models;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class AdminPanelView : UserControl
{
private readonly Repository _repo;
private User? _selected;
public AdminPanelView(Repository repo)
{
InitializeComponent();
_repo = repo;
if (!Session.IsAdmin)
{
StatusText.Text = "⚠ Kein Zugriff. Nur für Administratoren.";
return;
}
LoadUsers();
}
private void LoadUsers()
{
try
{
UsersGrid.ItemsSource = _repo.GetAllUsers();
StatusText.Text = $"● {_repo.GetAllUsers().Count} Benutzer geladen.";
}
catch (System.Exception ex) { StatusText.Text = "⚠ Fehler: " + ex.Message; }
}
private void GridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selected = UsersGrid.SelectedItem as User;
BtnEditUser.IsEnabled = _selected != null;
BtnDeleteUser.IsEnabled = _selected != null;
}
private void RefreshClick(object sender, RoutedEventArgs e) => LoadUsers();
private void AddUserClick(object sender, RoutedEventArgs e)
{
var dlg = new UserEditDialog(null);
if (dlg.ShowDialog() == true)
{
try
{
if (_repo.CreateUser(dlg.Username, dlg.Password, dlg.Role, dlg.Email))
LoadUsers();
else
MessageBox.Show("Fehler beim Anlegen. Benutzername bereits vergeben?", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (System.Exception ex) { MessageBox.Show("DB-Fehler: " + ex.Message); }
}
}
private void EditUserClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var dlg = new UserEditDialog(_selected);
if (dlg.ShowDialog() == true)
{
try
{
_repo.UpdateUser(_selected.UserID, dlg.Username, dlg.Role, dlg.Email);
LoadUsers();
}
catch (System.Exception ex) { MessageBox.Show("DB-Fehler: " + ex.Message); }
}
}
private void DeleteUserClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
if (_selected.UserID == Session.CurrentUser?.UserID)
{
MessageBox.Show("Sie können sich nicht selbst löschen!", "Fehler", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var r = MessageBox.Show($"Benutzer \"{_selected.Username}\" wirklich löschen?",
"Bestätigung", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (r == MessageBoxResult.Yes)
{
try { _repo.DeleteUser(_selected.UserID); LoadUsers(); }
catch (System.Exception ex) { MessageBox.Show("DB-Fehler: " + ex.Message); }
}
}
}

77
Views/BookingsView.xaml Normal file
View File

@@ -0,0 +1,77 @@
<UserControl x:Class="CineBook.Views.BookingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#0A0A0A">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="🎟 BUCHUNGSVERWALTUNG" Style="{StaticResource RetroHeader}" Margin="0,0,0,16"/>
<Border Grid.Row="1" Background="#111" BorderBrush="#222" BorderThickness="1" Padding="12" Margin="0,0,0,12">
<DockPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="STATUS:" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<ComboBox x:Name="StatusFilter" Width="160" Height="30"
Background="#111" Foreground="#F5C518" FontFamily="Courier New"
SelectionChanged="FilterChanged">
<ComboBoxItem>Alle</ComboBoxItem>
<ComboBoxItem>Confirmed</ComboBoxItem>
<ComboBoxItem>Cancelled</ComboBoxItem>
</ComboBox>
<Button Content="[ AKTUALISIEREN ]" Style="{StaticResource RetroButtonGray}"
Margin="8,0,0,0" Height="30" Click="RefreshClick"/>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="BtnCancel" Content="[ ✕ BUCHUNG STORNIEREN ]" Style="{StaticResource RetroButtonRed}"
Height="30" Click="CancelBookingClick" IsEnabled="False"/>
</StackPanel>
</DockPanel>
</Border>
<DataGrid x:Name="BookingsGrid" Grid.Row="2" Style="{StaticResource RetroDataGrid}"
SelectionChanged="GridSelectionChanged">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Foreground" Value="#F5C518"/>
<Setter Property="FontFamily" Value="Courier New"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="BorderBrush" Value="#333"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#CCC"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#1E1A00"/>
<Setter Property="Foreground" Value="#F5C518"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="CODE" Binding="{Binding BookingCode}" Width="130"/>
<DataGridTextColumn Header="BENUTZER" Binding="{Binding Username}" Width="100"/>
<DataGridTextColumn Header="FILM" Binding="{Binding MovieTitle}" Width="*"/>
<DataGridTextColumn Header="VORFÜHRUNG" Binding="{Binding ScreeningDisplay}" Width="150"/>
<DataGridTextColumn Header="SITZ" Binding="{Binding SeatDisplay}" Width="70"/>
<DataGridTextColumn Header="PREIS" Binding="{Binding PriceDisplay}" Width="80"/>
<DataGridTextColumn Header="STATUS" Binding="{Binding Status}" Width="90"/>
<DataGridTextColumn Header="GEBUCHT AM" Binding="{Binding DateDisplay}" Width="130"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="StatusText" Grid.Row="3" Style="{StaticResource RetroLabel}"
Foreground="#666" Margin="0,8,0,0"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,79 @@
using CineBook.Data;
using CineBook.Helpers;
using CineBook.Models;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class BookingsView : UserControl
{
private readonly Repository _repo;
private readonly User _currentUser;
private Booking? _selected;
private List<Booking> _allBookings = new();
public BookingsView(Repository repo, User user)
{
InitializeComponent();
_repo = repo;
_currentUser = user;
if (!Session.IsAdmin)
BtnCancel.Visibility = Visibility.Collapsed;
StatusFilter.SelectedIndex = 0;
LoadBookings();
}
private void LoadBookings()
{
try
{
_allBookings = Session.IsAdmin
? _repo.GetAllBookings()
: _repo.GetBookingsByUser(_currentUser.UserID);
ApplyFilter();
}
catch (System.Exception ex) { StatusText.Text = "⚠ Fehler: " + ex.Message; }
}
private void ApplyFilter()
{
var filter = (StatusFilter.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "Alle";
var data = filter == "Alle" ? _allBookings : _allBookings.Where(b => b.Status == filter).ToList();
BookingsGrid.ItemsSource = data;
StatusText.Text = $"● {data.Count} Buchungen angezeigt.";
}
private void FilterChanged(object sender, SelectionChangedEventArgs e) => ApplyFilter();
private void RefreshClick(object sender, RoutedEventArgs e) => LoadBookings();
private void GridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selected = BookingsGrid.SelectedItem as Booking;
BtnCancel.IsEnabled = _selected != null && _selected.Status == "Confirmed" && Session.IsAdmin;
}
private void CancelBookingClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var r = MessageBox.Show($"Buchung {_selected.BookingCode} wirklich stornieren?",
"Stornierung", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (r == MessageBoxResult.Yes)
{
try
{
_repo.CancelBooking(_selected.BookingID, _selected.SeatID, _selected.ScreeningID);
LoadBookings();
MessageBox.Show("Buchung erfolgreich storniert.", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (System.Exception ex)
{
MessageBox.Show("DB-Fehler: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

48
Views/LoginWindow.xaml Normal file
View File

@@ -0,0 +1,48 @@
<Window x:Class="CineBook.Views.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CineBook — Anmeldung" Height="460" Width="400"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
Background="#0A0A0A">
<Grid>
<StackPanel VerticalAlignment="Center" Margin="40">
<!-- Logo -->
<Border BorderBrush="#F5C518" BorderThickness="2" Padding="20,16" Margin="0,0,0,30">
<StackPanel>
<TextBlock Text="🎬 CINEBOOK" FontFamily="Courier New" FontSize="28"
FontWeight="Bold" Foreground="#F5C518" HorizontalAlignment="Center"/>
<TextBlock Text="━━━━━━━━━━━━━━━━━━" Foreground="#444" HorizontalAlignment="Center" Margin="0,4"/>
<TextBlock Text="K I N O V E R W A L T U N G S S Y S T E M" FontFamily="Courier New" FontSize="10"
Foreground="#888" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Status -->
<TextBlock x:Name="StatusText" Text="" Foreground="#C0392B"
FontFamily="Courier New" FontSize="12" HorizontalAlignment="Center"
Margin="0,0,0,10" Visibility="Collapsed"/>
<!-- Username -->
<TextBlock Text="BENUTZERNAME" Style="{StaticResource RetroLabel}" Margin="0,0,0,4"/>
<TextBox x:Name="UsernameBox" Style="{StaticResource RetroTextBox}"
Height="34" Margin="0,0,0,14"
KeyDown="InputKeyDown"/>
<!-- Password -->
<TextBlock Text="PASSWORT" Style="{StaticResource RetroLabel}" Margin="0,0,0,4"/>
<PasswordBox x:Name="PasswordBox" Style="{StaticResource RetroPasswordBox}"
Height="34" Margin="0,0,0,24"
KeyDown="InputKeyDown"/>
<!-- Login Button -->
<Button Content="[ ANMELDEN ]" Style="{StaticResource RetroButton}"
Height="38" Click="LoginClick"/>
<!-- Hint -->
<TextBlock Text="Demo: admin / admin123 | maria / 1" Foreground="#555"
FontFamily="Courier New" FontSize="10" HorizontalAlignment="Center"
Margin="0,20,0,0"/>
</StackPanel>
</Grid>
</Window>

88
Views/LoginWindow.xaml.cs Normal file
View File

@@ -0,0 +1,88 @@
using CineBook.Data;
using CineBook.Helpers;
using System.Windows;
using System.Windows.Input;
namespace CineBook.Views;
public partial class LoginWindow : Window
{
private readonly Repository _repo;
private readonly bool _dbConfigured;
public LoginWindow()
{
InitializeComponent();
var cs = DbConfig.GetConnectionString();
if (string.IsNullOrWhiteSpace(cs))
{
MessageBox.Show(
"Es ist keine DB-Verbindung konfiguriert.\n\n" +
"Bitte setze die Umgebungsvariable 'CINEBOOK_CONNECTION_STRING'.\n" +
"(Siehe Kommentar in Data/DbConfig.cs)",
"Konfiguration fehlt",
MessageBoxButton.OK,
MessageBoxImage.Error);
// Window bleibt offen, damit die Info sichtbar bleibt.
_repo = new Repository(new Db(""));
_dbConfigured = false;
}
else
{
var db = new Db(cs);
_repo = new Repository(db);
_dbConfigured = true;
}
UsernameBox.Focus();
}
private void LoginClick(object sender, RoutedEventArgs e) => TryLogin();
private void InputKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter) TryLogin();
}
private void TryLogin()
{
if (!_dbConfigured)
{
ShowError("DB-Verbindung nicht konfiguriert (CINEBOOK_CONNECTION_STRING). Login nicht möglich.");
return;
}
var user = UsernameBox.Text.Trim();
var pass = PasswordBox.Password;
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass))
{
ShowError("Bitte Benutzername und Passwort eingeben.");
return;
}
try
{
var found = _repo.GetUserByCredentials(user, pass);
if (found == null)
{
ShowError("Falscher Benutzername oder Passwort.");
PasswordBox.Clear();
return;
}
Session.CurrentUser = found;
var main = new MainWindow();
main.Show();
Close();
}
catch (System.Exception ex)
{
ShowError("DB-Fehler: " + ex.Message);
}
}
private void ShowError(string msg)
{
StatusText.Text = "⚠ " + msg;
StatusText.Visibility = Visibility.Visible;
}
}

118
Views/MainWindow.xaml Normal file
View File

@@ -0,0 +1,118 @@
<Window x:Class="CineBook.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CineBook — Kino Verwaltungssystem"
Height="680" Width="1100"
WindowStartupLocation="CenterScreen"
Background="#0A0A0A">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="56"/>
<RowDefinition Height="*"/>
<RowDefinition Height="28"/>
</Grid.RowDefinitions>
<!-- TOP BAR -->
<Border Grid.Row="0" Background="#111" BorderBrush="#F5C518" BorderThickness="0,0,0,2">
<DockPanel Margin="16,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="🎬" FontSize="22" VerticalAlignment="Center"/>
<TextBlock Text=" CINEBOOK" FontFamily="Courier New" FontSize="20"
FontWeight="Bold" Foreground="#F5C518" VerticalAlignment="Center"/>
<TextBlock Text=" — KINO VERWALTUNGSSYSTEM" FontFamily="Courier New"
FontSize="11" Foreground="#666" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right">
<TextBlock x:Name="UserInfoText" FontFamily="Courier New" FontSize="12"
Foreground="#888" VerticalAlignment="Center" Margin="0,0,16,0"/>
<Button Content="[ ABMELDEN ]" Style="{StaticResource RetroButtonRed}"
Height="30" Click="LogoutClick"/>
</StackPanel>
</DockPanel>
</Border>
<!-- MAIN CONTENT - TAB NAVIGATION -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- SIDEBAR NAV -->
<Border Background="#111" BorderBrush="#222" BorderThickness="0,0,2,0">
<StackPanel Margin="0,16,0,0">
<TextBlock Text="N A V I G A T I O N" FontFamily="Courier New" FontSize="9"
Foreground="#555" Margin="16,0,0,10"/>
<Button x:Name="BtnMovies" Content="🎥 FILME" Click="NavClick"
Tag="movies" Style="{StaticResource NavButton}"/>
<Button x:Name="BtnScreenings" Content="📅 VORFÜHRUNGEN" Click="NavClick"
Tag="screenings" Style="{StaticResource NavButton}"/>
<Button x:Name="BtnBookings" Content="🎟 BUCHUNGEN" Click="NavClick"
Tag="bookings" Style="{StaticResource NavButton}"/>
<Button x:Name="BtnAdmin" Content="🛠 ADMIN" Click="NavClick"
Tag="admin" Style="{StaticResource NavButton}" Visibility="Collapsed"/>
<Separator Background="#222" Margin="16,20"/>
<TextBlock Text="K O N T O" FontFamily="Courier New" FontSize="9"
Foreground="#555" Margin="16,0,0,10"/>
<Button x:Name="BtnMine" Content="👤 MEINE BUCHUNGEN" Click="NavClick"
Tag="mine" Style="{StaticResource NavButton}"/>
</StackPanel>
</Border>
<!-- VIEW AREA -->
<ContentControl x:Name="MainContent" Grid.Column="1" Background="#0A0A0A"/>
</Grid>
<!-- STATUS BAR -->
<Border Grid.Row="2" Background="#111" BorderBrush="#222" BorderThickness="0,1,0,0">
<DockPanel Margin="12,0">
<TextBlock x:Name="DbStatusText" FontFamily="Courier New" FontSize="10"
Foreground="#555" VerticalAlignment="Center"/>
<TextBlock x:Name="StatsText" DockPanel.Dock="Right" FontFamily="Courier New"
FontSize="10" Foreground="#555" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</DockPanel>
</Border>
</Grid>
<Window.Resources>
<Style x:Key="NavButton" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#888"/>
<Setter Property="FontFamily" Value="Courier New"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Padding" Value="16,10"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Foreground" Value="#F5C518"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="NavButtonActive" TargetType="Button" BasedOn="{StaticResource NavButton}">
<Setter Property="Background" Value="#1E1A00"/>
<Setter Property="Foreground" Value="#F5C518"/>
<Setter Property="BorderThickness" Value="3,0,0,0"/>
<Setter Property="BorderBrush" Value="#F5C518"/>
</Style>
</Window.Resources>
</Window>

96
Views/MainWindow.xaml.cs Normal file
View File

@@ -0,0 +1,96 @@
using CineBook.Data;
using CineBook.Helpers;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class MainWindow : Window
{
private readonly Repository _repo;
private Button? _activeNavBtn;
public MainWindow()
{
InitializeComponent();
if (!Session.IsLoggedIn)
{
// Schutz gegen NullReference, falls MainWindow direkt gestartet wird.
new LoginWindow().Show();
Close();
return;
}
var cs = DbConfig.GetConnectionString();
if (string.IsNullOrWhiteSpace(cs))
{
MessageBox.Show(
"DB-Verbindung nicht konfiguriert. Bitte setze 'CINEBOOK_CONNECTION_STRING'.",
"Konfiguration fehlt",
MessageBoxButton.OK,
MessageBoxImage.Error);
new LoginWindow().Show();
Close();
return;
}
var db = new Db(cs);
_repo = new Repository(db);
UserInfoText.Text = $"[ {Session.CurrentUser!.Username.ToUpper()} | {Session.CurrentUser!.Role.ToUpper()} ]";
if (Session.IsAdmin)
BtnAdmin.Visibility = Visibility.Visible;
SetStats();
Navigate("movies", BtnMovies);
}
private void NavClick(object sender, RoutedEventArgs e)
{
if (sender is Button btn)
Navigate(btn.Tag?.ToString() ?? "movies", btn);
}
private void Navigate(string view, Button btn)
{
if (_activeNavBtn != null)
_activeNavBtn.Style = (Style)Resources["NavButton"];
btn.Style = (Style)Resources["NavButtonActive"];
_activeNavBtn = btn;
MainContent.Content = view switch
{
"movies" => new MovieListView(_repo),
"screenings" => new ScreeningsView(_repo),
"bookings" => new BookingsView(_repo, Session.CurrentUser!),
"admin" => new AdminPanelView(_repo),
"mine" => new MyBookingsView(_repo, Session.CurrentUser!),
_ => new MovieListView(_repo)
};
}
private void LogoutClick(object sender, RoutedEventArgs e)
{
Session.Logout();
new LoginWindow().Show();
Close();
}
private void SetStats()
{
try
{
var (movies, screenings, bookings, users) = _repo.GetStats();
StatsText.Text = $"Filme: {movies} | Vorführungen: {screenings} | Buchungen: {bookings} | Benutzer: {users}";
DbStatusText.Text = "● DB VERBUNDEN";
DbStatusText.Foreground = System.Windows.Media.Brushes.LimeGreen;
}
catch
{
DbStatusText.Text = "● DB NICHT ERREICHBAR";
DbStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
}

View File

@@ -0,0 +1,74 @@
<Window x:Class="CineBook.Views.MovieEditDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Film bearbeiten" Height="420" Width="480"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Background="#0A0A0A">
<Grid Margin="24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="DialogTitle" Text="🎥 FILM BEARBEITEN" Style="{StaticResource RetroHeader}"
Margin="0,0,0,20"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="TITEL *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<TextBox x:Name="TitleBox" Grid.Column="1" Style="{StaticResource RetroTextBox}" Height="30" Margin="0,0,0,10"/>
<TextBlock Grid.Row="1" Text="GENRE" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<ComboBox x:Name="GenreBox" Grid.Row="1" Grid.Column="1" Style="{StaticResource RetroComboBox}"
Height="30" Margin="0,0,0,10" Background="#111" Foreground="#F5C518">
<ComboBoxItem>Action</ComboBoxItem>
<ComboBoxItem>Drama</ComboBoxItem>
<ComboBoxItem>Komödie</ComboBoxItem>
<ComboBoxItem>Sci-Fi</ComboBoxItem>
<ComboBoxItem>Thriller</ComboBoxItem>
<ComboBoxItem>Horror</ComboBoxItem>
<ComboBoxItem>Dokumentation</ComboBoxItem>
<ComboBoxItem>Animation</ComboBoxItem>
</ComboBox>
<TextBlock Grid.Row="2" Text="DAUER (Min)" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<TextBox x:Name="DurationBox" Grid.Row="2" Grid.Column="1" Style="{StaticResource RetroTextBox}"
Height="30" Margin="0,0,0,10"/>
<TextBlock Grid.Row="3" Text="BEWERTUNG" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<TextBox x:Name="RatingBox" Grid.Row="3" Grid.Column="1" Style="{StaticResource RetroTextBox}"
Height="30" Margin="0,0,0,10"/>
<TextBlock Grid.Row="4" Text="AKTIV" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<CheckBox x:Name="ActiveCheck" Grid.Row="4" Grid.Column="1" IsChecked="True"
Foreground="#F5C518" FontFamily="Courier New" VerticalAlignment="Center" Margin="0,0,0,10"/>
<TextBlock Grid.Row="5" Text="BESCHREIBUNG" Style="{StaticResource RetroLabel}" VerticalAlignment="Top" Margin="0,4,0,0"/>
<TextBox x:Name="DescBox" Grid.Row="5" Grid.Column="1" Style="{StaticResource RetroTextBox}"
TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto"/>
</Grid>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,16,0,0">
<TextBlock x:Name="ErrorText" Foreground="#C0392B" FontFamily="Courier New" FontSize="11"
VerticalAlignment="Center" Margin="0,0,12,0"/>
<Button Content="[ ABBRECHEN ]" Style="{StaticResource RetroButtonGray}"
Height="32" Click="CancelClick" Margin="0,0,8,0"/>
<Button Content="[ SPEICHERN ]" Style="{StaticResource RetroButtonGreen}"
Height="32" Click="SaveClick"/>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,76 @@
using CineBook.Models;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class MovieEditDialog : Window
{
public Movie? Result { get; private set; }
private readonly Movie? _editing;
public MovieEditDialog(Movie? movie)
{
InitializeComponent();
_editing = movie;
if (movie != null)
{
DialogTitle.Text = "🎥 FILM BEARBEITEN";
TitleBox.Text = movie.Title;
// Set genre combobox
foreach (ComboBoxItem item in GenreBox.Items)
if (item.Content?.ToString() == movie.Genre)
GenreBox.SelectedItem = item;
DurationBox.Text = movie.DurationMinutes.ToString();
RatingBox.Text = movie.Rating.ToString("F1");
ActiveCheck.IsChecked = movie.IsActive;
DescBox.Text = movie.Description;
}
else
{
DialogTitle.Text = "🎥 FILM HINZUFÜGEN";
RatingBox.Text = "7.0";
ActiveCheck.IsChecked = true;
}
}
private void SaveClick(object sender, RoutedEventArgs e)
{
// Validierung
if (string.IsNullOrWhiteSpace(TitleBox.Text))
{
ErrorText.Text = "⚠ Titel ist Pflichtfeld!"; return;
}
if (!int.TryParse(DurationBox.Text, out var dur) || dur <= 0)
{
ErrorText.Text = "⚠ Ungültige Dauer!"; return;
}
if (!decimal.TryParse(RatingBox.Text.Replace(',', '.'),
System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out var rating)
|| rating < 0 || rating > 10)
{
ErrorText.Text = "⚠ Bewertung muss zwischen 0-10 liegen!"; return;
}
Result = new Movie
{
MovieID = _editing?.MovieID ?? 0,
Title = TitleBox.Text.Trim(),
Genre = (GenreBox.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "",
DurationMinutes = dur,
Rating = rating,
IsActive = ActiveCheck.IsChecked == true,
Description = DescBox.Text.Trim()
};
DialogResult = true;
Close();
}
private void CancelClick(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
}

86
Views/MovieListView.xaml Normal file
View File

@@ -0,0 +1,86 @@
<UserControl x:Class="CineBook.Views.MovieListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#0A0A0A">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- HEADER -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,16">
<TextBlock Text="🎥 FILMVERWALTUNG" Style="{StaticResource RetroHeader}"/>
</StackPanel>
<!-- TOOLBAR -->
<Border Grid.Row="1" Background="#111" BorderBrush="#222" BorderThickness="1" Padding="12" Margin="0,0,0,12">
<DockPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="SUCHE:" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<TextBox x:Name="SearchBox" Style="{StaticResource RetroTextBox}"
Width="220" Height="30" TextChanged="SearchChanged"/>
<Button Content="[ SUCHEN ]" Style="{StaticResource RetroButton}"
Margin="8,0,0,0" Height="30" Click="SearchClick"/>
<Button Content="[ ALLE ]" Style="{StaticResource RetroButtonGray}"
Margin="4,0,0,0" Height="30" Click="LoadAllClick"/>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="BtnAdd" Content="[ + HINZUFÜGEN ]" Style="{StaticResource RetroButtonGreen}"
Height="30" Click="AddClick"/>
<Button x:Name="BtnEdit" Content="[ ✎ BEARBEITEN ]" Style="{StaticResource RetroButton}"
Margin="6,0" Height="30" Click="EditClick" IsEnabled="False"/>
<Button x:Name="BtnDelete" Content="[ ✕ LÖSCHEN ]" Style="{StaticResource RetroButtonRed}"
Height="30" Click="DeleteClick" IsEnabled="False"/>
</StackPanel>
</DockPanel>
</Border>
<!-- DATA GRID -->
<DataGrid x:Name="MoviesGrid" Grid.Row="2" Style="{StaticResource RetroDataGrid}"
SelectionChanged="GridSelectionChanged">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Foreground" Value="#F5C518"/>
<Setter Property="FontFamily" Value="Courier New"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="BorderBrush" Value="#333"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#CCC"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#1E1A00"/>
<Setter Property="Foreground" Value="#F5C518"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#161200"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding MovieID}" Width="50"/>
<DataGridTextColumn Header="TITEL" Binding="{Binding Title}" Width="*"/>
<DataGridTextColumn Header="GENRE" Binding="{Binding Genre}" Width="100"/>
<DataGridTextColumn Header="DAUER" Binding="{Binding DurationDisplay}" Width="90"/>
<DataGridTextColumn Header="BEWERTUNG" Binding="{Binding RatingDisplay}" Width="100"/>
<DataGridCheckBoxColumn Header="AKTIV" Binding="{Binding IsActive}" Width="70"/>
<DataGridTextColumn Header="BESCHREIBUNG" Binding="{Binding Description}" Width="200"/>
</DataGrid.Columns>
</DataGrid>
<!-- STATUS -->
<TextBlock x:Name="StatusText" Grid.Row="3" Style="{StaticResource RetroLabel}"
Foreground="#666" Margin="0,8,0,0"/>
</Grid>
</UserControl>

120
Views/MovieListView.xaml.cs Normal file
View File

@@ -0,0 +1,120 @@
using CineBook.Data;
using CineBook.Helpers;
using CineBook.Models;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class MovieListView : UserControl
{
private readonly Repository _repo;
private Movie? _selected;
public MovieListView(Repository repo)
{
InitializeComponent();
_repo = repo;
// Nur Admins dürfen bearbeiten/löschen
if (!Session.IsAdmin)
{
BtnAdd.IsEnabled = false;
BtnAdd.Visibility = Visibility.Collapsed;
BtnDelete.Visibility = Visibility.Collapsed;
BtnEdit.Visibility = Visibility.Collapsed;
}
LoadMovies();
}
private void LoadMovies()
{
try
{
MoviesGrid.ItemsSource = _repo.GetMovies();
StatusText.Text = $"● {_repo.GetMovies().Count} Filme geladen.";
}
catch (System.Exception ex)
{
StatusText.Text = "⚠ Fehler: " + ex.Message;
}
}
private void GridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selected = MoviesGrid.SelectedItem as Movie;
BtnEdit.IsEnabled = _selected != null && Session.IsAdmin;
BtnDelete.IsEnabled = _selected != null && Session.IsAdmin;
}
private void SearchChanged(object sender, TextChangedEventArgs e) { }
private void SearchClick(object sender, RoutedEventArgs e)
{
var term = SearchBox.Text.Trim();
if (string.IsNullOrEmpty(term)) { LoadMovies(); return; }
try { MoviesGrid.ItemsSource = _repo.SearchMovies(term); }
catch (System.Exception ex) { StatusText.Text = "⚠ " + ex.Message; }
}
private void LoadAllClick(object sender, RoutedEventArgs e)
{
SearchBox.Text = "";
LoadMovies();
}
private void AddClick(object sender, RoutedEventArgs e)
{
var dlg = new MovieEditDialog(null);
if (dlg.ShowDialog() == true && dlg.Result != null)
{
try
{
if (_repo.CreateMovie(dlg.Result))
LoadMovies();
else
MessageBox.Show("Fehler beim Speichern.", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (System.Exception ex)
{
MessageBox.Show("DB-Fehler: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private void EditClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var dlg = new MovieEditDialog(_selected);
if (dlg.ShowDialog() == true && dlg.Result != null)
{
try
{
if (_repo.UpdateMovie(dlg.Result))
LoadMovies();
}
catch (System.Exception ex)
{
MessageBox.Show("DB-Fehler: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private void DeleteClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var r = MessageBox.Show($"Film \"{_selected.Title}\" wirklich löschen?", "Bestätigung",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (r == MessageBoxResult.Yes)
{
try
{
_repo.DeleteMovie(_selected.MovieID);
LoadMovies();
}
catch (System.Exception ex)
{
MessageBox.Show("DB-Fehler: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

84
Views/MyBookingsView.xaml Normal file
View File

@@ -0,0 +1,84 @@
<UserControl x:Class="CineBook.Views.MyBookingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#0A0A0A">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="0,0,0,16">
<TextBlock Text="👤 MEINE BUCHUNGEN" Style="{StaticResource RetroHeader}"/>
<TextBlock x:Name="UserNameText" FontFamily="Courier New" FontSize="11" Foreground="#666" Margin="0,4,0,0"/>
</StackPanel>
<!-- STATS PANEL -->
<Border Grid.Row="1" Background="#111" BorderBrush="#222" BorderThickness="1" Padding="16,12" Margin="0,0,0,14">
<StackPanel Orientation="Horizontal">
<Border Background="#1B2E1B" BorderBrush="#27AE60" BorderThickness="1" Padding="16,8" CornerRadius="2" Margin="0,0,12,0">
<StackPanel>
<TextBlock x:Name="TotalBookingsText" Text="0" FontFamily="Courier New" FontSize="22"
FontWeight="Bold" Foreground="#27AE60" HorizontalAlignment="Center"/>
<TextBlock Text="BUCHUNGEN" FontFamily="Courier New" FontSize="9" Foreground="#555" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<Border Background="#2A1A00" BorderBrush="#F5C518" BorderThickness="1" Padding="16,8" CornerRadius="2" Margin="0,0,12,0">
<StackPanel>
<TextBlock x:Name="TotalSpentText" Text="0 €" FontFamily="Courier New" FontSize="22"
FontWeight="Bold" Foreground="#F5C518" HorizontalAlignment="Center"/>
<TextBlock Text="AUSGEGEBEN" FontFamily="Courier New" FontSize="9" Foreground="#555" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<Border Background="#2A1A1A" BorderBrush="#C0392B" BorderThickness="1" Padding="16,8" CornerRadius="2">
<StackPanel>
<TextBlock x:Name="ActiveBookingsText" Text="0" FontFamily="Courier New" FontSize="22"
FontWeight="Bold" Foreground="#C0392B" HorizontalAlignment="Center"/>
<TextBlock Text="AKTIV" FontFamily="Courier New" FontSize="9" Foreground="#555" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</StackPanel>
</Border>
<DataGrid x:Name="MyGrid" Grid.Row="2" Style="{StaticResource RetroDataGrid}">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Foreground" Value="#F5C518"/>
<Setter Property="FontFamily" Value="Courier New"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="BorderBrush" Value="#333"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#CCC"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#1E1A00"/>
<Setter Property="Foreground" Value="#F5C518"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="BUCHUNGSCODE" Binding="{Binding BookingCode}" Width="140"/>
<DataGridTextColumn Header="FILM" Binding="{Binding MovieTitle}" Width="*"/>
<DataGridTextColumn Header="VORFÜHRUNG" Binding="{Binding ScreeningDisplay}" Width="150"/>
<DataGridTextColumn Header="SITZ" Binding="{Binding SeatDisplay}" Width="70"/>
<DataGridTextColumn Header="PREIS" Binding="{Binding PriceDisplay}" Width="80"/>
<DataGridTextColumn Header="STATUS" Binding="{Binding Status}" Width="100"/>
<DataGridTextColumn Header="DATUM" Binding="{Binding DateDisplay}" Width="130"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="StatusText" Grid.Row="3" Style="{StaticResource RetroLabel}"
Foreground="#666" Margin="0,8,0,0"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,36 @@
using CineBook.Data;
using CineBook.Models;
using System.Linq;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class MyBookingsView : UserControl
{
private readonly Repository _repo;
private readonly User _user;
public MyBookingsView(Repository repo, User user)
{
InitializeComponent();
_repo = repo;
_user = user;
UserNameText.Text = $"Angemeldet als: {user.Username} ({user.Role}) | {user.Email}";
Load();
}
private void Load()
{
try
{
var bookings = _repo.GetBookingsByUser(_user.UserID);
MyGrid.ItemsSource = bookings;
TotalBookingsText.Text = bookings.Count.ToString();
TotalSpentText.Text = bookings.Where(b => b.Status == "Confirmed").Sum(b => b.TotalPrice).ToString("F2") + " €";
ActiveBookingsText.Text = bookings.Count(b => b.Status == "Confirmed").ToString();
StatusText.Text = $"● {bookings.Count} Buchungen gefunden.";
}
catch (System.Exception ex) { StatusText.Text = "⚠ Fehler: " + ex.Message; }
}
}

View File

@@ -0,0 +1,64 @@
<Window x:Class="CineBook.Views.ScreeningEditDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Neue Vorführung" Height="360" Width="440"
WindowStartupLocation="CenterOwner" ResizeMode="NoResize"
Background="#0A0A0A">
<Grid Margin="24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="📅 NEUE VORFÜHRUNG" Style="{StaticResource RetroHeader}" Margin="0,0,0,20"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="FILM *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<ComboBox x:Name="MovieBox" Grid.Column="1" Height="30" Margin="0,0,0,10"
Background="#111" Foreground="#F5C518" FontFamily="Courier New"/>
<TextBlock Grid.Row="1" Text="DATUM *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<DatePicker x:Name="DatePicker" Grid.Row="1" Grid.Column="1" Height="30" Margin="0,0,0,10"
Background="#111" Foreground="#F5C518" FontFamily="Courier New"/>
<TextBlock Grid.Row="2" Text="UHRZEIT *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<TextBox x:Name="TimeBox" Grid.Row="2" Grid.Column="1" Style="{StaticResource RetroTextBox}"
Height="30" Margin="0,0,0,10" Text="18:00"/>
<TextBlock Grid.Row="3" Text="SAAL *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<ComboBox x:Name="HallBox" Grid.Row="3" Grid.Column="1" Height="30" Margin="0,0,0,10"
Background="#111" Foreground="#F5C518" FontFamily="Courier New">
<ComboBoxItem>Saal 1</ComboBoxItem>
<ComboBoxItem>Saal 2</ComboBoxItem>
<ComboBoxItem>Saal 3</ComboBoxItem>
<ComboBoxItem>VIP-Saal</ComboBoxItem>
</ComboBox>
<TextBlock Grid.Row="4" Text="PREIS (€) *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center"/>
<TextBox x:Name="PriceBox" Grid.Row="4" Grid.Column="1" Style="{StaticResource RetroTextBox}"
Height="30" Text="12.00"/>
</Grid>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,16,0,0">
<TextBlock x:Name="ErrorText" Foreground="#C0392B" FontFamily="Courier New" FontSize="11"
VerticalAlignment="Center" Margin="0,0,12,0"/>
<Button Content="[ ABBRECHEN ]" Style="{StaticResource RetroButtonGray}" Height="32"
Click="CancelClick" Margin="0,0,8,0"/>
<Button Content="[ SPEICHERN ]" Style="{StaticResource RetroButtonGreen}" Height="32"
Click="SaveClick"/>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,87 @@
using CineBook.Models;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class ScreeningEditDialog : Window
{
public Screening? Result { get; private set; }
private readonly List<Movie> _movies;
private readonly Screening? _edit;
public ScreeningEditDialog(List<Movie> movies, Screening? edit = null)
{
InitializeComponent();
_movies = movies;
_edit = edit;
foreach (var m in movies)
MovieBox.Items.Add(new ComboBoxItem { Content = m.Title, Tag = m.MovieID, Foreground = System.Windows.Media.Brushes.Gold, FontFamily = new System.Windows.Media.FontFamily("Courier New") });
if (MovieBox.Items.Count > 0) MovieBox.SelectedIndex = 0;
HallBox.SelectedIndex = 0;
DatePicker.SelectedDate = DateTime.Today.AddDays(1);
if (_edit != null)
{
Title = "Vorführung bearbeiten";
// Film vorauswählen
for (var i = 0; i < MovieBox.Items.Count; i++)
{
if (MovieBox.Items[i] is ComboBoxItem ci && (int)ci.Tag == _edit.MovieID)
{
MovieBox.SelectedIndex = i;
break;
}
}
DatePicker.SelectedDate = _edit.ScreeningDate;
TimeBox.Text = _edit.StartTime.ToString(@"hh\:mm");
// Hall vorauswählen
for (var i = 0; i < HallBox.Items.Count; i++)
{
if (HallBox.Items[i] is ComboBoxItem hi && (hi.Content?.ToString() ?? "") == _edit.Hall)
{
HallBox.SelectedIndex = i;
break;
}
}
PriceBox.Text = _edit.PricePerSeat.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
}
private void SaveClick(object sender, RoutedEventArgs e)
{
if (MovieBox.SelectedItem is not ComboBoxItem mi) { ErrorText.Text = "⚠ Film auswählen!"; return; }
if (DatePicker.SelectedDate == null) { ErrorText.Text = "⚠ Datum auswählen!"; return; }
if (!TimeSpan.TryParse(TimeBox.Text, out var time)) { ErrorText.Text = "⚠ Uhrzeit ungültig (HH:MM)!"; return; }
if (HallBox.SelectedItem is not ComboBoxItem hi) { ErrorText.Text = "⚠ Saal auswählen!"; return; }
if (!decimal.TryParse(PriceBox.Text.Replace(',', '.'),
System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out var price) || price <= 0)
{ ErrorText.Text = "⚠ Ungültiger Preis!"; return; }
Result = new Screening
{
ScreeningID = _edit?.ScreeningID ?? 0,
MovieID = (int)mi.Tag,
ScreeningDate = DatePicker.SelectedDate.Value,
StartTime = time,
Hall = hi.Content?.ToString() ?? "Saal 1",
// Seats werden beim Anlegen im Repository automatisch erzeugt.
// Beim Bearbeiten werden TotalSeats/AvailableSeats nicht verändert (damit Buchungen konsistent bleiben).
TotalSeats = _edit?.TotalSeats ?? 80,
AvailableSeats = _edit?.AvailableSeats ?? 80,
PricePerSeat = price
};
DialogResult = true;
Close();
}
private void CancelClick(object sender, RoutedEventArgs e) { DialogResult = false; Close(); }
}

73
Views/ScreeningsView.xaml Normal file
View File

@@ -0,0 +1,73 @@
<UserControl x:Class="CineBook.Views.ScreeningsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#0A0A0A">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="📅 VORFÜHRUNGEN" Style="{StaticResource RetroHeader}" Margin="0,0,0,16"/>
<Border Grid.Row="1" Background="#111" BorderBrush="#222" BorderThickness="1" Padding="12" Margin="0,0,0,12">
<DockPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="FILM-FILTER:" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<ComboBox x:Name="MovieFilter" Width="200" Height="30"
Background="#111" Foreground="#F5C518" FontFamily="Courier New"
SelectionChanged="FilterChanged"/>
<Button Content="[ ALLE ]" Style="{StaticResource RetroButtonGray}"
Margin="8,0,0,0" Height="30" Click="LoadAllClick"/>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="BtnAdd" Content="[ + NEUE VORFÜHRUNG ]" Style="{StaticResource RetroButtonGreen}"
Height="30" Click="AddClick"/>
<Button x:Name="BtnEdit" Content="[ ✎ BEARBEITEN ]" Style="{StaticResource RetroButtonGray}"
Margin="6,0,0,0" Height="30" Click="EditClick" IsEnabled="False"/>
<Button x:Name="BtnDelete" Content="[ ✕ LÖSCHEN ]" Style="{StaticResource RetroButtonRed}"
Margin="6,0,0,0" Height="30" Click="DeleteClick" IsEnabled="False"/>
<Button x:Name="BtnBookSeat" Content="[ 🎟 TICKET BUCHEN ]" Style="{StaticResource RetroButton}"
Margin="6,0,0,0" Height="30" Click="BookSeatClick" IsEnabled="False"/>
</StackPanel>
</DockPanel>
</Border>
<DataGrid x:Name="ScreeningsGrid" Grid.Row="2" Style="{StaticResource RetroDataGrid}"
SelectionChanged="GridSelectionChanged">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Foreground" Value="#F5C518"/>
<Setter Property="FontFamily" Value="Courier New"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="BorderBrush" Value="#333"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#CCC"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#1E1A00"/>
<Setter Property="Foreground" Value="#F5C518"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ScreeningID}" Width="50"/>
<DataGridTextColumn Header="FILM" Binding="{Binding MovieTitle}" Width="*"/>
<DataGridTextColumn Header="DATUM/ZEIT" Binding="{Binding DateTimeDisplay}" Width="160"/>
<DataGridTextColumn Header="SAAL" Binding="{Binding Hall}" Width="80"/>
<DataGridTextColumn Header="FREIE SITZE" Binding="{Binding SeatsDisplay}" Width="110"/>
<DataGridTextColumn Header="PREIS" Binding="{Binding PriceDisplay}" Width="90"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="StatusText" Grid.Row="3" Style="{StaticResource RetroLabel}"
Foreground="#666" Margin="0,8,0,0"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,158 @@
using CineBook.Data;
using CineBook.Helpers;
using CineBook.Models;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class ScreeningsView : UserControl
{
private readonly Repository _repo;
private Screening? _selected;
public ScreeningsView(Repository repo)
{
InitializeComponent();
_repo = repo;
if (!Session.IsAdmin)
{
BtnAdd.Visibility = Visibility.Collapsed;
BtnEdit.Visibility = Visibility.Collapsed;
BtnDelete.Visibility = Visibility.Collapsed;
}
LoadMovieFilter();
LoadScreenings();
}
private void LoadMovieFilter()
{
try
{
var movies = _repo.GetMovies(onlyActive: true);
MovieFilter.Items.Clear();
MovieFilter.Items.Add(new ComboBoxItem { Content = "— Alle Filme —", Tag = 0, Foreground = System.Windows.Media.Brushes.Gray, FontFamily = new System.Windows.Media.FontFamily("Courier New") });
foreach (var m in movies)
MovieFilter.Items.Add(new ComboBoxItem { Content = m.Title, Tag = m.MovieID, Foreground = System.Windows.Media.Brushes.Gold, FontFamily = new System.Windows.Media.FontFamily("Courier New") });
MovieFilter.SelectedIndex = 0;
}
catch { }
}
private void LoadScreenings()
{
try
{
var list = _repo.GetScreenings();
ScreeningsGrid.ItemsSource = list;
StatusText.Text = $"● {list.Count} Vorführungen geladen.";
}
catch (System.Exception ex) { StatusText.Text = "⚠ Fehler: " + ex.Message; }
}
private void FilterChanged(object sender, SelectionChangedEventArgs e)
{
if (MovieFilter.SelectedItem is ComboBoxItem item && item.Tag is int id && id > 0)
{
try { ScreeningsGrid.ItemsSource = _repo.GetScreeningsByMovie(id); }
catch (System.Exception ex) { StatusText.Text = "⚠ " + ex.Message; }
}
else LoadScreenings();
}
private void GridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selected = ScreeningsGrid.SelectedItem as Screening;
BtnDelete.IsEnabled = _selected != null && Session.IsAdmin;
BtnEdit.IsEnabled = _selected != null && Session.IsAdmin;
BtnBookSeat.IsEnabled = _selected != null && _selected.AvailableSeats > 0;
}
private void LoadAllClick(object sender, RoutedEventArgs e)
{
MovieFilter.SelectedIndex = 0;
LoadScreenings();
}
private void AddClick(object sender, RoutedEventArgs e)
{
var movies = _repo.GetMovies(onlyActive: true);
var dlg = new ScreeningEditDialog(movies);
if (dlg.ShowDialog() == true && dlg.Result != null)
{
try { _repo.CreateScreening(dlg.Result); LoadScreenings(); LoadMovieFilter(); }
catch (System.Exception ex) { MessageBox.Show("DB-Fehler: " + ex.Message); }
}
}
private void EditClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var movies = _repo.GetMovies(onlyActive: true);
var dlg = new ScreeningEditDialog(movies, _selected);
if (dlg.ShowDialog() == true && dlg.Result != null)
{
try
{
if (_repo.UpdateScreening(dlg.Result))
LoadScreenings();
else
MessageBox.Show("Fehler beim Speichern.", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (System.Exception ex)
{
MessageBox.Show("DB-Fehler: " + ex.Message);
}
}
}
private void DeleteClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var r = MessageBox.Show($"Vorführung löschen?", "Bestätigung", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (r == MessageBoxResult.Yes)
{
try { _repo.DeleteScreening(_selected.ScreeningID); LoadScreenings(); }
catch (System.Exception ex) { MessageBox.Show("DB-Fehler: " + ex.Message); }
}
}
private void BookSeatClick(object sender, RoutedEventArgs e)
{
if (_selected == null) return;
var seats = _repo.GetSeatsByScreening(_selected.ScreeningID);
if (seats.Count == 0)
{
if (Session.IsAdmin)
{
var r = MessageBox.Show(
"Für diese Vorführung existieren noch keine Sitzplätze.\nSoll jetzt automatisch ein Sitzplan erstellt werden?",
"Sitzplan fehlt",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (r == MessageBoxResult.Yes)
{
if (_repo.EnsureSeatsForScreening(_selected.ScreeningID, _selected.TotalSeats))
seats = _repo.GetSeatsByScreening(_selected.ScreeningID);
}
}
if (seats.Count == 0)
{
MessageBox.Show("Keine Sitzplatzdaten verfügbar.", "Hinweis");
return;
}
}
var dlg = new SeatSelectionDialog(_selected, seats, Session.CurrentUser!);
if (dlg.ShowDialog() == true)
{
try
{
_repo.CreateBooking(Session.CurrentUser!.UserID, dlg.SelectedScreeningId, dlg.SelectedSeatId, dlg.TotalPrice);
LoadScreenings();
MessageBox.Show($"✓ Buchung erfolgreich!\nSitz: {dlg.SeatDisplay}\nPreis: {dlg.TotalPrice:F2} €",
"Buchung bestätigt", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (System.Exception ex) { MessageBox.Show("DB-Fehler: " + ex.Message); }
}
}
}

View File

@@ -0,0 +1,54 @@
<Window x:Class="CineBook.Views.SeatSelectionDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Sitzplatz wählen" Height="500" Width="560"
WindowStartupLocation="CenterOwner" ResizeMode="NoResize"
Background="#0A0A0A">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="🎟 SITZPLATZ AUSWAHL" Style="{StaticResource RetroHeader}" Margin="0,0,0,8"/>
<Border Grid.Row="1" Background="#111" BorderBrush="#333" BorderThickness="1" Padding="12,8" Margin="0,0,0,16">
<StackPanel>
<TextBlock x:Name="MovieInfoText" FontFamily="Courier New" FontSize="13" Foreground="#F5C518"/>
<TextBlock x:Name="ScreeningInfoText" FontFamily="Courier New" FontSize="11" Foreground="#888" Margin="0,4,0,0"/>
</StackPanel>
</Border>
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="20" Height="20" Background="#27AE60" CornerRadius="2" Margin="0,0,4,0"/>
<TextBlock Text="Frei" FontFamily="Courier New" FontSize="11" Foreground="#888" Margin="0,0,16,0" VerticalAlignment="Center"/>
<Border Width="20" Height="20" Background="#C0392B" CornerRadius="2" Margin="0,0,4,0"/>
<TextBlock Text="Belegt" FontFamily="Courier New" FontSize="11" Foreground="#888" Margin="0,0,16,0" VerticalAlignment="Center"/>
<Border Width="20" Height="20" Background="#F5C518" CornerRadius="2" Margin="0,0,4,0"/>
<TextBlock Text="Ausgewählt" FontFamily="Courier New" FontSize="11" Foreground="#888" VerticalAlignment="Center"/>
</StackPanel>
<Border Background="#222" Height="8" CornerRadius="4" Margin="20,0,20,16">
<TextBlock Text="L E I N W A N D" FontFamily="Courier New" FontSize="8"
Foreground="#555" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ItemsControl x:Name="SeatsPanel"/>
</StackPanel>
</ScrollViewer>
<Border Grid.Row="3" Background="#111" BorderBrush="#333" BorderThickness="1" Padding="12,8" Margin="0,12,0,12">
<StackPanel Orientation="Horizontal">
<TextBlock Text="AUSGEWÄHLT: " FontFamily="Courier New" FontSize="12" Foreground="#888" VerticalAlignment="Center"/>
<TextBlock x:Name="SelectedSeatText" FontFamily="Courier New" FontSize="12" Foreground="#F5C518" VerticalAlignment="Center"/>
<TextBlock Text=" | PREIS: " FontFamily="Courier New" FontSize="12" Foreground="#888" VerticalAlignment="Center"/>
<TextBlock x:Name="PriceText" FontFamily="Courier New" FontSize="12" Foreground="#27AE60" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="[ ABBRECHEN ]" Style="{StaticResource RetroButtonGray}" Height="32"
Click="CancelClick" Margin="0,0,8,0"/>
<Button x:Name="BtnConfirm" Content="[ ✓ BUCHUNG BESTÄTIGEN ]" Style="{StaticResource RetroButtonGreen}"
Height="32" Click="ConfirmClick" IsEnabled="False"/>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,141 @@
using CineBook.Models;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace CineBook.Views;
public partial class SeatSelectionDialog : Window
{
private readonly Screening _screening;
private readonly List<Seat> _seats;
private Seat? _selectedSeat;
public int SelectedSeatId => _selectedSeat?.SeatID ?? 0;
public int SelectedScreeningId => _screening.ScreeningID;
public decimal TotalPrice => _screening.PricePerSeat;
public string SeatDisplay => _selectedSeat?.Display ?? "";
public SeatSelectionDialog(Screening screening, List<Seat> seats, User user)
{
InitializeComponent();
_screening = screening;
_seats = seats;
MovieInfoText.Text = screening.MovieTitle;
ScreeningInfoText.Text = $"{screening.DateTimeDisplay} | {screening.Hall} | {screening.PriceDisplay} pro Sitz";
BuildSeatGrid();
UpdateSelection();
}
private void BuildSeatGrid()
{
var rows = _seats.GroupBy(s => s.Row).OrderBy(g => g.Key);
var panel = new StackPanel();
foreach (var row in rows)
{
var rowPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 2, 0, 2)
};
rowPanel.Children.Add(new TextBlock
{
Text = row.Key,
Width = 24,
Foreground = Brushes.Gray,
FontFamily = new FontFamily("Courier New"),
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
});
foreach (var seat in row.OrderBy(s => s.SeatNumber))
{
var btn = new Button
{
Content = seat.SeatNumber.ToString(),
Width = 34,
Height = 30,
Margin = new Thickness(2),
FontFamily = new FontFamily("Courier New"),
FontSize = 11,
Tag = seat,
IsEnabled = !seat.IsBooked,
Background = seat.IsBooked
? new SolidColorBrush(Color.FromRgb(0x44, 0x22, 0x22))
: new SolidColorBrush(Color.FromRgb(0x1B, 0x4D, 0x2A)),
Foreground = seat.IsBooked ? Brushes.DarkRed : Brushes.LightGreen,
BorderBrush = seat.IsBooked
? new SolidColorBrush(Color.FromRgb(0x80, 0x20, 0x20))
: new SolidColorBrush(Color.FromRgb(0x27, 0xAE, 0x60)),
BorderThickness = new Thickness(1),
Cursor = seat.IsBooked
? System.Windows.Input.Cursors.No
: System.Windows.Input.Cursors.Hand
};
btn.ToolTip = $"Reihe {seat.Row}, Sitz {seat.SeatNumber} ({seat.Category})";
btn.Click += SeatClick;
rowPanel.Children.Add(btn);
}
panel.Children.Add(rowPanel);
}
SeatsPanel.ItemsSource = new[] { new ContentControl { Content = panel } };
}
private void SeatClick(object sender, RoutedEventArgs e)
{
if (sender is not Button btn || btn.Tag is not Seat seat) return;
_selectedSeat = seat;
UpdateSelection();
foreach (var child in FindVisualChildren<Button>(SeatsPanel))
{
if (child.Tag is Seat s && !s.IsBooked)
{
bool isSel = s.SeatID == seat.SeatID;
child.Background = isSel
? new SolidColorBrush(Color.FromRgb(0x7A, 0x62, 0x00))
: new SolidColorBrush(Color.FromRgb(0x1B, 0x4D, 0x2A));
child.Foreground = isSel ? Brushes.Gold : Brushes.LightGreen;
}
}
}
private void UpdateSelection()
{
SelectedSeatText.Text = _selectedSeat != null
? $"Reihe {_selectedSeat.Row}, Sitz {_selectedSeat.SeatNumber} ({_selectedSeat.Category})"
: "— kein Sitz gewählt —";
PriceText.Text = _selectedSeat != null ? $"{_screening.PricePerSeat:F2} €" : "";
BtnConfirm.IsEnabled = _selectedSeat != null;
}
private void ConfirmClick(object sender, RoutedEventArgs e)
{
if (_selectedSeat == null) return;
DialogResult = true;
Close();
}
private void CancelClick(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t) yield return t;
foreach (var sub in FindVisualChildren<T>(child))
yield return sub;
}
}
}

56
Views/UserEditDialog.xaml Normal file
View File

@@ -0,0 +1,56 @@
<Window x:Class="CineBook.Views.UserEditDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Benutzer bearbeiten" Height="340" Width="420"
WindowStartupLocation="CenterOwner" ResizeMode="NoResize"
Background="#0A0A0A">
<Grid Margin="24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="DialogTitle" Text="👤 BENUTZER BEARBEITEN"
Style="{StaticResource RetroHeader}" Margin="0,0,0,20"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="BENUTZERNAME *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<TextBox x:Name="UsernameBox" Grid.Column="1" Style="{StaticResource RetroTextBox}" Height="30" Margin="0,0,0,10"/>
<TextBlock Grid.Row="1" Text="PASSWORT" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<PasswordBox x:Name="PasswordBox" Grid.Row="1" Grid.Column="1" Style="{StaticResource RetroPasswordBox}"
Height="30" Margin="0,0,0,10"/>
<TextBlock Grid.Row="2" Text="ROLLE *" Style="{StaticResource RetroLabel}" VerticalAlignment="Center" Margin="0,0,0,10"/>
<ComboBox x:Name="RoleBox" Grid.Row="2" Grid.Column="1" Height="30" Margin="0,0,0,10"
Background="#111" Foreground="#F5C518" FontFamily="Courier New">
<ComboBoxItem>User</ComboBoxItem>
<ComboBoxItem>Admin</ComboBoxItem>
</ComboBox>
<TextBlock Grid.Row="3" Text="E-MAIL" Style="{StaticResource RetroLabel}" VerticalAlignment="Center"/>
<TextBox x:Name="EmailBox" Grid.Row="3" Grid.Column="1" Style="{StaticResource RetroTextBox}" Height="30"/>
</Grid>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,16,0,0">
<TextBlock x:Name="ErrorText" Foreground="#C0392B" FontFamily="Courier New" FontSize="11"
VerticalAlignment="Center" Margin="0,0,12,0"/>
<Button Content="[ ABBRECHEN ]" Style="{StaticResource RetroButtonGray}" Height="32"
Click="CancelClick" Margin="0,0,8,0"/>
<Button Content="[ SPEICHERN ]" Style="{StaticResource RetroButtonGreen}" Height="32"
Click="SaveClick"/>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,58 @@
using CineBook.Models;
using System.Windows;
using System.Windows.Controls;
namespace CineBook.Views;
public partial class UserEditDialog : Window
{
public string Username { get; private set; } = "";
public string Password { get; private set; } = "";
public string Role { get; private set; } = "User";
public string Email { get; private set; } = "";
private readonly User? _editing;
public UserEditDialog(User? user)
{
InitializeComponent();
_editing = user;
if (user != null)
{
DialogTitle.Text = "👤 BENUTZER BEARBEITEN";
UsernameBox.Text = user.Username;
EmailBox.Text = user.Email;
foreach (ComboBoxItem item in RoleBox.Items)
if (item.Content?.ToString() == user.Role)
RoleBox.SelectedItem = item;
}
else
{
DialogTitle.Text = "👤 BENUTZER ANLEGEN";
RoleBox.SelectedIndex = 0;
}
}
private void SaveClick(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(UsernameBox.Text) || UsernameBox.Text.Trim().Length < 3)
{ ErrorText.Text = "⚠ Benutzername mindestens 3 Zeichen!"; return; }
if (_editing == null && (string.IsNullOrEmpty(PasswordBox.Password) || PasswordBox.Password.Length < 6))
{ ErrorText.Text = "⚠ Passwort mindestens 6 Zeichen!"; return; }
if (RoleBox.SelectedItem is not ComboBoxItem ri)
{ ErrorText.Text = "⚠ Rolle auswählen!"; return; }
Username = UsernameBox.Text.Trim();
Password = PasswordBox.Password;
Role = ri.Content?.ToString() ?? "User";
Email = EmailBox.Text.Trim();
DialogResult = true;
Close();
}
private void CancelClick(object sender, RoutedEventArgs e) { DialogResult = false; Close(); }
}