Phase 3: scaffold Manager Dashboard — all pages, layout, routing
Includes: LoginPage (PIN pad), DashboardPage (30s polling table grid), OrderDetailPage (full actions), ProductsPage (CRUD + printer zone), WaitersPage (block/reset PIN/delete), TablesPage, ReportsPage (shift summary + order history + CSV export), SettingsPage (printers + test print + sysadmin lock/unlock). TailwindCSS, React Query, react-hot-toast. Docker Compose service on port 5174.
This commit is contained in:
14
manager_dashboard/src/components/ConfirmModal.jsx
Normal file
14
manager_dashboard/src/components/ConfirmModal.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
export default function ConfirmModal({ title, message, confirmLabel = 'Επιβεβαίωση', confirmClass = 'btn-danger', onConfirm, onCancel }) {
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-2xl shadow-xl w-full max-w-sm p-6 space-y-4">
|
||||
<h2 className="text-lg font-bold text-gray-800">{title}</h2>
|
||||
{message && <p className="text-gray-600 text-sm">{message}</p>}
|
||||
<div className="flex gap-3 pt-2">
|
||||
<button onClick={onCancel} className="flex-1 btn btn-secondary">Ακύρωση</button>
|
||||
<button onClick={onConfirm} className={`flex-1 btn ${confirmClass}`}>{confirmLabel}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
47
manager_dashboard/src/components/Sidebar.jsx
Normal file
47
manager_dashboard/src/components/Sidebar.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { useState } from 'react'
|
||||
|
||||
const NAV = [
|
||||
{ to: '/dashboard', icon: '📊', label: 'Dashboard' },
|
||||
{ to: '/tables', icon: '🪑', label: 'Τραπέζια' },
|
||||
{ to: '/products', icon: '📦', label: 'Προϊόντα' },
|
||||
{ to: '/waiters', icon: '👥', label: 'Σερβιτόροι' },
|
||||
{ to: '/reports', icon: '📋', label: 'Αναφορές' },
|
||||
{ to: '/settings', icon: '⚙️', label: 'Ρυθμίσεις' },
|
||||
]
|
||||
|
||||
export default function Sidebar() {
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
|
||||
return (
|
||||
<aside className={`flex flex-col bg-primary-800 text-white shrink-0 transition-all duration-200 ${collapsed ? 'w-16' : 'w-56'}`}>
|
||||
{/* Logo / collapse toggle */}
|
||||
<div className="flex items-center justify-between px-4 py-4 border-b border-primary-700">
|
||||
{!collapsed && <span className="font-bold text-lg tracking-wide">POS</span>}
|
||||
<button
|
||||
onClick={() => setCollapsed(c => !c)}
|
||||
className="p-1 rounded hover:bg-primary-700 transition-colors ml-auto"
|
||||
aria-label="Toggle sidebar"
|
||||
>
|
||||
{collapsed ? '→' : '←'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 py-4 space-y-1 px-2">
|
||||
{NAV.map(({ to, icon, label }) => (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={to}
|
||||
className={({ isActive }) =>
|
||||
`flex items-center gap-3 px-3 py-3 rounded-lg font-medium transition-colors min-h-[44px] ` +
|
||||
(isActive ? 'bg-primary-600 text-white' : 'text-primary-100 hover:bg-primary-700')
|
||||
}
|
||||
>
|
||||
<span className="text-xl leading-none shrink-0">{icon}</span>
|
||||
{!collapsed && <span className="text-sm">{label}</span>}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
18
manager_dashboard/src/components/StatusBadge.jsx
Normal file
18
manager_dashboard/src/components/StatusBadge.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
const MAP = {
|
||||
free: { label: 'Ελεύθερο', cls: 'bg-gray-100 text-gray-600' },
|
||||
open: { label: 'Ανοιχτό', cls: 'bg-green-100 text-green-700' },
|
||||
partially_paid: { label: 'Μερική πληρωμή', cls: 'bg-amber-100 text-amber-700' },
|
||||
paid: { label: 'Πληρώθηκε', cls: 'bg-blue-100 text-blue-700' },
|
||||
closed: { label: 'Κλειστό', cls: 'bg-gray-200 text-gray-500' },
|
||||
cancelled: { label: 'Ακυρώθηκε', cls: 'bg-red-100 text-red-600' },
|
||||
active: { label: 'Ενεργό', cls: 'bg-green-100 text-green-700' },
|
||||
}
|
||||
|
||||
export default function StatusBadge({ status }) {
|
||||
const { label, cls } = MAP[status] ?? { label: status, cls: 'bg-gray-100 text-gray-600' }
|
||||
return (
|
||||
<span className={`inline-block text-xs font-semibold px-2 py-0.5 rounded-full ${cls}`}>
|
||||
{label}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user