feature/137-improve-catering-ui #68

Merged
Typhus merged 2 commits from feature/137-improve-catering-ui into main 2026-05-02 11:01:36 +00:00
4 changed files with 162 additions and 55 deletions
Showing only changes of commit f06329f727 - Show all commits

View File

@ -1 +1 @@
0.5.0 0.5.1

View File

@ -1,29 +1,30 @@
import logging
from asyncio import create_task, sleep
from functools import partial from functools import partial
from typing import Optional, Callable from typing import Optional, Callable
from rio import Component, Row, Card, Column, Text, TextStyle, Spacer, PointerEventListener, Button from rio import Component, Row, Card, Column, Text, TextStyle, Spacer, PointerEventListener, Button, Rectangle, Popup, Icon, Color
from src.ezgg_lan_manager import ReceiptPrintingService
from src.ezgg_lan_manager.components.StatusChangePopup import StatusChangePopup
from src.ezgg_lan_manager.services.CateringService import CateringService from src.ezgg_lan_manager.services.CateringService import CateringService
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ezgg_lan_manager.types.Seat import Seat from src.ezgg_lan_manager.types.Seat import Seat
class CateringManagementOrderDisplayStatusButton(Component): logger = logging.getLogger(__name__.split(".")[-1])
status: CateringOrderStatus
clicked_cb: Callable
def build(self) -> Component:
return Button(
content=Text(
CateringOrder.translate_order_status(self.status)
),
shape="rectangle",
on_press=partial(self.clicked_cb, self.status)
)
class CateringManagementOrderDisplay(Component): class CateringManagementOrderDisplay(Component):
order: CateringOrder order: CateringOrder
seat: Optional[Seat] seat: Optional[Seat]
clicked_cb: Callable clicked_cb: Callable
status_change_popup_open: bool = False
def reprint_order(self) -> None:
create_task(self.session[ReceiptPrintingService].print_order(self.order.customer, self.order))
def open_status_change_popup(self) -> None:
self.status_change_popup_open = True
def format_order_status(self, status: CateringOrderStatus) -> Text: def format_order_status(self, status: CateringOrderStatus) -> Text:
status_text = CateringOrder.translate_order_status(status) status_text = CateringOrder.translate_order_status(status)
@ -36,14 +37,12 @@ class CateringManagementOrderDisplay(Component):
return Text(text=status_text, style=TextStyle(fill=color)) return Text(text=status_text, style=TextStyle(fill=color))
async def status_button_clicked(self, new_status: CateringOrderStatus) -> None: async def change_status(self, new_status: CateringOrderStatus) -> Optional[str]:
if self.order.status == CateringOrderStatus.CANCELED: await sleep(1)
return
if new_status == CateringOrderStatus.CANCELED: logger.debug(f"Status of order with ID {self.order.order_id} changing from {self.order.status} to {new_status}")
# ToDo: Hier sollten wir nochmal nachfragen ob der Bediener sich wirklich sicher ist, if self.order.status == CateringOrderStatus.CANCELED: # Can not un-cancel
# und anwarnen das eine stornierte Bestellung nicht ent-storniert werden kann. return "Stornierte Bestellungen können nicht angepasst werden"
pass
if self.order.status != new_status: if self.order.status != new_status:
if new_status == CateringOrderStatus.CANCELED: if new_status == CateringOrderStatus.CANCELED:
@ -61,9 +60,11 @@ class CateringManagementOrderDisplay(Component):
is_delivery=self.order.is_delivery is_delivery=self.order.is_delivery
) )
self.status_change_popup_open = False
def build(self) -> Component: def build(self) -> Component:
return PointerEventListener( card = Card(
content=Card(
content=Column( content=Column(
Row( Row(
Text(f"ID: {self.order.order_id}", margin_left=0.3, margin_top=0.2, justify="center", style=TextStyle(font_size=1.2)), Text(f"ID: {self.order.order_id}", margin_left=0.3, margin_top=0.2, justify="center", style=TextStyle(font_size=1.2)),
@ -85,19 +86,32 @@ class CateringManagementOrderDisplay(Component):
Text("Geliefert" if self.order.is_delivery else "Abgeholt", margin_right=0.3, margin_bottom=0.5), Text("Geliefert" if self.order.is_delivery else "Abgeholt", margin_right=0.3, margin_bottom=0.5),
), ),
Row( Row(
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.RECEIVED, self.status_button_clicked), Rectangle(
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.CANCELED, self.status_button_clicked), content=Button(
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.EN_ROUTE, self.status_button_clicked) content=Text("Beleg drucken", justify="left"),
shape="rectangle",
on_press=self.reprint_order
),
stroke_width=0.1
),
Rectangle(
content=Button(
content=Text("Status ändern", justify="right"),
shape="rectangle",
on_press=self.open_status_change_popup
),
stroke_width=0.1
), ),
Row(
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.READY_FOR_PICKUP, self.status_button_clicked),
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.COMPLETED, self.status_button_clicked),
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.DELAYED, self.status_button_clicked),
) )
), ),
color=self.session.theme.hud_color, color=self.session.theme.hud_color,
colorize_on_hover=True, colorize_on_hover=True,
margin=1 margin=1
), )
status_change_popup = StatusChangePopup(card, self.status_change_popup_open, self.change_status)
return PointerEventListener(
content=status_change_popup,
on_press=partial(self.clicked_cb, self.order) on_press=partial(self.clicked_cb, self.order)
) )

View File

@ -0,0 +1,92 @@
from functools import partial
from typing import Callable, Optional
from rio import Column, Row, Text, Button, Component, Icon, Popup, Rectangle, Color, Tooltip, PointerEventListener, PointerEvent, ProgressCircle
from src.ezgg_lan_manager.types.CateringOrder import CateringOrderStatus, CateringOrder
ICONS_BY_STATUS = {
CateringOrderStatus.RECEIVED: "material/move_to_inbox",
CateringOrderStatus.DELAYED: "material/hourglass_top",
CateringOrderStatus.READY_FOR_PICKUP: "material/takeout_dining",
CateringOrderStatus.EN_ROUTE: "material/local_shipping",
CateringOrderStatus.COMPLETED: "material/check_circle",
CateringOrderStatus.CANCELED: "material/cancel",
}
class StatusChangeButton(Component):
status: CateringOrderStatus
clicked_cb: Callable
def build(self) -> Component:
return Tooltip(
anchor=PointerEventListener(
content=Rectangle(
fill=Color.TRANSPARENT,
content=Column(
Icon(
icon=ICONS_BY_STATUS[self.status]
)
),
stroke_width=0.1,
stroke_color=Color.TRANSPARENT,
hover_stroke_width=0.1,
hover_stroke_color=Color.BLACK
),
on_press=partial(self.clicked_cb, self.status)
),
tip=Text(text=CateringOrder.translate_order_status(self.status)),
position="top"
)
class StatusChangePopup(Component):
anchor: Component
popup_open: bool
status_should_change_cb: Callable
response: Optional[str] = None
is_loading: bool = False
async def handle_button_clicked(self, status: CateringOrderStatus, _: PointerEvent) -> None:
self.is_loading = True
self.response = await self.status_should_change_cb(status)
self.is_loading = False
def close(self) -> None:
self.popup_open = False
def build(self) -> Component:
if self.is_loading:
content = Row(
ProgressCircle(margin=1)
)
elif self.response:
content = Row(
Text(text=self.response, justify="center", overflow="wrap", margin=1)
)
else:
content = Row(
StatusChangeButton(CateringOrderStatus.RECEIVED, self.handle_button_clicked),
StatusChangeButton(CateringOrderStatus.DELAYED, self.handle_button_clicked),
StatusChangeButton(CateringOrderStatus.READY_FOR_PICKUP, self.handle_button_clicked),
StatusChangeButton(CateringOrderStatus.EN_ROUTE, self.handle_button_clicked),
StatusChangeButton(CateringOrderStatus.COMPLETED, self.handle_button_clicked),
StatusChangeButton(CateringOrderStatus.CANCELED, self.handle_button_clicked),
spacing=0.5,
margin=0.5
)
return Popup(
anchor=self.anchor,
content=Rectangle(
content=Column(
content,
Button(content=Text(text="Abbrechen", justify="center", fill=self.session.theme.secondary_color), shape="rectangle", style="colored-text", on_press=self.close),
proportions=[2.5, 1]
),
fill=self.session.theme.hud_color,
min_width=34,
min_height=8.3
),
is_open=self.popup_open
)

View File

@ -106,6 +106,7 @@ class ManageCateringPage(Component):
return sorted_list return sorted_list
async def order_clicked(self, order: CateringOrder, _: PointerEvent) -> None: async def order_clicked(self, order: CateringOrder, _: PointerEvent) -> None:
await self.update_orders()
self.order_popup_order = order self.order_popup_order = order
self.order_popup_open = True self.order_popup_open = True