Phase 2: scaffold Waiter PWA — React+Vite, PWA manifest, all pages and components

This commit is contained in:
2026-04-20 12:03:26 +03:00
parent 803358e52c
commit 36cc67dbbc
30 changed files with 8129 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import TableCard from '../components/TableCard'
import ConnectionBanner from '../components/ConnectionBanner'
import useAuthStore from '../store/authStore'
import client from '../api/client'
const FILTERS = ['all', 'mine', 'free']
const FILTER_LABELS = { all: 'Όλα', mine: 'Δικά μου', free: 'Ελεύθερα' }
export default function TableListPage() {
const { user, logout } = useAuthStore()
const [tables, setTables] = useState([])
const [orders, setOrders] = useState([])
const [filter, setFilter] = useState('all')
const [offline, setOffline] = useState(false)
const navigate = useNavigate()
useEffect(() => {
const handler = () => setOffline(true)
window.addEventListener('backend-offline', handler)
return () => window.removeEventListener('backend-offline', handler)
}, [])
async function load() {
try {
const [tablesRes, ordersRes] = await Promise.all([
client.get('/api/tables/'),
client.get('/api/orders/my'),
])
setTables(tablesRes.data)
setOrders(ordersRes.data)
setOffline(false)
} catch {}
}
useEffect(() => { load() }, [])
function getOrder(tableId) {
return orders.find(o => o.table_id === tableId && ['open', 'partially_paid'].includes(o.status))
}
const filtered = tables.filter(t => {
const order = getOrder(t.id)
if (filter === 'free') return !order
if (filter === 'mine') return order && order.waiters?.some(w => w.waiter_id === user?.id)
return true
})
function handleLogout() {
logout()
navigate('/login')
}
return (
<div className="page">
<header className="top-bar">
<span className="top-bar__title">Τραπέζια</span>
<span className="top-bar__user">{user?.username}</span>
<button className="icon-btn" onClick={handleLogout} title="Αποσύνδεση"></button>
</header>
{offline && <ConnectionBanner />}
<div className="filter-tabs">
{FILTERS.map(f => (
<button key={f} className={`filter-tab ${filter === f ? 'filter-tab--active' : ''}`} onClick={() => setFilter(f)}>
{FILTER_LABELS[f]}
</button>
))}
</div>
<div className="table-grid">
{filtered.map(t => (
<TableCard
key={t.id}
table={t}
order={getOrder(t.id)}
currentUserId={user?.id}
onClick={() => navigate(`/tables/${t.id}`)}
/>
))}
</div>
<button className="fab" onClick={load} title="Ανανέωση"></button>
</div>
)
}