Fix order item names, add Select All, pay confirmation with total, close empty order

This commit is contained in:
2026-04-20 12:22:36 +03:00
parent 79f316aeb7
commit e9b4cfcd26
3 changed files with 69 additions and 6 deletions

View File

@@ -8,7 +8,7 @@ from database import get_db
from models.order import Order, OrderItem, OrderWaiter
from models.user import User, AssistantAssignment
from models.product import Product
from schemas.order import OrderCreate, OrderOut, OrderItemOut, AddItemsRequest, PayItemsRequest, AssignWaiterRequest
from schemas.order import OrderCreate, OrderOut, OrderItemOut, AddItemsRequest, PayItemsRequest, AssignWaiterRequest, OrderWaiterOut
from routers.deps import get_current_user, require_manager
from services.printer_service import route_and_print

View File

@@ -15,10 +15,17 @@ class AddItemsRequest(BaseModel):
items: List[OrderItemInput]
class ProductNameOut(BaseModel):
id: int
name: str
model_config = {"from_attributes": True}
class OrderItemOut(BaseModel):
id: int
order_id: int
product_id: int
product: Optional[ProductNameOut] = None
added_by: int
quantity: int
unit_price: float
@@ -44,6 +51,11 @@ class AssignWaiterRequest(BaseModel):
waiter_id: int
class OrderWaiterOut(BaseModel):
waiter_id: int
model_config = {"from_attributes": True}
class OrderOut(BaseModel):
id: int
table_id: int
@@ -54,5 +66,6 @@ class OrderOut(BaseModel):
closed_by: Optional[int] = None
notes: Optional[str] = None
items: List[OrderItemOut] = []
waiters: List[OrderWaiterOut] = []
model_config = {"from_attributes": True}

View File

@@ -4,6 +4,10 @@ import OrderSummary from '../components/OrderSummary'
import useAuthStore from '../store/authStore'
import client from '../api/client'
function fmtPrice(v) {
return Number(v).toFixed(2) + ' €'
}
export default function TableDetailPage() {
const { tableId } = useParams()
const { user } = useAuthStore()
@@ -15,6 +19,7 @@ export default function TableDetailPage() {
const [paying, setPaying] = useState(false)
const [selectedIds, setSelectedIds] = useState([])
const [confirmClose, setConfirmClose] = useState(false)
const [confirmPay, setConfirmPay] = useState(false)
const [error, setError] = useState('')
async function load() {
@@ -28,7 +33,7 @@ export default function TableDetailPage() {
} else {
setOrder(null)
}
} catch (err) {
} catch {
setError('Σφάλμα φόρτωσης')
} finally {
setLoading(false)
@@ -37,6 +42,9 @@ export default function TableDetailPage() {
useEffect(() => { load() }, [tableId])
const activeItems = order?.items?.filter(i => i.status === 'active') || []
const allPaid = order && activeItems.length === 0
const isMyOrder = order && (
order.opened_by === user?.id || order.waiters?.some(w => w.waiter_id === user?.id)
)
@@ -51,8 +59,8 @@ export default function TableDetailPage() {
}
async function paySelected() {
if (selectedIds.length === 0) return
setPaying(true)
setConfirmPay(false)
try {
await client.post(`/api/orders/${order.id}/pay`, { item_ids: selectedIds })
setSelectedIds([])
@@ -78,8 +86,17 @@ export default function TableDetailPage() {
setSelectedIds(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id])
}
const allPaid = order && order.items?.filter(i => i.status === 'active').length === 0
&& order.items?.some(i => i.status === 'paid')
function selectAll() {
const allActive = activeItems.map(i => i.id)
const allSelected = allActive.every(id => selectedIds.includes(id))
setSelectedIds(allSelected ? [] : allActive)
}
const selectedTotal = activeItems
.filter(i => selectedIds.includes(i.id))
.reduce((s, i) => s + i.unit_price * i.quantity, 0)
const allActiveSelected = activeItems.length > 0 && activeItems.every(i => selectedIds.includes(i.id))
if (loading) return <div className="page page--centered"><p style={{ color: '#94a3b8' }}>Φόρτωση</p></div>
@@ -111,6 +128,14 @@ export default function TableDetailPage() {
onToggle={toggleItem}
/>
{isMyOrder && activeItems.length > 0 && (
<div style={{ padding: '4px 12px 8px' }}>
<button className="link-btn" onClick={selectAll} style={{ fontSize: 15 }}>
{allActiveSelected ? '☑ Αποεπιλογή όλων' : '☐ Επιλογή όλων'}
</button>
</div>
)}
{isMyOrder && (
<div className="action-bar">
<button className="btn btn--accent" onClick={() => navigate(`/tables/${tableId}/add`)}>
@@ -119,7 +144,7 @@ export default function TableDetailPage() {
<button
className="btn btn--success"
onClick={paySelected}
onClick={() => setConfirmPay(true)}
disabled={selectedIds.length === 0 || paying}
>
{paying ? '…' : `Πληρωμή (${selectedIds.length})`}
@@ -143,6 +168,31 @@ export default function TableDetailPage() {
</div>
)}
{/* Pay confirmation */}
{confirmPay && (
<div className="modal-overlay" onClick={() => setConfirmPay(false)}>
<div className="modal-sheet" onClick={e => e.stopPropagation()}>
<div className="modal-handle" />
<h2 className="modal-title">Επιβεβαίωση πληρωμής</h2>
<p style={{ color: '#94a3b8', textAlign: 'center', marginBottom: 4 }}>
{selectedIds.length} αντικείμενο{selectedIds.length !== 1 ? 'α' : ''}
</p>
<p style={{ color: '#f59e0b', textAlign: 'center', fontSize: 28, fontWeight: 700, marginBottom: 24 }}>
{fmtPrice(selectedTotal)}
</p>
<div style={{ display: 'flex', gap: 12 }}>
<button className="btn btn--secondary" style={{ flex: 1 }} onClick={() => setConfirmPay(false)}>
Άκυρο
</button>
<button className="btn btn--success" style={{ flex: 1 }} onClick={paySelected}>
Πληρώθηκαν
</button>
</div>
</div>
</div>
)}
{/* Close confirmation */}
{confirmClose && (
<div className="modal-overlay" onClick={() => setConfirmClose(false)}>
<div className="modal-sheet" onClick={e => e.stopPropagation()}>