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,87 @@
from pydantic import BaseModel, field_validator
from typing import Optional, List
MAX_TABLE_NAME_LENGTH = 6
class TableGroupCreate(BaseModel):
name: str
prefix: Optional[str] = None
color: Optional[str] = None
class TableGroupUpdate(BaseModel):
name: Optional[str] = None
prefix: Optional[str] = None
color: Optional[str] = None
class TableGroupOut(BaseModel):
id: int
name: str
prefix: Optional[str] = None
sort_order: int = 0
color: Optional[str] = None
model_config = {"from_attributes": True}
class TableBase(BaseModel):
label: Optional[str] = None
group_id: Optional[int] = None
is_active: bool = True
class TableCreate(BaseModel):
label: Optional[str] = None
group_id: Optional[int] = None
@field_validator("label")
@classmethod
def label_max_length(cls, v):
if v is not None:
v = v.strip()
if len(v) > MAX_TABLE_NAME_LENGTH:
raise ValueError(f"Table name cannot exceed {MAX_TABLE_NAME_LENGTH} characters")
return v
class TableBatchCreate(BaseModel):
group_id: Optional[int] = None
count: int
name_prefix: str # e.g. "TBL-" → TBL-1, TBL-2 ...
# start_number is computed on the backend from existing tables in the group
class TableUpdate(BaseModel):
label: Optional[str] = None
group_id: Optional[int] = None
is_active: Optional[bool] = None
@field_validator("label")
@classmethod
def label_max_length(cls, v):
if v is not None:
v = v.strip()
if len(v) > MAX_TABLE_NAME_LENGTH:
raise ValueError(f"Table name cannot exceed {MAX_TABLE_NAME_LENGTH} characters")
return v
class TableFloorplanUpdate(BaseModel):
floor_x: float
floor_y: float
class TableOut(BaseModel):
id: int
number: int
label: Optional[str] = None
group_id: Optional[int] = None
is_active: bool = True
floor_x: Optional[float] = None
floor_y: Optional[float] = None
group: Optional[TableGroupOut] = None
has_active_order: bool = False
model_config = {"from_attributes": True}