from asyncio import sleep, create_task import rio from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table from src.ez_lan_manager.components.CateringCartItem import CateringCartItem from src.ez_lan_manager.components.CateringOrderItem import CateringOrderItem from src.ez_lan_manager.services.AccountingService import AccountingService from src.ez_lan_manager.services.CateringService import CateringService, CateringError, CateringErrorType from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringMenuItemsWithAmount from src.ez_lan_manager.types.SessionStorage import SessionStorage POPUP_CLOSE_TIMEOUT_SECONDS = 3 class ShoppingCartAndOrders(Component): show_cart: bool = True orders: list[CateringOrder] = [] order_button_loading: bool = False popup_message: str = "" popup_is_shown: bool = False popup_is_error: bool = True 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) async def on_remove_item(self, list_id: int) -> None: catering_service = self.session[CateringService] user_id = self.session[SessionStorage].user_id cart = catering_service.get_cart(user_id) try: cart.pop(list_id) except IndexError: return catering_service.save_cart(user_id, cart) await self.force_refresh() async def on_empty_cart_pressed(self) -> None: self.session[CateringService].save_cart(self.session[SessionStorage].user_id, []) await self.force_refresh() async def on_add_item(self, article_id: int) -> None: catering_service = self.session[CateringService] user_id = self.session[SessionStorage].user_id if not user_id: return cart = catering_service.get_cart(user_id) item_to_add = await catering_service.get_menu_item_by_id(article_id) cart.append(item_to_add) catering_service.save_cart(user_id, cart) await self.force_refresh() async def show_popup(self, text: str, is_error: bool) -> None: self.popup_is_error = is_error self.popup_message = text self.popup_is_shown = True await self.force_refresh() await sleep(POPUP_CLOSE_TIMEOUT_SECONDS) self.popup_is_shown = False await self.force_refresh() async def on_order_pressed(self) -> None: self.order_button_loading = True await self.force_refresh() user_id = self.session[SessionStorage].user_id cart = self.session[CateringService].get_cart(user_id) show_popup_task = None if len(cart) < 1: show_popup_task = create_task(self.show_popup("Warenkorb leer", True)) else: items_with_amounts: CateringMenuItemsWithAmount = {} for item in cart: try: items_with_amounts[item] += 1 except KeyError: items_with_amounts[item] = 1 try: await self.session[CateringService].place_order(items_with_amounts, user_id) except CateringError as catering_error: if catering_error.error_type == CateringErrorType.INCLUDES_DISABLED_ITEM: show_popup_task = create_task(self.show_popup("Warenkorb enthält gesperrte Artikel", True)) elif catering_error.error_type == CateringErrorType.INSUFFICIENT_FUNDS: 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, []) self.order_button_loading = False if not show_popup_task: show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False)) async def _create_order_info_modal(self, order: CateringOrder) -> None: def build_dialog_content() -> rio.Component: # @todo: rio 0.10.8 did not have the ability to align the columns, check back in a future version table = Table( { "Artikel": [item.name for item in order.items.keys()] + ["Gesamtpreis:"], "Anzahl": [item for item in order.items.values()] + [""], "Preis": [AccountingService.make_euro_string_from_int(item.price) for item in order.items.keys()] + [AccountingService.make_euro_string_from_int(order.price)], }, show_row_numbers=False ) return rio.Card( rio.Column( rio.Text( f"Deine Bestellung ({order.order_id})", align_x=0.5, margin_bottom=0.5 ), table, margin=2, ), align_x=0.5, align_y=0.2, min_width=50, min_height=10, color=self.session.theme.primary_color, margin_left=1, margin_right=1, margin_top=2, margin_bottom=1, ) dialog = await self.session.show_custom_dialog( build=build_dialog_content, modal=True, user_closeable=True, ) await dialog.wait_for_close() def build(self) -> rio.Component: user_id = self.session[SessionStorage].user_id catering_service = self.session[CateringService] cart = catering_service.get_cart(user_id) if self.show_cart: cart_container = ScrollContainer( content=Column( *[CateringCartItem( article_name=cart_item.name, article_price=cart_item.price, article_id=cart_item.item_id, remove_item_cb=self.on_remove_item, list_id=idx ) for idx, cart_item in enumerate(cart)], Spacer(grow_y=True) ), min_height=8, min_width=33, margin=1 ) return Column( cart_container, Popup( anchor=cart_container, content=Text(self.popup_message, style=TextStyle(fill=self.session.theme.danger_color if self.popup_is_error else self.session.theme.success_color), overflow="wrap", margin=2, justify="center", min_width=20), is_open=self.popup_is_shown, position="center", color=self.session.theme.primary_color ), Row( Text( text=f"Preis: {AccountingService.make_euro_string_from_int(sum(cart_item.price for cart_item in cart))}", style=TextStyle( fill=self.session.theme.background_color, font_size=0.8 ), margin=1 ), Button( content=Text( "Warenkorb leeren", style=TextStyle(fill=self.session.theme.danger_color, font_size=0.9), align_x=0.2 ), margin=1, margin_left=0, shape="rectangle", style="major", color="primary", on_press=self.on_empty_cart_pressed ), Button( content=Text( "Bestellen", style=TextStyle(fill=self.session.theme.success_color, font_size=0.9), align_x=0.2 ), margin=1, margin_left=0, shape="rectangle", style="major", color="primary", on_press=self.on_order_pressed, is_loading=self.order_button_loading ) ) ) else: orders_container = ScrollContainer( content=Column( *[CateringOrderItem( order=order_item, info_modal_cb=self._create_order_info_modal ) for order_item in self.orders], Spacer(grow_y=True) ), min_height=8, min_width=33, margin=1 ) return Column(orders_container)