feat: initial commit — local services (backend + manager dashboard + waiter PWA)

Includes all work to date:
- local_backend: FastAPI backend with products, orders, tables, shifts, cloud sync
- manager_dashboard: React manager UI with product/category management, reports, settings
- waiter_pwa: React PWA for waiter devices
- Category reparent endpoint and UI
- Waiter domain: local_ip sent on heartbeat, waiter_domain persisted from cloud response
- QR code modal in AppInfoTab for waiter domain
- Product form: number input spinners removed, category pre-selected on new product
- Category row: count badge moved to far right

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 14:04:38 +03:00
commit 8ba8c95ecd
209 changed files with 48017 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
from pydantic import BaseModel
from datetime import datetime
from typing import Optional, List
from schemas.base import UTCDatetime
class UserBase(BaseModel):
username: str
role: str
is_active: bool = True
full_name: Optional[str] = None
nickname: Optional[str] = None
mobile_phone: Optional[str] = None
avatar_url: Optional[str] = None
email: Optional[str] = None
class UserCreate(UserBase):
pin: str
class UserUpdate(BaseModel):
username: Optional[str] = None
role: Optional[str] = None
is_active: Optional[bool] = None
full_name: Optional[str] = None
nickname: Optional[str] = None
mobile_phone: Optional[str] = None
class WaiterZoneOut(BaseModel):
id: int
waiter_id: int
group_id: Optional[int] = None # None = all zones
model_config = {"from_attributes": True}
class UserOut(UserBase):
id: int
created_at: UTCDatetime
zone_assignments: List[WaiterZoneOut] = []
model_config = {"from_attributes": True}
class SetZonesRequest(BaseModel):
"""Replace all zone assignments for a waiter in one call.
group_ids=[] means remove all (sees nothing).
group_ids=[null] or all_zones=True means the wildcard 'all zones' sentinel."""
group_ids: Optional[List[Optional[int]]] = None # list of group ids; None in list = all-zones sentinel
all_zones: bool = False # convenience flag: if True, set a single NULL-group_id row
class AssistantAssignmentOut(BaseModel):
id: int
primary_waiter_id: int
assistant_waiter_id: int
assigned_at: UTCDatetime
model_config = {"from_attributes": True}