pre-release-changes #14
@ -19,12 +19,6 @@
|
||||
username=""
|
||||
password=""
|
||||
|
||||
[seating]
|
||||
# SeatID -> Category
|
||||
A01 = "NORMAL"
|
||||
A02 = "NORMAL"
|
||||
C01 = "LUXUS"
|
||||
|
||||
[tickets]
|
||||
[tickets."NORMAL"]
|
||||
total_tickets=30
|
||||
|
||||
63
sql/catering_menu_items.sql
Normal file
63
sql/catering_menu_items.sql
Normal 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);
|
||||
@ -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",
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
141
src/ez_lan_manager/pages/OverviewPage.py
Normal file
141
src/ez_lan_manager/pages/OverviewPage.py
Normal 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()
|
||||
)
|
||||
@ -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(
|
||||
|
||||
@ -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))
|
||||
),
|
||||
|
||||
@ -19,3 +19,4 @@ from .ManageNewsPage import ManageNewsPage
|
||||
from .ManageUsersPage import ManageUsersPage
|
||||
from .ManageCateringPage import ManageCateringPage
|
||||
from .ManageTournamentsPage import ManageTournamentsPage
|
||||
from .OverviewPage import OverviewPage
|
||||
|
||||
@ -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} €"
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user