diff --git a/src/ez_lan_manager/components/LoginBox.py b/src/ez_lan_manager/components/LoginBox.py new file mode 100644 index 0000000..e3f5ff5 --- /dev/null +++ b/src/ez_lan_manager/components/LoginBox.py @@ -0,0 +1,102 @@ +from typing import Callable + +from rio import Component, TextStyle, Color, TextInput, Button, Text, Rectangle, Column, Row, Spacer, TextInputChangeEvent + +from src.ez_lan_manager.services.UserService import UserService +from src.ez_lan_manager.types.SessionStorage import SessionStorage + + +class LoginBox(Component): + status_change_cb: Callable + TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9) + user_name_input_text: list[str] = [""] + password_input_text: list[str] = [""] + user_name_input_is_valid = True + password_input_is_valid = True + login_button_is_loading = False + + async def _on_login_pressed(self) -> None: + if await self.session[UserService].is_login_valid(self.user_name_input_text[0], self.password_input_text[0]): + self.user_name_input_is_valid = True + self.password_input_is_valid = True + self.login_button_is_loading = False + await self.session[SessionStorage].set_user_id((await self.session[UserService].get_user(self.user_name_input_text[0])).user_id) + await self.status_change_cb() + else: + self.user_name_input_is_valid = False + self.password_input_is_valid = False + self.login_button_is_loading = False + await self.force_refresh() + + def build(self) -> Component: + def set_user_name_input_text(e: TextInputChangeEvent) -> None: + self.user_name_input_text[0] = e.text + + def set_password_input_text(e: TextInputChangeEvent) -> None: + self.password_input_text[0] = e.text + + user_name_input = TextInput( + text="", + label="Benutzername", + accessibility_label="Benutzername", + min_height=0.5, + on_confirm=lambda _: self._on_login_pressed(), + on_change=set_user_name_input_text, + is_valid=self.user_name_input_is_valid + ) + password_input = TextInput( + text="", + label="Passwort", + accessibility_label="Passwort", + is_secret=True, + on_confirm=lambda _: self._on_login_pressed(), + on_change=set_password_input_text, + is_valid=self.password_input_is_valid + ) + login_button = Button( + Text("LOGIN", style=self.TEXT_STYLE, justify="center"), + shape="rectangle", + style="minor", + color="secondary", + margin_bottom=0.4, + on_press=self._on_login_pressed + ) + register_button = Button( + Text("REG", style=self.TEXT_STYLE, justify="center"), + shape="rectangle", + style="minor", + color="secondary", + on_press=lambda: self.session.navigate_to("./register") + ) + forgot_password_button = Button( + Text("LST PWD", style=self.TEXT_STYLE, justify="center"), + shape="rectangle", + style="minor", + color="secondary", + on_press=lambda: self.session.navigate_to("./forgot-password") + ) + + return Rectangle( + content=Column( + user_name_input, + password_input, + Column( + Row( + login_button + ), + Row( + register_button, + Spacer(), + forgot_password_button, + proportions=(49, 2, 49) + ) + ), + spacing=0.4 + ), + fill=Color.TRANSPARENT, + min_height=8, + min_width=12, + align_x=0.5, + margin_top=0.3, + margin_bottom=2 + ) \ No newline at end of file diff --git a/src/ez_lan_manager/components/UserInfoAndLoginBox.py b/src/ez_lan_manager/components/UserInfoAndLoginBox.py index 5284bc1..2f4e218 100644 --- a/src/ez_lan_manager/components/UserInfoAndLoginBox.py +++ b/src/ez_lan_manager/components/UserInfoAndLoginBox.py @@ -1,198 +1,15 @@ import logging -from random import choice -from typing import Optional -from rio import Component, Column, Text, Row, Rectangle, Button, TextStyle, Color, Spacer, TextInput, Link, event - -from src.ez_lan_manager import UserService -from src.ez_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton -from src.ez_lan_manager.services.AccountingService import AccountingService -from src.ez_lan_manager.services.TicketingService import TicketingService -from src.ez_lan_manager.services.SeatingService import SeatingService -from src.ez_lan_manager.types.Seat import Seat -from src.ez_lan_manager.types.Ticket import Ticket -from src.ez_lan_manager.types.User import User +from rio import Component +from src.ez_lan_manager.components.LoginBox import LoginBox +from src.ez_lan_manager.components.UserInfoBox import UserInfoBox from src.ez_lan_manager.types.SessionStorage import SessionStorage logger = logging.getLogger(__name__.split(".")[-1]) -class StatusButton(Component): - STYLE = TextStyle(fill=Color.from_hex("121212"), font_size=0.5) - label: str - target_url: str - enabled: bool - - def build(self) -> Component: - return Link( - content=Button( - content=Text(self.label, style=self.STYLE, justify="center"), - shape="rectangle", - style="major", - color="success" if self.enabled else "danger", - grow_x=True, - margin_left=0.6, - margin_right=0.6, - margin_top=0.6 - ), - target_url=self.target_url, - align_y=0.5, - grow_y=False - ) - - class UserInfoAndLoginBox(Component): - TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9) - show_login: bool = True - user: Optional[User] = None - user_balance: Optional[int] = 0 - user_ticket: Optional[Ticket] = None - user_seat: Optional[Seat] = None - - @event.on_populate - async def async_init(self) -> None: - if self.session[SessionStorage].user_id: - self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id) - 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) - - @staticmethod - def get_greeting() -> str: - return choice(["Guten Tacho", "Tuten Gag", "Servus", "Moinjour", "Hallöchen", "Heyho", "Moinsen"]) - - # @FixMe: If the user logs out and then tries to log back in, it does not work - # If the user navigates to another page and then tries again. It works. - # When fixed, remove the workaround below. - async def logout(self) -> None: - self.show_login = True - await self.session[SessionStorage].clear() - await self.force_refresh() - # @FixMe: Workaround for the bug described above. Navigating to another page solves the issue. - # Yet, this is not desired behavior. - subpage = str(self.session.active_page_url) - if subpage.endswith("/") or subpage.endswith("news"): - self.session.navigate_to("./overview") - else: - self.session.navigate_to("./news") - - async def _on_login_pressed(self) -> None: - user_name = self.user_name_input.text.lower() - if await self.session[UserService].is_login_valid(user_name, self.password_input.text): - self.user_name_input.is_valid = True - self.password_input.is_valid = True - self.login_button.is_loading = False - await self.session[SessionStorage].set_user_id((await self.session[UserService].get_user(user_name)).user_id) - await self.async_init() - self.show_login = False - else: - self.user_name_input.is_valid = False - self.password_input.is_valid = False - self.login_button.is_loading = False - def build(self) -> Component: - self.user_name_input = TextInput( - text="", - label="Benutzername", - accessibility_label="Benutzername", - min_height=0.5, - on_confirm=lambda _: self._on_login_pressed() - ) - self.password_input = TextInput( - text="", - label="Passwort", - accessibility_label="Passwort", - is_secret=True, - on_confirm=lambda _: self._on_login_pressed() - ) - self.login_button = Button( - Text("LOGIN", style=self.TEXT_STYLE, justify="center"), - shape="rectangle", - style="minor", - color="secondary", - margin_bottom=0.4, - on_press=self._on_login_pressed - ) - self.register_button = Button( - Text("REG", style=self.TEXT_STYLE, justify="center"), - shape="rectangle", - style="minor", - color="secondary", - on_press=lambda: self.session.navigate_to("./register") - ) - self.forgot_password_button = Button( - Text("LST PWD", style=self.TEXT_STYLE, justify="center"), - shape="rectangle", - style="minor", - color="secondary", - on_press=lambda: self.session.navigate_to("./forgot-password") - ) - - if self.user is None and self.session[SessionStorage].user_id is None: - return Rectangle( - content=Column( - self.user_name_input, - self.password_input, - Column( - Row( - self.login_button - ), - Row( - self.register_button, - Spacer(), - self.forgot_password_button, - proportions=(49, 2, 49) - ) - ), - spacing=0.4 - ), - fill=Color.TRANSPARENT, - min_height=8, - min_width=12, - align_x=0.5, - margin_top=0.3, - margin_bottom=2 - ) - elif self.user is None and self.session[SessionStorage].user_id is not None: - return Rectangle( - content=Column(), - fill=Color.TRANSPARENT, - min_height=8, - min_width=12, - align_x=0.5, - margin_top=0.3, - margin_bottom=2 - ) + if self.session[SessionStorage].user_id is None: + return LoginBox(status_change_cb=self.force_refresh) else: - return Rectangle( - content=Column( - Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), justify="center"), - Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"), - Row( - StatusButton(label="TICKET", target_url="./buy_ticket", - enabled=self.user_ticket is not None), - StatusButton(label="SITZPLATZ", target_url="./seating", - enabled=self.user_seat is not None), - proportions=(50, 50), - grow_y=False - ), - UserInfoBoxButton("Profil bearbeiten", "./edit-profile"), - UserInfoBoxButton(f"Guthaben: {self.session[AccountingService].make_euro_string_from_int(self.user_balance)}", "./account"), - Button( - content=Text("Ausloggen", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)), - shape="rectangle", - style="minor", - color="secondary", - grow_x=True, - margin_left=0.6, - margin_right=0.6, - margin_top=0.6, - on_press=self.logout - ) - ), - fill=Color.TRANSPARENT, - min_height=8, - min_width=12, - align_x=0.5, - margin_top=0.3, - margin_bottom=2 - ) + return UserInfoBox(status_change_cb=self.force_refresh) diff --git a/src/ez_lan_manager/components/UserInfoBox.py b/src/ez_lan_manager/components/UserInfoBox.py new file mode 100644 index 0000000..378aeb7 --- /dev/null +++ b/src/ez_lan_manager/components/UserInfoBox.py @@ -0,0 +1,100 @@ +from random import choice +from typing import Optional, Callable + +from rio import Component, TextStyle, Color, Button, Text, Rectangle, Column, Row, Spacer, Link, event + +from src.ez_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton +from src.ez_lan_manager.services.UserService import UserService +from src.ez_lan_manager.services.AccountingService import AccountingService +from src.ez_lan_manager.services.TicketingService import TicketingService +from src.ez_lan_manager.services.SeatingService import SeatingService +from src.ez_lan_manager.types.Seat import Seat +from src.ez_lan_manager.types.Ticket import Ticket +from src.ez_lan_manager.types.User import User +from src.ez_lan_manager.types.SessionStorage import SessionStorage + + +class StatusButton(Component): + STYLE = TextStyle(fill=Color.from_hex("121212"), font_size=0.5) + label: str + target_url: str + enabled: bool + + def build(self) -> Component: + return Link( + content=Button( + content=Text(self.label, style=self.STYLE, justify="center"), + shape="rectangle", + style="major", + color="success" if self.enabled else "danger", + grow_x=True, + margin_left=0.6, + margin_right=0.6, + margin_top=0.6 + ), + target_url=self.target_url, + align_y=0.5, + grow_y=False + ) + +class UserInfoBox(Component): + status_change_cb: Callable + TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9) + user: Optional[User] = None + user_balance: Optional[int] = 0 + user_ticket: Optional[Ticket] = None + user_seat: Optional[Seat] = None + + @staticmethod + def get_greeting() -> str: + return choice(["Guten Tacho", "Tuten Gag", "Servus", "Moinjour", "Hallöchen", "Heyho", "Moinsen"]) + + async def logout(self) -> None: + await self.session[SessionStorage].clear() + self.user = None + await self.status_change_cb() + + @event.on_populate + async def async_init(self) -> None: + if self.session[SessionStorage].user_id: + self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id) + 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) + + def build(self) -> Component: + if not self.user: + return Spacer() + return Rectangle( + content=Column( + Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), justify="center"), + Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"), + Row( + StatusButton(label="TICKET", target_url="./buy_ticket", + enabled=self.user_ticket is not None), + StatusButton(label="SITZPLATZ", target_url="./seating", + enabled=self.user_seat is not None), + proportions=(50, 50), + grow_y=False + ), + UserInfoBoxButton("Profil bearbeiten", "./edit-profile"), + UserInfoBoxButton(f"Guthaben: {self.session[AccountingService].make_euro_string_from_int(self.user_balance)}", "./account"), + Button( + content=Text("Ausloggen", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)), + shape="rectangle", + style="minor", + color="secondary", + grow_x=True, + margin_left=0.6, + margin_right=0.6, + margin_top=0.6, + on_press=self.logout + ) + ), + fill=Color.TRANSPARENT, + min_height=8, + min_width=12, + align_x=0.5, + margin_top=0.3, + margin_bottom=2 + )