diff --git a/src/ez_lan_manager/assets/img/anon_pfp.png b/src/ez_lan_manager/assets/img/anon_pfp.png index e69de29..265d8b5 100644 Binary files a/src/ez_lan_manager/assets/img/anon_pfp.png and b/src/ez_lan_manager/assets/img/anon_pfp.png differ diff --git a/src/ez_lan_manager/components/SeatingPlan.py b/src/ez_lan_manager/components/SeatingPlan.py index 54fe842..c194ce4 100644 --- a/src/ez_lan_manager/components/SeatingPlan.py +++ b/src/ez_lan_manager/components/SeatingPlan.py @@ -1,12 +1,21 @@ +from typing import Callable + from rio import Component, Rectangle, Grid 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 class SeatingPlan(Component): + seat_clicked_cb: Callable + seating_info: list[Seat] + + def get_seat(self, seat_id: str) -> Seat: + return next(filter(lambda seat: seat.seat_id == seat_id, self.seating_info)) + """ This seating plan is for the community center "Bottenhorn" """ @@ -26,60 +35,60 @@ class SeatingPlan(Component): block_a_margin_left = 12 block_a_margin_top = 1 (grid - .add(SeatPixel("A01"), row=block_a_margin_top, column=block_a_margin_left, width=2, height=3) - .add(SeatPixel("A02"), row=block_a_margin_top + 4, column=block_a_margin_left, width=2, height=3) - .add(SeatPixel("A03"), row=block_a_margin_top + 8, column=block_a_margin_left, width=2, height=3) - .add(SeatPixel("A10"), row=block_a_margin_top, column=block_a_margin_left + 3, width=2, height=3) - .add(SeatPixel("A11"), row=block_a_margin_top + 4, column=block_a_margin_left + 3, width=2, height=3) - .add(SeatPixel("A12"), row=block_a_margin_top + 8, column=block_a_margin_left + 3, width=2, height=3) + .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) ) # Block B block_b_margin_left = 20 block_b_margin_top = 1 (grid - .add(SeatPixel("B01"), row=block_b_margin_top, column=block_b_margin_left, width=2, height=3) - .add(SeatPixel("B02"), row=block_b_margin_top + 4, column=block_b_margin_left, width=2, height=3) - .add(SeatPixel("B03"), row=block_b_margin_top + 8, column=block_b_margin_left, width=2, height=3) - .add(SeatPixel("B10"), row=block_b_margin_top, column=block_b_margin_left + 3, width=2, height=3) - .add(SeatPixel("B11"), row=block_b_margin_top + 4, column=block_b_margin_left + 3, width=2, height=3) - .add(SeatPixel("B12"), row=block_b_margin_top + 8, column=block_b_margin_left + 3, width=2, height=3) + .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) ) # Block C block_c_margin_left = 28 block_c_margin_top = 1 (grid - .add(SeatPixel("C01"), row=block_c_margin_top, column=block_c_margin_left, width=2, height=3) - .add(SeatPixel("C02"), row=block_c_margin_top + 4, column=block_c_margin_left, width=2, height=3) - .add(SeatPixel("C03"), row=block_c_margin_top + 8, column=block_c_margin_left, width=2, height=3) - .add(SeatPixel("C10"), row=block_c_margin_top, column=block_c_margin_left + 3, width=2, height=3) - .add(SeatPixel("C11"), row=block_c_margin_top + 4, column=block_c_margin_left + 3, width=2, height=3) - .add(SeatPixel("C12"), row=block_c_margin_top + 8, column=block_c_margin_left + 3, width=2, height=3) + .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) ) # Block D block_d_margin_left = 20 block_d_margin_top = 20 (grid - .add(SeatPixel("D01"), row=block_d_margin_top, column=block_d_margin_left, width=2, height=3) - .add(SeatPixel("D02"), row=block_d_margin_top + 4, column=block_d_margin_left, width=2, height=3) - .add(SeatPixel("D03"), row=block_d_margin_top + 8, column=block_d_margin_left, width=2, height=3) - .add(SeatPixel("D10"), row=block_d_margin_top, column=block_d_margin_left + 3, width=2, height=3) - .add(SeatPixel("D11"), row=block_d_margin_top + 4, column=block_d_margin_left + 3, width=2, height=3) - .add(SeatPixel("D12"), row=block_d_margin_top + 8, column=block_d_margin_left + 3, width=2, height=3) + .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) ) # Block E block_e_margin_left = 28 block_e_margin_top = 20 (grid - .add(SeatPixel("E01"), row=block_e_margin_top, column=block_e_margin_left, width=2, height=3) - .add(SeatPixel("E02"), row=block_e_margin_top + 4, column=block_e_margin_left, width=2, height=3) - .add(SeatPixel("E03"), row=block_e_margin_top + 8, column=block_e_margin_left, width=2, height=3) - .add(SeatPixel("E10"), row=block_e_margin_top, column=block_e_margin_left + 3, width=2, height=3) - .add(SeatPixel("E11"), row=block_e_margin_top + 4, column=block_e_margin_left + 3, width=2, height=3) - .add(SeatPixel("E12"), row=block_e_margin_top + 8, column=block_e_margin_left + 3, width=2, height=3) + .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) ) # Middle Wall @@ -100,7 +109,9 @@ class SeatingPlan(Component): grid.add(TextPixel(icon_name="material/bed"), row=1, column=1, width=4, height=11) # Toilet - grid.add(TextPixel(icon_name="material/wc"), row=1, column=7, width=3, height=4) + 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) # Entry/Helpdesk grid.add(TextPixel(text="Einlass\n &Orga"), row=19, column=3, width=7, height=5) diff --git a/src/ez_lan_manager/components/SeatingPlanInfoBox.py b/src/ez_lan_manager/components/SeatingPlanInfoBox.py new file mode 100644 index 0000000..6a2bcd3 --- /dev/null +++ b/src/ez_lan_manager/components/SeatingPlanInfoBox.py @@ -0,0 +1,26 @@ +from typing import Optional + +from rio import Component, Column, Text, TextStyle, Button, Spacer + +from src.ez_lan_manager import AccountingService + + +class SeatingPlanInfoBox(Component): + seat_id: Optional[str] = None + seat_occupant: Optional[str] = None + seat_price: int = 0 + is_blocked: bool = False + + def build(self) -> Component: + if self.is_blocked: + return Column(Text(f"Sitzplatz gesperrt", margin=1, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), wrap=True, justify="center"), min_height=10) + if self.seat_id is None and self.seat_occupant is None: + return Column(Text(f"Sitzplatz auswählen...", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), wrap=True, justify="center"), min_height=10) + return Column( + Text(f"Dieser Sitzplatz ({self.seat_id}) ist gebucht von:", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), wrap=True, justify="center"), + Text(f"{self.seat_occupant}", margin_bottom=1, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), wrap=True, justify="center"), + min_height=10 + ) if self.seat_id and self.seat_occupant else Column( + Text(f"Dieser Sitzplatz ({self.seat_id}) ist frei", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), wrap=True, justify="center"), + Button(Text(f"Buchen ({AccountingService.make_euro_string_from_int(self.seat_price)})", margin=1, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.1), wrap=True, justify="center"), shape="rounded", style="major", color="secondary", margin=1, grow_y=False), min_height=10 + ) diff --git a/src/ez_lan_manager/components/SeatingPlanPixels.py b/src/ez_lan_manager/components/SeatingPlanPixels.py index d5e7994..1f1013d 100644 --- a/src/ez_lan_manager/components/SeatingPlanPixels.py +++ b/src/ez_lan_manager/components/SeatingPlanPixels.py @@ -1,21 +1,39 @@ -from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color -from typing import Optional +from functools import partial + +from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, MouseEventListener +from typing import Optional, Callable + +from src.ez_lan_manager.types.Seat import Seat +from src.ez_lan_manager.types.SessionStorage import SessionStorage class SeatPixel(Component): seat_id: str + on_press_cb: Callable + seat: Seat + + def determine_color(self) -> Color: + if self.seat.user is not None and self.seat.user.user_id == self.session[SessionStorage].user_id: + return Color.from_hex("800080") + elif self.seat.is_blocked or self.seat.user is not None: + return self.session.theme.danger_color + return self.session.theme.success_color def build(self) -> Component: - return Rectangle( - content=Text(self.seat_id, style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5), - min_width=1, - min_height=1, - fill=self.session.theme.success_color, - hover_stroke_width = 0.1, - grow_x=True, - grow_y=True, - hover_fill=self.session.theme.hud_color, - transition_time=0.4 + return MouseEventListener( + content=Rectangle( + content=Text(self.seat_id, style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False), + min_width=1, + min_height=1, + fill=self.determine_color(), + hover_stroke_width = 0.1, + grow_x=True, + grow_y=True, + hover_fill=self.session.theme.hud_color, + transition_time=0.4, + ripple=True + ), + on_press=partial(self.on_press_cb, self.seat_id) ) class TextPixel(Component): @@ -25,7 +43,7 @@ class TextPixel(Component): def build(self) -> Component: if self.text is not None: - content = Text(self.text, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1), align_x=0.5) + content = Text(self.text, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1), align_x=0.5, selectable=False) elif self.icon_name is not None: content = Icon(self.icon_name, fill=self.session.theme.neutral_color) else: @@ -40,8 +58,8 @@ class TextPixel(Component): hover_stroke_width = None if self.no_outline else 0.1, grow_x=True, grow_y=True, - hover_fill=None if self.no_outline else self.session.theme.hud_color, - transition_time=0.4 + hover_fill=None, + ripple=True ) class WallPixel(Component): diff --git a/src/ez_lan_manager/pages/SeatingPlanPage.py b/src/ez_lan_manager/pages/SeatingPlanPage.py index c63f176..4906586 100644 --- a/src/ez_lan_manager/pages/SeatingPlanPage.py +++ b/src/ez_lan_manager/pages/SeatingPlanPage.py @@ -1,21 +1,52 @@ -from rio import Text, Column, TextStyle, Component, event +from typing import Optional -from src.ez_lan_manager import ConfigurationService +from from_root import from_root +from rio import Text, Column, TextStyle, Component, event, PressEvent, ProgressCircle, Row, Image, Button, Spacer + +from src.ez_lan_manager import ConfigurationService, SeatingService, TicketingService from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox from src.ez_lan_manager.components.SeatingPlan import SeatingPlan +from src.ez_lan_manager.components.SeatingPlanInfoBox import SeatingPlanInfoBox from src.ez_lan_manager.pages import BasePage +from src.ez_lan_manager.types.Seat import Seat + class SeatingPlanPage(Component): + seating_info: Optional[list[Seat]] = None + current_seat_id: Optional[str] = None + current_seat_occupant: Optional[str] = None + current_seat_price: int = 0 + current_seat_is_blocked: bool = False + @event.on_populate async def on_populate(self) -> None: await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Sitzplan") + self.seating_info = await self.session[SeatingService].get_seating() + + async def on_seat_clicked(self, seat_id: str, _: PressEvent) -> None: + seat = next(filter(lambda s: s.seat_id == seat_id, self.seating_info), None) + if not seat: + return + self.current_seat_is_blocked = seat.is_blocked + self.current_seat_id = seat.seat_id + ticket_info = self.session[TicketingService].get_ticket_info_by_category(seat.category) + price = 0 if not ticket_info else ticket_info.price + self.current_seat_price = price + if seat.user: + self.current_seat_occupant = seat.user.user_name + else: + self.current_seat_occupant = None def build(self) -> Component: return BasePage( content=Column( - MainViewContentBox(Text("Sitzplatz Infobox", margin=1, style=TextStyle(fill=self.session.theme.neutral_color))), MainViewContentBox( - SeatingPlan() + SeatingPlan(seat_clicked_cb=self.on_seat_clicked, seating_info=self.seating_info) 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)) + ), + MainViewContentBox( + 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) ), align_y=0 ),