add basic overview page for catering management

This commit is contained in:
David Rodenkirchen 2025-01-27 09:34:23 +01:00
parent d86a3da135
commit 0a2647bcf8
6 changed files with 136 additions and 7 deletions

View File

@ -25,6 +25,6 @@ class CateringCartItem(Component):
return Row(
Text(self.ellipsize_string(self.article_name), align_x=0, overflow="wrap", min_width=19, style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)),
Text(AccountingService.make_euro_string_from_int(self.article_price), style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)),
IconButton(icon="material/close", size=2, color=self.session.theme.danger_color, style="plain-text", on_press=lambda: self.remove_item_cb(self.list_id)),
IconButton(icon="material/close", min_size=2, color=self.session.theme.danger_color, style="plain-text", on_press=lambda: self.remove_item_cb(self.list_id)),
proportions=(19, 5, 2)
)

View File

@ -0,0 +1,47 @@
from typing import Optional
from rio import Component, Row, Card, Column, Text, TextStyle, Spacer
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ez_lan_manager.types.Seat import Seat
class CateringManagementOrderDisplay(Component):
order: CateringOrder
seat: Optional[Seat]
def format_order_status(self, status: CateringOrderStatus) -> Text:
status_text = CateringOrder.translate_order_status(status)
color = self.session.theme.warning_color
if status == CateringOrderStatus.DELAYED or status == CateringOrderStatus.CANCELED:
color = self.session.theme.danger_color
elif status == CateringOrderStatus.COMPLETED:
color = self.session.theme.success_color
return Text(text=status_text, style=TextStyle(fill=color))
def build(self) -> Component:
return Card(
content=Column(
Row(
Text(f"Status: ", margin_left=0.3, margin_top=0.2),
self.format_order_status(self.order.status),
Spacer(),
Text(self.order.order_date.strftime("%d.%m. - %H:%M Uhr"), margin_right=0.3),
),
Row(
Text(f"Gast: {self.order.customer.user_name}", margin_left=0.3),
Spacer(),
Text(f"Sitzplatz: {'-' if not self.seat else self.seat.seat_id}", margin_right=0.3),
),
Row(
Text("Diese Bestellung wird:", margin_left=0.3),
Spacer(),
Text("Geliefert" if self.order.is_delivery else "Abgeholt", margin_right=0.3, margin_bottom=0.2),
)
),
color=self.session.theme.hud_color,
colorize_on_hover=True,
margin=1
)

View File

@ -65,8 +65,9 @@ class ShoppingCartAndOrders(Component):
user_id = self.session[SessionStorage].user_id
cart = self.session[CateringService].get_cart(user_id)
show_popup_task = None
if len(cart) < 1:
_ = create_task(self.show_popup("Warenkorb leer", True))
show_popup_task = create_task(self.show_popup("Warenkorb leer", True))
else:
items_with_amounts: CateringMenuItemsWithAmount = {}
for item in cart:
@ -78,14 +79,15 @@ class ShoppingCartAndOrders(Component):
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:
_ = create_task(self.show_popup("Warenkorb enthält gesperrte Artikel", True))
show_popup_task = create_task(self.show_popup("Warenkorb enthält gesperrte Artikel", True))
elif catering_error.error_type == CateringErrorType.INSUFFICIENT_FUNDS:
_ = create_task(self.show_popup("Guthaben nicht ausreichend", True))
show_popup_task = create_task(self.show_popup("Guthaben nicht ausreichend", True))
else:
_ = create_task(self.show_popup("Unbekannter Fehler", True))
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
_ = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", 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:

View File

@ -65,6 +65,8 @@ class UserInfoBox(Component):
self.session[AccountingService].add_update_hook(self.update)
async def update(self) -> None:
if not self.user:
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)

View File

@ -1,16 +1,52 @@
import logging
from dataclasses import field, dataclass
from datetime import datetime
from typing import Optional
from rio import Column, Component, event, TextStyle, Text, Spacer
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager import ConfigurationService, CateringService, SeatingService
from src.ez_lan_manager.components.CateringManagementOrderDisplay import CateringManagementOrderDisplay
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ez_lan_manager.types.Seat import Seat
logger = logging.getLogger(__name__.split(".")[-1])
@dataclass
class CateringOrderWithSeat:
catering_order: CateringOrder
seat: Optional[Seat]
class ManageCateringPage(Component):
all_orders: list[CateringOrderWithSeat] = field(default_factory=list)
last_updated: Optional[datetime] = None
@event.on_populate
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Catering Verwaltung")
self.all_orders = await self.populate_seating(await self.session[CateringService].get_orders())
self.last_updated = datetime.now()
@event.periodic(30)
async def update_orders(self) -> None:
polled_orders = await self.session[CateringService].get_orders()
self.all_orders = await self.populate_seating(polled_orders)
self.last_updated = datetime.now()
async def populate_seating(self, orders: list[CateringOrder]) -> list[CateringOrderWithSeat]:
result = []
for order in orders:
seat = await self.session[SeatingService].get_user_seat(order.customer.user_id)
result.append(CateringOrderWithSeat(catering_order=order, seat=seat))
return result
def get_all_pending_orders(self) -> list[CateringOrderWithSeat]:
filtered_list = list(filter(lambda o: o.catering_order.status != CateringOrderStatus.COMPLETED and o.catering_order.status != CateringOrderStatus.CANCELED, self.all_orders))
sorted_list = sorted(filtered_list, key=lambda o: o.catering_order.order_date)
return sorted_list
def build(self) -> Component:
return Column(
@ -28,5 +64,30 @@ class ManageCateringPage(Component):
)
)
),
MainViewContentBox(
Column(
Text(
text="Offene Bestellungen",
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1.2
),
margin_top=2,
margin_bottom=0.2,
align_x=0.5
),
Text(
text=f"Letzte Aktualisierung: {'-' if not self.last_updated else self.last_updated.strftime('%H:%M:%S')}",
style=TextStyle(
fill=self.session.theme.background_color,
font_size=0.7
),
margin_top=0.2,
margin_bottom=2,
align_x=0.5
),
*[CateringManagementOrderDisplay(v.catering_order, v.seat) for v in self.get_all_pending_orders()],
)
),
Spacer()
)

View File

@ -31,3 +31,20 @@ class CateringOrder:
for item, amount in self.items.items():
total += (item.price * amount)
return total
@staticmethod
def translate_order_status(status: CateringOrderStatus) -> str:
if status == CateringOrderStatus.RECEIVED:
return "Eingegangen"
elif status == CateringOrderStatus.DELAYED:
return "Verzögert"
elif status == CateringOrderStatus.READY_FOR_PICKUP:
return "Abholbereit"
elif status == CateringOrderStatus.EN_ROUTE:
return "In Zustellung"
elif status == CateringOrderStatus.COMPLETED:
return "Abgeschlossen"
elif status == CateringOrderStatus.CANCELED:
return "Storniert"
else:
raise RuntimeError("Unknown CateringOrderStatus:", status)