pre-release-changes #14

Merged
Typhus merged 17 commits from pre-release-changes into main 2025-03-25 23:07:43 +00:00
21 changed files with 490 additions and 164 deletions

View File

@ -1 +1 @@
0.0.1
0.1.0

View File

@ -19,12 +19,6 @@
username=""
password=""
[seating]
# SeatID -> Category
A01 = "NORMAL"
A02 = "NORMAL"
C01 = "LUXUS"
[tickets]
[tickets."NORMAL"]
total_tickets=30

View File

@ -0,0 +1,63 @@
INSERT INTO `catering_menu_items` VALUES
(7,'Schnitzel Wiener Art','mit Pommes',10.50,'MAIN_COURSE',0),
(8,'Jäger Schnitzel mit Champignonrahm Sauce','mit Pommes',11.50,'MAIN_COURSE',0),
(9,'Tortellini in Käsesauce mit Fleischfüllung','',10.50,'MAIN_COURSE',0),
(10,'Tortellini in Käsesauce ohne Fleischfüllung','Vegetarisch',10.50,'MAIN_COURSE',0),
(11,'Käse Schinken Wrap','',5.00,'SNACK',1),
(12,'Puten Paprika Wrap','',7.00,'SNACK',0),
(13,'Tomate Mozzarella Wrap','',6.00,'SNACK',0),
(14,'Portion Pommes','',4.00,'SNACK',0),
(15,'Rinds-Currywurst','',4.50,'SNACK',0),
(16,'Rinds-Currywurst mit Pommes','',6.50,'SNACK',0),
(17,'Nudelsalat','',4.50,'SNACK',0),
(18,'Nudelsalat mit Bockwurst','',6.00,'SNACK',0),
(19,'Kartoffelsalat','',4.50,'SNACK',0),
(20,'Kartoffelsalat mit Bockwurst','',6.00,'SNACK',0),
(21,'Sandwichtoast - Schinken','',1.80,'SNACK',0),
(22,'Sandwichtoast - Käse','',1.80,'SNACK',0),
(23,'Sandwichtoast - Schinken/Käse','',2.10,'SNACK',0),
(24,'Sandwichtoast - Salami','',1.80,'SNACK',0),
(25,'Sandwichtoast - Salami/Käse','',2.10,'SNACK',0),
(26,'Chips - Western Style','',1.30,'SNACK',0),
(27,'Nachos - Salted','',1.30,'SNACK',0),
(28,'Panna Cotta mit Erdbeersauce','',7.00,'DESSERT',0),
(29,'Panna Cotta mit Blaubeersauce','',7.00,'DESSERT',0),
(30,'Mousse au Chocolat','',7.00,'DESSERT',0),
(31,'Fruit Loops','',1.50,'BREAKFAST',0),
(32,'Smacks','',1.50,'BREAKFAST',0),
(33,'Knuspermüsli','Schoko',2.00,'BREAKFAST',0),
(34,'Cini Minis','',1.50,'BREAKFAST',0),
(35,'Brötchen - Schinken','mit Margarine',1.20,'BREAKFAST',0),
(36,'Brötchen - Käse','mit Margarine',1.20,'BREAKFAST',0),
(37,'Brötchen - Schinken/Käse','mit Margarine',1.40,'BREAKFAST',0),
(38,'Brötchen - Salami','mit Margarine',1.20,'BREAKFAST',0),
(39,'Brötchen - Salami/Käse','mit Margarine',1.40,'BREAKFAST',0),
(40,'Brötchen - Nutella','mit Margarine',1.20,'BREAKFAST',0),
(41,'Wasser - Still','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(42,'Wasser - Medium','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(43,'Wasser - Spritzig','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(44,'Coca-Cola','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(45,'Coca-Cola Zero','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(46,'Fanta','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(47,'Sprite','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(48,'Spezi','von Paulaner, 0,5L Flasche',1.50,'BEVERAGE_NON_ALCOHOLIC',0),
(49,'Red Bull','',2.00,'BEVERAGE_NON_ALCOHOLIC',0),
(50,'Energy','Hausmarke',1.50,'BEVERAGE_NON_ALCOHOLIC',0),
(51,'Pils','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',0),
(52,'Radler','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',0),
(53,'Diesel','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',0),
(54,'Apfelwein Pur','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',0),
(55,'Apfelwein Sauer','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',0),
(56,'Apfelwein Cola','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',0),
(57,'Vodka Energy','',4.00,'BEVERAGE_COCKTAIL',0),
(58,'Vodka O-Saft','',4.00,'BEVERAGE_COCKTAIL',0),
(59,'Whiskey Cola','mit Bourbon',4.00,'BEVERAGE_COCKTAIL',0),
(60,'Jägermeister Energy','',4.00,'BEVERAGE_COCKTAIL',0),
(61,'Sex on the Beach','',5.50,'BEVERAGE_COCKTAIL',0),
(62,'Long Island Ice Tea','',5.50,'BEVERAGE_COCKTAIL',0),
(63,'Caipirinha','',5.50,'BEVERAGE_COCKTAIL',0),
(64,'Jägermeister','',2.00,'BEVERAGE_SHOT',0),
(65,'Tequila','',2.00,'BEVERAGE_SHOT',0),
(66,'PfEZzi','Getunter Pfefferminz-Schnaps',1.99,'BEVERAGE_SHOT',0),
(67,'Zigaretten','Elixyr',8.00,'NON_FOOD',0),
(68,'Mentholfilter','passend für Elixyr',1.20,'NON_FOOD',0);

View File

@ -62,7 +62,7 @@ if __name__ == "__main__":
ComponentPage(
name="Overview",
url_segment="overview",
build=lambda: pages.PlaceholderPage(placeholder_name="LAN Übersicht"),
build=pages.OverviewPage,
),
ComponentPage(
name="BuyTicket",

View File

@ -25,7 +25,7 @@ def init_services() -> tuple[AccountingService, CateringService, ConfigurationSe
news_service = NewsService(db_service)
mailing_service = MailingService(configuration_service)
ticketing_service = TicketingService(configuration_service.get_ticket_info(), db_service, accounting_service)
seating_service = SeatingService(configuration_service.get_seating_configuration(), configuration_service.get_lan_info(), db_service, ticketing_service)
seating_service = SeatingService(configuration_service.get_lan_info(), db_service, ticketing_service)
catering_service = CateringService(db_service, accounting_service, user_service)
local_data_service = LocalDataService()

View File

@ -54,7 +54,7 @@ class DesktopNavigation(Component):
DesktopNavigationButton("FAQ", "./faq"),
DesktopNavigationButton("Regeln & AGB", "./rules-gtc"),
Spacer(min_height=1),
DesktopNavigationButton("Discord", "#", open_new_tab=True), # Temporarily disabled: https://discord.gg/8gTjg34yyH
DesktopNavigationButton("Discord", "https://discord.gg/8gTjg34yyH", open_new_tab=True),
DesktopNavigationButton("Die EZ GG e.V.", "https://ezgg-ev.de/about", open_new_tab=True),
DesktopNavigationButton("Kontakt", "./contact"),
DesktopNavigationButton("Impressum & DSGVO", "./imprint"),

View File

@ -1,21 +1,21 @@
from typing import Callable
from rio import Component, Rectangle, Grid, Column, Row, Text, TextStyle, Color
from rio import Component, Rectangle, Grid, Column, Row, Text, TextStyle, Color, PointerEventListener
from src.ez_lan_manager.components.SeatingPlanPixels import SeatPixel, WallPixel, InvisiblePixel, TextPixel
from src.ez_lan_manager.types.Seat import Seat
MAX_GRID_WIDTH_PIXELS = 34
MAX_GRID_HEIGHT_PIXELS = 45
MAX_GRID_WIDTH_PIXELS = 60
MAX_GRID_HEIGHT_PIXELS = 60
class SeatingPlanLegend(Component):
def build(self) -> Component:
return Column(
Text("Legende", style=TextStyle(fill=self.session.theme.neutral_color), justify="center", margin=1),
Row(
Text("L = Luxus Platz", justify="center", style=TextStyle(fill=self.session.theme.neutral_color)),
Text("N = Normaler Platz", justify="center", style=TextStyle(fill=self.session.theme.neutral_color)),
),
# Row( # Disabled for upcoming LAN
# Text("L = Luxus Platz", justify="center", style=TextStyle(fill=self.session.theme.neutral_color)),
# Text("N = Normaler Platz", justify="center", style=TextStyle(fill=self.session.theme.neutral_color)),
# ),
Row(
Rectangle(
content=Column(
@ -71,9 +71,11 @@ class SeatingPlanLegend(Component):
class SeatingPlan(Component):
seat_clicked_cb: Callable
seating_info: list[Seat]
info_clicked_cb: Callable
def get_seat(self, seat_id: str) -> Seat:
return next(filter(lambda seat: seat.seat_id == seat_id, self.seating_info))
seat = next(filter(lambda seat_: seat_.seat_id == seat_id, self.seating_info), None)
return seat if seat else Seat(seat_id="Z99", is_blocked=True, category="LUXUS", user=None)
"""
This seating plan is for the community center "Bottenhorn"
@ -81,106 +83,171 @@ class SeatingPlan(Component):
def build(self) -> Component:
grid = Grid()
# Outlines
for column_id in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(InvisiblePixel(), row=0, column=column_id)
for y in range(0, 13):
grid.add(WallPixel(), row=y, column=0)
for y in range(13, 19):
grid.add(InvisiblePixel(), row=y, column=0)
for y in range(19, MAX_GRID_HEIGHT_PIXELS):
for x in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=0, column=x)
for y in range(0, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=0)
for x in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=MAX_GRID_HEIGHT_PIXELS, column=x)
for x in range(0, 31):
grid.add(WallPixel(), row=15, column=x)
for x in range(41, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=32, column=x)
for x in range(31, 34):
grid.add(WallPixel(), row=32, column=x)
grid.add(WallPixel(), row=19, column=x)
for x in range(42, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=11, column=x)
grid.add(WallPixel(), row=22, column=x)
for x in range(22, 30):
grid.add(WallPixel(), row=5, column=x)
grid.add(WallPixel(), row=10, column=x)
for y in range(5, 11):
grid.add(WallPixel(), row=y, column=21)
grid.add(WallPixel(), row=y, column=30)
for y in range(40, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=30)
for y in range(32, 36):
grid.add(WallPixel(), row=y, column=30)
for y in range(19, 33):
grid.add(WallPixel(), row=y, column=34)
for y in range(16, 20):
grid.add(WallPixel(), row=y, column=30)
for y in range(0, 5):
grid.add(WallPixel(), row=y, column=41)
for y in range(9, 15):
grid.add(WallPixel(), row=y, column=41)
for y in range(19, 33):
grid.add(WallPixel(), row=y, column=41)
# Block A
block_a_margin_left = 12
block_a_margin_top = 1
(grid
.add(SeatPixel("A01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A01")), row=block_a_margin_top, column=block_a_margin_left, width=2, height=3)
.add(SeatPixel("A02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A02")), row=block_a_margin_top + 4, column=block_a_margin_left, width=2, height=3)
.add(SeatPixel("A03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A03")), row=block_a_margin_top + 8, column=block_a_margin_left, width=2, height=3)
.add(SeatPixel("A10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A10")), row=block_a_margin_top, column=block_a_margin_left + 3, width=2, height=3)
.add(SeatPixel("A11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A11")), row=block_a_margin_top + 4, column=block_a_margin_left + 3, width=2, height=3)
.add(SeatPixel("A12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A12")), row=block_a_margin_top + 8, column=block_a_margin_left + 3, width=2, height=3)
)
grid.add(SeatPixel("A01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A01")), row=57, column=2, width=3, height=2)
grid.add(SeatPixel("A02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A02")), row=57, column=5, width=3, height=2)
grid.add(SeatPixel("A03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A03")), row=57, column=8, width=3, height=2)
grid.add(SeatPixel("A04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A04")), row=57, column=11, width=3, height=2)
grid.add(SeatPixel("A05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A05")), row=57, column=14, width=3, height=2)
grid.add(SeatPixel("A06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A06")), row=57, column=17, width=3, height=2)
grid.add(SeatPixel("A10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A10")), row=55, column=2, width=3, height=2)
grid.add(SeatPixel("A11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A11")), row=55, column=5, width=3, height=2)
grid.add(SeatPixel("A12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A12")), row=55, column=8, width=3, height=2)
grid.add(SeatPixel("A13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A13")), row=55, column=11, width=3, height=2)
grid.add(SeatPixel("A14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A14")), row=55, column=14, width=3, height=2)
grid.add(SeatPixel("A15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A15")), row=55, column=17, width=3, height=2)
# Block B
block_b_margin_left = 20
block_b_margin_top = 1
(grid
.add(SeatPixel("B01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B01")), row=block_b_margin_top, column=block_b_margin_left, width=2, height=3)
.add(SeatPixel("B02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B02")), row=block_b_margin_top + 4, column=block_b_margin_left, width=2, height=3)
.add(SeatPixel("B03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B03")), row=block_b_margin_top + 8, column=block_b_margin_left, width=2, height=3)
.add(SeatPixel("B10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B10")), row=block_b_margin_top, column=block_b_margin_left + 3, width=2, height=3)
.add(SeatPixel("B11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B11")), row=block_b_margin_top + 4, column=block_b_margin_left + 3, width=2, height=3)
.add(SeatPixel("B12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B12")), row=block_b_margin_top + 8, column=block_b_margin_left + 3, width=2, height=3)
)
grid.add(SeatPixel("B01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B01")), row=50, column=2, width=3, height=2)
grid.add(SeatPixel("B02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B02")), row=50, column=5, width=3, height=2)
grid.add(SeatPixel("B03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B03")), row=50, column=8, width=3, height=2)
grid.add(SeatPixel("B04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B04")), row=50, column=11, width=3, height=2)
grid.add(SeatPixel("B05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B05")), row=50, column=14, width=3, height=2)
grid.add(SeatPixel("B10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B10")), row=48, column=2, width=3, height=2)
grid.add(SeatPixel("B11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B11")), row=48, column=5, width=3, height=2)
grid.add(SeatPixel("B12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B12")), row=48, column=8, width=3, height=2)
grid.add(SeatPixel("B13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B13")), row=48, column=11, width=3, height=2)
grid.add(SeatPixel("B14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B14")), row=48, column=14, width=3, height=2)
# Block C
block_c_margin_left = 28
block_c_margin_top = 1
(grid
.add(SeatPixel("C01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C01")), row=block_c_margin_top, column=block_c_margin_left, width=2, height=3)
.add(SeatPixel("C02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C02")), row=block_c_margin_top + 4, column=block_c_margin_left, width=2, height=3)
.add(SeatPixel("C03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C03")), row=block_c_margin_top + 8, column=block_c_margin_left, width=2, height=3)
.add(SeatPixel("C10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C10")), row=block_c_margin_top, column=block_c_margin_left + 3, width=2, height=3)
.add(SeatPixel("C11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C11")), row=block_c_margin_top + 4, column=block_c_margin_left + 3, width=2, height=3)
.add(SeatPixel("C12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C12")), row=block_c_margin_top + 8, column=block_c_margin_left + 3, width=2, height=3)
)
grid.add(SeatPixel("C01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C01")), row=43, column=2, width=3, height=2)
grid.add(SeatPixel("C02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C02")), row=43, column=5, width=3, height=2)
grid.add(SeatPixel("C03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C03")), row=43, column=8, width=3, height=2)
grid.add(SeatPixel("C04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C04")), row=43, column=11, width=3, height=2)
grid.add(SeatPixel("C05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C05")), row=43, column=14, width=3, height=2)
grid.add(SeatPixel("C10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C10")), row=41, column=2, width=3, height=2)
grid.add(SeatPixel("C11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C11")), row=41, column=5, width=3, height=2)
grid.add(SeatPixel("C12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C12")), row=41, column=8, width=3, height=2)
grid.add(SeatPixel("C13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C13")), row=41, column=11, width=3, height=2)
grid.add(SeatPixel("C14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C14")), row=41, column=14, width=3, height=2)
# Block D
block_d_margin_left = 20
block_d_margin_top = 20
(grid
.add(SeatPixel("D01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D01")), row=block_d_margin_top, column=block_d_margin_left, width=2, height=3)
.add(SeatPixel("D02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D02")), row=block_d_margin_top + 4, column=block_d_margin_left, width=2, height=3)
.add(SeatPixel("D03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D03")), row=block_d_margin_top + 8, column=block_d_margin_left, width=2, height=3)
.add(SeatPixel("D10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D10")), row=block_d_margin_top, column=block_d_margin_left + 3, width=2, height=3)
.add(SeatPixel("D11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D11")), row=block_d_margin_top + 4, column=block_d_margin_left + 3, width=2, height=3)
.add(SeatPixel("D12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D12")), row=block_d_margin_top + 8, column=block_d_margin_left + 3, width=2, height=3)
)
grid.add(SeatPixel("D01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D01")), row=34, column=2, width=3, height=2)
grid.add(SeatPixel("D02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D02")), row=34, column=5, width=3, height=2)
grid.add(SeatPixel("D03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D03")), row=34, column=8, width=3, height=2)
grid.add(SeatPixel("D04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D04")), row=34, column=11, width=3, height=2)
grid.add(SeatPixel("D05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D05")), row=34, column=14, width=3, height=2)
grid.add(SeatPixel("D06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D06")), row=34, column=17, width=3, height=2)
grid.add(SeatPixel("D10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D10")), row=32, column=2, width=3, height=2)
grid.add(SeatPixel("D11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D11")), row=32, column=5, width=3, height=2)
grid.add(SeatPixel("D12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D12")), row=32, column=8, width=3, height=2)
grid.add(SeatPixel("D13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D13")), row=32, column=11, width=3, height=2)
grid.add(SeatPixel("D14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D14")), row=32, column=14, width=3, height=2)
grid.add(SeatPixel("D15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D15")), row=32, column=17, width=3, height=2)
# Block E
block_e_margin_left = 28
block_e_margin_top = 20
(grid
.add(SeatPixel("E01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E01")), row=block_e_margin_top, column=block_e_margin_left, width=2, height=3)
.add(SeatPixel("E02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E02")), row=block_e_margin_top + 4, column=block_e_margin_left, width=2, height=3)
.add(SeatPixel("E03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E03")), row=block_e_margin_top + 8, column=block_e_margin_left, width=2, height=3)
.add(SeatPixel("E10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E10")), row=block_e_margin_top, column=block_e_margin_left + 3, width=2, height=3)
.add(SeatPixel("E11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E11")), row=block_e_margin_top + 4, column=block_e_margin_left + 3, width=2, height=3)
.add(SeatPixel("E12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E12")), row=block_e_margin_top + 8, column=block_e_margin_left + 3, width=2, height=3)
)
grid.add(SeatPixel("E01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E01")), row=27, column=2, width=3, height=2)
grid.add(SeatPixel("E02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E02")), row=27, column=5, width=3, height=2)
grid.add(SeatPixel("E03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E03")), row=27, column=8, width=3, height=2)
grid.add(SeatPixel("E04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E04")), row=27, column=11, width=3, height=2)
grid.add(SeatPixel("E05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E05")), row=27, column=14, width=3, height=2)
grid.add(SeatPixel("E06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E06")), row=27, column=17, width=3, height=2)
# Middle Wall
for y in range(0, 13):
grid.add(WallPixel(), row=y, column=10)
for y in range(19, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=10)
grid.add(SeatPixel("E10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E10")), row=25, column=2, width=3, height=2)
grid.add(SeatPixel("E11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E11")), row=25, column=5, width=3, height=2)
grid.add(SeatPixel("E12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E12")), row=25, column=8, width=3, height=2)
grid.add(SeatPixel("E13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E13")), row=25, column=11, width=3, height=2)
grid.add(SeatPixel("E14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E14")), row=25, column=14, width=3, height=2)
grid.add(SeatPixel("E15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E15")), row=25, column=17, width=3, height=2)
# Stage
for x in range(11, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=35, column=x)
grid.add(TextPixel(text="Bühne"), row=36, column=11, width=24, height=9)
grid.add(PointerEventListener(
TextPixel(text="Bühne"),
on_press=lambda _: self.info_clicked_cb("Hier darf ab Freitag 20 Uhr ebenfalls geschlafen werden.")
), row=16, column=1, width=29, height=4)
# Drinks
grid.add(TextPixel(text="G\ne\nt\nr\nä\nn\nk\ne"), row=21, column=11, width=3, height=11)
grid.add(PointerEventListener(
TextPixel(text="G\ne\nt\nr\nä\nn\nk\ne"),
on_press=lambda _: self.info_clicked_cb("Ich mag Bier, B - I - R")
), row=20, column=30, width=4, height=12)
# Main Entrance
grid.add(PointerEventListener(
TextPixel(text="H\na\nl\nl\ne\nn\n\ne\ni\nn\ng\na\nn\ng"),
on_press=lambda _: self.info_clicked_cb("Hallo, ich bin ein Haupteingang")
), row=33, column=56, width=4, height=27)
# Sleeping
grid.add(TextPixel(icon_name="material/bed"), row=1, column=1, width=4, height=11)
grid.add(PointerEventListener(
TextPixel(icon_name="material/bed"),
on_press=lambda _: self.info_clicked_cb("In diesem Raum kann geschlafen werden.\nAchtung: Hier werden nicht alle Teilnehmer Platz finden.")
), row=1, column=1, width=20, height=14)
# Toilet
grid.add(TextPixel(icon_name="material/floor", no_outline=True), row=1, column=7, width=3, height=2)
grid.add(TextPixel(icon_name="material/north", no_outline=True), row=3, column=7, width=3, height=2)
grid.add(TextPixel(icon_name="material/wc"), row=5, column=7, width=3, height=2)
grid.add(PointerEventListener(
TextPixel(icon_name="material/wc"),
on_press=lambda _: self.info_clicked_cb("Damen Toilette")
), row=1, column=42, width=19, height=10)
grid.add(PointerEventListener(
TextPixel(icon_name="material/wc"),
on_press=lambda _: self.info_clicked_cb("Herren Toilette")
), row=12, column=42, width=19, height=10)
# Entry/Helpdesk
grid.add(TextPixel(text="Einlass\n &Orga"), row=19, column=3, width=7, height=5)
grid.add(PointerEventListener(
TextPixel(text="Einlass\n &Orga"),
on_press=lambda _: self.info_clicked_cb("Für alle Anliegen findest du hier rund um die Uhr jemanden vom Team.")
), row=40, column=22, width=8, height=12)
# Wall below Entry/Helpdesk
for y in range(24, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=3)
# Entry Arrow
grid.add(TextPixel(icon_name="material/east", no_outline=True), row=15, column=1, width=2, height=2)
return Rectangle(
content=grid,

View File

@ -1,7 +1,11 @@
from decimal import Decimal
from functools import partial
from typing import Optional, Callable
from rio import Component, Column, Text, TextStyle, Button, Spacer
from rio import Component, Column, Text, TextStyle, Button, Spacer, event
from src.ez_lan_manager import TicketingService
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class SeatingPlanInfoBox(Component):
@ -12,8 +16,30 @@ class SeatingPlanInfoBox(Component):
seat_occupant: Optional[str] = None
seat_price: Decimal = Decimal("0")
is_blocked: bool = False
has_user_ticket: bool = False
booking_button_text: str = ""
override_text: str = "" # If this is set, all other functionality is disabled and the text is shown
@event.on_populate
async def check_ticket(self) -> None:
if self.session[SessionStorage].user_id:
user_ticket = await self.session[TicketingService].get_user_ticket(self.session[SessionStorage].user_id)
self.has_user_ticket = not (user_ticket is None)
self.booking_button_text = "Buchen" if self.has_user_ticket else "Ticket kaufen"
self.force_refresh()
async def purchase_clicked(self):
if self.has_user_ticket:
await self.purchase_cb()
else:
self.session.navigate_to("./buy_ticket")
def build(self) -> Component:
if self.override_text:
return Column(Text(self.override_text, margin=1,
style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap",
justify="center"), min_height=10)
if not self.show:
return Spacer()
if self.is_blocked:
@ -36,7 +62,7 @@ class SeatingPlanInfoBox(Component):
style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"),
Button(
Text(
f"Buchen",
text=self.booking_button_text,
margin=1,
style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.1),
overflow="wrap",
@ -48,7 +74,10 @@ class SeatingPlanInfoBox(Component):
margin=1,
grow_y=False,
is_sensitive=not self.is_booking_blocked,
on_press=self.purchase_cb
),
on_press=self.purchase_clicked
) if self.session[SessionStorage].user_id else Text(f"Du musst eingeloggt sein um einen Sitzplatz zu buchen",
margin=1,
style=TextStyle(fill=self.session.theme.neutral_color),
overflow="wrap", justify="center"),
min_height=10
)

View File

@ -1,6 +1,6 @@
from functools import partial
from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, PointerEventListener, Column
from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, PointerEventListener, Column, Row
from typing import Optional, Callable
from src.ez_lan_manager.types.Seat import Seat
@ -22,13 +22,13 @@ class SeatPixel(Component):
def build(self) -> Component:
return PointerEventListener(
content=Rectangle(
content=Column(
Text(f"{self.seat_id}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"{self.seat.category[0]}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5, selectable=False, overflow="wrap")
content=Row(
Text(f"{self.seat_id}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5, selectable=False)
),
min_width=1,
min_height=1,
fill=self.determine_color(),
stroke_width = 0.1,
hover_stroke_width = 0.1,
grow_x=True,
grow_y=True,

View File

@ -2,7 +2,7 @@ from asyncio import sleep, create_task
from decimal import Decimal
import rio
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table, event
from src.ez_lan_manager.components.CateringCartItem import CateringCartItem
from src.ez_lan_manager.components.CateringOrderItem import CateringOrderItem
@ -21,6 +21,11 @@ class ShoppingCartAndOrders(Component):
popup_is_shown: bool = False
popup_is_error: bool = True
@event.periodic(5)
async def periodic_refresh_of_orders(self) -> None:
if not self.show_cart and not self.popup_is_shown:
self.orders = await self.session[CateringService].get_orders_for_user(self.session[SessionStorage].user_id)
async def switch(self) -> None:
self.show_cart = not self.show_cart
self.orders = await self.session[CateringService].get_orders_for_user(self.session[SessionStorage].user_id)
@ -85,7 +90,8 @@ class ShoppingCartAndOrders(Component):
show_popup_task = create_task(self.show_popup("Guthaben nicht ausreichend", True))
else:
show_popup_task = create_task(self.show_popup("Unbekannter Fehler", True))
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
else:
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
self.order_button_loading = False
if not show_popup_task:
show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))
@ -124,7 +130,7 @@ class ShoppingCartAndOrders(Component):
dialog = await self.session.show_custom_dialog(
build=build_dialog_content,
modal=True,
user_closeable=True,
user_closable=True,
)
await dialog.wait_for_close()

View File

@ -73,6 +73,8 @@ class UserInfoBox(Component):
async def update(self) -> None:
if not self.user:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
if not self.user:
return
self.user_balance = await self.session[AccountingService].get_balance(self.user.user_id)
self.user_ticket = await self.session[TicketingService].get_user_ticket(self.user.user_id)
self.user_seat = await self.session[SeatingService].get_user_seat(self.user.user_id)

View File

@ -1,3 +1,4 @@
from decimal import Decimal
from functools import partial
from typing import Optional
@ -12,7 +13,7 @@ from src.ez_lan_manager.types.User import User
class AccountPage(Component):
user: Optional[User] = None
balance: Optional[int] = None
balance: Optional[Decimal] = None
transaction_history: list[Transaction] = list()
banking_info_revealer_open: bool = False
paypal_info_revealer_open: bool = False

View File

@ -10,6 +10,8 @@ from src.ez_lan_manager.components.DesktopNavigation import DesktopNavigation
class BasePage(Component):
color = "secondary"
corner_radius = (0, 0.5, 0, 0)
footer_size = 53.1
@event.periodic(60)
async def check_db_conn(self) -> None:
is_healthy = await self.session[DatabaseService].is_healthy()
@ -20,6 +22,16 @@ class BasePage(Component):
async def on_window_size_change(self):
self.force_refresh()
@event.on_page_change
def check_needed_size(self):
# ToDo: Low-Prio: Change layout, so the footer is always as wide as needed.
# This is a workaround, bc the seating page needs more width
if "/seating" in self.session.active_page_url.__str__():
self.footer_size = 78.2
else:
self.footer_size = 53.1
self.force_refresh()
def build(self) -> Component:
content = Card(
PageView(),
@ -47,7 +59,7 @@ class BasePage(Component):
grow_x=False,
grow_y=False,
min_height=1.2,
min_width=53.1,
min_width=self.footer_size,
margin_bottom=3
),
Spacer(grow_x=True, grow_y=False),

View File

@ -4,7 +4,6 @@ from typing import Optional
from rio import Text, Column, TextStyle, Component, event, TextInput, MultiLineTextInput, Row, Button
from src.ez_lan_manager import ConfigurationService, UserService, MailingService
from src.ez_lan_manager.components.AnimatedText import AnimatedText
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
@ -14,9 +13,15 @@ class ContactPage(Component):
# Workaround: Can not reassign this value without rio triggering refresh
# Using list to bypass this behavior
last_message_sent: list[datetime] = [datetime(day=1, month=1, year=2000)]
display_printing: list[bool] = [False]
user: Optional[User] = None
e_mail: str = ""
subject: str = ""
message: str = ""
submit_button_is_loading: bool = False
response_message: str = ""
is_success: bool = True
@event.on_populate
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Kontakt")
@ -24,73 +29,67 @@ class ContactPage(Component):
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
else:
self.user = None
self.e_mail = "" if not self.user else self.user.user_mail
async def on_send_pressed(self) -> None:
error_msg = ""
self.submit_button.is_loading = True
self.submit_button.force_refresh()
self.submit_button_is_loading = True
now = datetime.now()
if not self.email_input.text:
if not self.e_mail:
error_msg = "E-Mail darf nicht leer sein!"
elif not self.subject_input.text:
elif not self.subject:
error_msg = "Betreff darf nicht leer sein!"
elif not self.message_input.text:
elif not self.message:
error_msg = "Nachricht darf nicht leer sein!"
elif (now - self.last_message_sent[0]) < timedelta(minutes=1):
error_msg = "Immer mit der Ruhe!"
if error_msg:
self.submit_button.is_loading = False
await self.animated_text.display_text(False, error_msg)
self.submit_button_is_loading = False
self.is_success = False
self.response_message = error_msg
return
mail_recipient = self.session[ConfigurationService].get_lan_info().organizer_mail
msg = (f"Kontaktformular vom {now.strftime('%d.%m.%Y %H:%M')}:\n\n"
f"Betreff: {self.subject_input.text}\n"
f"Absender: {self.email_input.text}\n\n"
f"Betreff: {self.subject}\n"
f"Absender: {self.e_mail}\n\n"
f"Inhalt:\n"
f"{self.message_input.text}\n")
f"{self.message}\n")
await self.session[MailingService].send_email("Kontaktformular-Mitteilung", msg, mail_recipient)
self.last_message_sent[0] = datetime.now()
self.submit_button.is_loading = False
await self.animated_text.display_text(True, "Nachricht erfolgreich gesendet!")
self.submit_button_is_loading = False
self.is_success = True
self.response_message = "Nachricht erfolgreich gesendet!"
def build(self) -> Component:
self.animated_text = AnimatedText(
margin_top=2,
margin_bottom=1,
align_x=0.1
)
self.email_input = TextInput(
email_input = TextInput(
label="E-Mail Adresse",
text="" if not self.user else self.user.user_mail,
text=self.bind().e_mail,
margin_left=1,
margin_right=1,
margin_bottom=1,
grow_x=True
)
self.subject_input = TextInput(
subject_input = TextInput(
label="Betreff",
text="",
text=self.bind().subject,
margin_left=1,
margin_right=1,
margin_bottom=1,
grow_x=True
)
self.message_input = MultiLineTextInput(
message_input = MultiLineTextInput(
label="Deine Nachricht an uns",
text="",
text=self.bind().message,
margin_left=1,
margin_right=1,
margin_bottom=1,
min_height=5
)
self.submit_button = Button(
submit_button = Button(
content=Text(
"Absenden",
style=TextStyle(fill=self.session.theme.success_color, font_size=0.9),
@ -102,7 +101,8 @@ class ContactPage(Component):
shape="rectangle",
style="major",
color="primary",
on_press=self.on_send_pressed
on_press=self.on_send_pressed,
is_loading=self.bind().submit_button_is_loading
)
return Column(
MainViewContentBox(
@ -117,12 +117,21 @@ class ContactPage(Component):
margin_bottom=1,
align_x=0.5
),
self.email_input,
self.subject_input,
self.message_input,
email_input,
subject_input,
message_input,
Row(
self.animated_text,
self.submit_button,
Text(
text=self.bind().response_message,
style=TextStyle(
fill=self.session.theme.success_color if self.is_success else self.session.theme.danger_color,
font_size=0.9
),
margin_top=2,
margin_bottom=1,
align_x=0.1
),
submit_button,
)
)
),

View File

@ -0,0 +1,141 @@
from rio import Column, Component, event, Text, Spacer, Row, Link
from src.ez_lan_manager import ConfigurationService, TicketingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class OverviewPage(Component):
@event.on_populate
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Übersicht")
def build(self) -> Component:
lan_info = self.session[ConfigurationService].get_lan_info()
return Column(
MainViewContentBox(
Column(
Text(lan_info.name, font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5),
Text(f"Edition {lan_info.iteration}", font_size=0.9, justify="center", fill=self.session.theme.neutral_color, margin_bottom=1.5)
)
),
MainViewContentBox(
Column(
Text("Allgemeines", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Wann?", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"{lan_info.date_from.strftime("%d.%m.")} bis {lan_info.date_till.strftime("%d.%m.%Y")}", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Wo?", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Link(Text(f"DGH Donsbach", fill=self.session.theme.secondary_color, margin_right=1), target_url="https://maps.app.goo.gl/3Zyue776A22jdoxz5", open_in_new_tab=True),
margin_bottom=0.3
),
Row(
Text("Einlass", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(lan_info.date_from.strftime("Freitag %H:%M Uhr"), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Ende", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(lan_info.date_till.strftime("Sonntag %H:%M Uhr"), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Anmeldung", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text("Geöffnet", fill=self.session.theme.success_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Teilnehmer", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(str(self.session[TicketingService].get_total_tickets()), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
)
,
Row(
Text("Ticket Preise", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Link(Text(f"Preisliste", fill=self.session.theme.secondary_color, margin_right=1), target_url="./buy_ticket", open_in_new_tab=False),
margin_bottom=0.3
)
)
)
),
MainViewContentBox(
Column(
Text("Technisches", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Internet", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"60/20 Mbit/s (down/up)", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Routing", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"Flaches Netz", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("WLAN", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"vorhanden", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
)
)
)
),
MainViewContentBox(
Column(
Text("Sonstiges", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Schlafen", fill=self.session.theme.neutral_color, margin_left=1, justify="center"),
margin_bottom=0.3
),
Row(
Text("Es steht ein Schlafsaal zur Verfügung. Nach der Eröffnung steht auch die Bühne als Schlafbereich zur Verfügung.", font_size=0.7,
fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3
),
Row(
Text("Essen & Trinken", fill=self.session.theme.neutral_color, margin_left=1, justify="center"),
margin_bottom=0.3
),
Row(
Text("Wir sorgen für euer leibliches Wohl, ihr dürft aber auch eure eigenen Speißen und Getränke mitbringen.", font_size=0.7, fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3
),
Row(
Text("Parken", fill=self.session.theme.neutral_color, margin_left=1, justify="center"),
margin_bottom=0.3
),
Row(
Text("Vor der Halle sind ausreichend Parkplätze vorhanden.", font_size=0.7, fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3
)
)
)
),
MainViewContentBox(
Column(
Text("Turniere & Ablauf", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Zum aktuellen Zeitpunkt steht noch nicht fest welche Turniere gespielt werden. Wir planen diverse Online- und Offline Turniere mit Preisen durchzuführen. Weitere Informationen gibt es, sobald sie kommen, auf der NEWS- und Turnier-Seite.", font_size=0.7,
fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3
)
)
)
),
Spacer()
)

View File

@ -13,7 +13,7 @@ logger = logging.getLogger(__name__.split(".")[-1])
class RegisterPage(Component):
def on_pw_change(self, _: TextInputChangeEvent) -> None:
def on_pw_focus_loss(self, _: TextInputChangeEvent) -> None:
if not (self.pw_1.text == self.pw_2.text) or len(self.pw_1.text) < MINIMUM_PASSWORD_LENGTH:
self.pw_1.is_valid = False
self.pw_2.is_valid = False
@ -21,14 +21,14 @@ class RegisterPage(Component):
self.pw_1.is_valid = True
self.pw_2.is_valid = True
def on_email_changed(self, change_event: TextInputChangeEvent) -> None:
def on_email_focus_loss(self, change_event: TextInputChangeEvent) -> None:
try:
validate_email(change_event.text, check_deliverability=False)
self.email_input.is_valid = True
except EmailNotValidError:
self.email_input.is_valid = False
def on_user_name_input_change(self, _: TextInputChangeEvent) -> None:
def on_user_name_focus_loss(self, _: TextInputChangeEvent) -> None:
current_text = self.user_name_input.text
if len(current_text) > UserService.MAX_USERNAME_LENGTH:
self.user_name_input.text = current_text[:UserService.MAX_USERNAME_LENGTH]
@ -100,7 +100,7 @@ class RegisterPage(Component):
margin_right=1,
margin_bottom=1,
grow_x=True,
on_change=self.on_user_name_input_change
on_lose_focus=self.on_user_name_focus_loss
)
self.email_input = TextInput(
label="E-Mail Adresse",
@ -109,7 +109,7 @@ class RegisterPage(Component):
margin_right=1,
margin_bottom=1,
grow_x=True,
on_change=self.on_email_changed
on_lose_focus=self.on_email_focus_loss
)
self.pw_1 = TextInput(
label="Passwort",
@ -119,7 +119,7 @@ class RegisterPage(Component):
margin_bottom=1,
grow_x=True,
is_secret=True,
on_change=self.on_pw_change
on_lose_focus=self.on_pw_focus_loss
)
self.pw_2 = TextInput(
label="Passwort wiederholen",
@ -129,7 +129,7 @@ class RegisterPage(Component):
margin_bottom=1,
grow_x=True,
is_secret=True,
on_change=self.on_pw_change
on_lose_focus=self.on_pw_focus_loss
)
self.submit_button = Button(
content=Text(

View File

@ -30,6 +30,7 @@ class SeatingPlanPage(Component):
purchase_box_loading: bool = False
purchase_box_success_msg: Optional[str] = None
purchase_box_error_msg: Optional[str] = None
seating_info_text = ""
is_booking_blocked: bool = False
@event.on_populate
@ -47,6 +48,7 @@ class SeatingPlanPage(Component):
self.is_booking_blocked = True
async def on_seat_clicked(self, seat_id: str, _: PressEvent) -> None:
self.seating_info_text = ""
self.show_info_box = True
self.show_purchase_box = False
seat = next(filter(lambda s: s.seat_id == seat_id, self.seating_info), None)
@ -62,6 +64,12 @@ class SeatingPlanPage(Component):
else:
self.current_seat_occupant = None
async def on_info_clicked(self, text: str) -> None:
self.show_info_box = True
self.show_purchase_box = False
self.current_seat_id = None
self.seating_info_text = text
def set_error(self, msg: str) -> None:
self.purchase_box_error_msg = msg
self.purchase_box_success_msg = None
@ -119,7 +127,7 @@ class SeatingPlanPage(Component):
Column(
SeatingPlanInfoBox(seat_id=self.current_seat_id, seat_occupant=self.current_seat_occupant, seat_price=self.current_seat_price,
is_blocked=self.current_seat_is_blocked, is_booking_blocked=self.is_booking_blocked, show=self.show_info_box,
purchase_cb=self.on_purchase_clicked),
purchase_cb=self.on_purchase_clicked, override_text=self.seating_info_text),
SeatingPurchaseBox(
show=self.show_purchase_box,
seat_id=self.current_seat_id,
@ -132,7 +140,7 @@ class SeatingPlanPage(Component):
)
),
MainViewContentBox(
SeatingPlan(seat_clicked_cb=self.on_seat_clicked, seating_info=self.seating_info) if self.seating_info else
SeatingPlan(seat_clicked_cb=self.on_seat_clicked, seating_info=self.seating_info, info_clicked_cb=self.on_info_clicked) if self.seating_info else
Column(ProgressCircle(color=self.session.theme.secondary_color, margin=3),
Text("Sitzplan wird geladen", style=TextStyle(fill=self.session.theme.neutral_color), align_x=0.5, margin=1))
),

View File

@ -19,3 +19,4 @@ from .ManageNewsPage import ManageNewsPage
from .ManageUsersPage import ManageUsersPage
from .ManageCateringPage import ManageCateringPage
from .ManageTournamentsPage import ManageTournamentsPage
from .OverviewPage import OverviewPage

View File

@ -2,6 +2,7 @@ import logging
from collections.abc import Callable
from datetime import datetime
from decimal import Decimal, ROUND_DOWN
from typing import Optional
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.Transaction import Transaction
@ -65,9 +66,11 @@ class AccountingService:
return await self._db_service.get_all_transactions_for_user(user_id)
@staticmethod
def make_euro_string_from_decimal(euros: Decimal) -> str:
def make_euro_string_from_decimal(euros: Optional[Decimal]) -> str:
"""
Internally, all money values are euros as decimal. Only when showing them to the user we generate a string.
"""
if euros is None:
return "0.00 €"
rounded_decimal = str(euros.quantize(Decimal(".01"), rounding=ROUND_DOWN))
return f"{rounded_decimal}"

View File

@ -71,15 +71,6 @@ class ConfigurationService:
logger.fatal("Error loading LAN Info, exiting...")
sys.exit(1)
def get_seating_configuration(self) -> SeatingConfiguration:
try:
return SeatingConfiguration(
seats=self._config["seating"]
)
except KeyError:
logger.fatal("Error loading seating configuration, exiting...")
sys.exit(1)
def get_ticket_info(self) -> tuple[TicketInfo, ...]:
try:
return tuple([TicketInfo(

View File

@ -22,8 +22,7 @@ class SeatAlreadyTakenError(Exception):
pass
class SeatingService:
def __init__(self, seating_configuration: SeatingConfiguration, lan_info: LanInfo, db_service: DatabaseService, ticketing_service: TicketingService) -> None:
self._seating_configuration = seating_configuration
def __init__(self, lan_info: LanInfo, db_service: DatabaseService, ticketing_service: TicketingService) -> None:
self._lan_info = lan_info
self._db_service = db_service
self._ticketing_service = ticketing_service