import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import toast from 'react-hot-toast'
import client from '../api/client'
// ── Helpers ───────────────────────────────────────────────────────────────────
function fmt(n) {
if (n == null) return '—'
return n.toLocaleString('el-GR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €'
}
function fmtDate(iso) {
if (!iso) return '—'
return new Date(iso).toLocaleDateString('el-GR', { day: '2-digit', month: '2-digit', year: 'numeric' })
}
function fmtDateTime(iso) {
if (!iso) return '—'
return new Date(iso).toLocaleString('el-GR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })
}
const ORDER_STATUS_LABELS = {
closed: 'Κλειστή', paid: 'Πληρωμένη', open: 'Ανοιχτή',
partially_paid: 'Μερική πλ.', cancelled: 'Ακυρωμένη',
}
const ORDER_STATUS_COLORS = {
closed: '#6b7280', paid: '#16a34a', open: '#3b82f6',
partially_paid: '#d97706', cancelled: '#ef4444',
}
const inputStyle = {
width: '100%', padding: '8px 11px', border: '1px solid #e5e7eb',
borderRadius: 8, fontSize: 13.5, outline: 'none', fontFamily: 'inherit', background: 'white',
}
// ── Customer modal (create / edit) ───────────────────────────────────────────
const EMPTY_FORM = { name: '', nickname: '', phone: '', email: '', notes: '' }
function CustomerModal({ initial, onClose, onSave, isPending }) {
const [form, setForm] = useState(initial ?? EMPTY_FORM)
const f = (k, v) => setForm(p => ({ ...p, [k]: v }))
const isNew = !initial?.id
return (
{isNew ? 'Νέος Πελάτης' : 'Επεξεργασία Πελάτη'}
)
}
// ── Customer detail panel ─────────────────────────────────────────────────────
function CustomerDetail({ customer, onEdit, onClose }) {
const { data, isLoading } = useQuery({
queryKey: ['customer-orders', customer.id],
queryFn: () => client.get(`/api/customers/${customer.id}/orders`).then(r => r.data),
staleTime: 30_000,
})
const { data: tabsData = [] } = useQuery({
queryKey: ['customer-tabs', customer.id],
queryFn: () => client.get(`/api/tabs/customer/${customer.id}`).then(r => r.data),
staleTime: 30_000,
})
const openTab = tabsData.find(t => t.status === 'open')
const orders = data?.orders || []
return (
{/* Header */}
{customer.name}
{customer.nickname &&
«{customer.nickname}»
}
{/* Stats row */}
{[
{ label: 'Επισκέψεις', value: customer.visit_count },
{ label: 'Σύνολο εξόδων', value: fmt(customer.total_spent) },
{ label: 'Μέσος λογ.', value: customer.visit_count > 0 ? fmt(customer.total_spent / customer.visit_count) : '—' },
].map(s => (
))}
{/* Open tab banner */}
{openTab && (
Ανοιχτή Καρτέλα
{openTab.entries.length} χρεώσεις · πληρωμένο {fmt(openTab.total_paid)}
{fmt(openTab.balance)}
)}
{/* Contact info */}
{(customer.phone || customer.email || customer.notes) ? (
{customer.phone &&
📞{customer.phone}
}
{customer.email &&
✉{customer.email}
}
{customer.notes && (
📝 {customer.notes}
)}
) : (
Δεν υπάρχουν στοιχεία επικοινωνίας.
)}
{/* Visit history */}
Ιστορικό Επισκέψεων
{isLoading &&
Φόρτωση…
}
{!isLoading && orders.length === 0 && (
Δεν υπάρχουν επισκέψεις ακόμα.
)}
{orders.map(o => (
{o.table_name ? `Τραπέζι ${o.table_name}` : 'Χωρίς τραπέζι'}
{' '}
{ORDER_STATUS_LABELS[o.status] || o.status}
{fmtDateTime(o.opened_at)} · {o.item_count} αντικ.
{fmt(o.total)}
))}
)
}
// ── Main page ─────────────────────────────────────────────────────────────────
export default function CustomersPage() {
const qc = useQueryClient()
const [search, setSearch] = useState('')
const [selected, setSelected] = useState(null) // customer being viewed
const [modal, setModal] = useState(null) // null | { customer } | { customer: null }
const { data: customers = [], isLoading } = useQuery({
queryKey: ['customers', search],
queryFn: () => client.get('/api/customers/', { params: search ? { search } : {} }).then(r => r.data),
staleTime: 30_000,
})
const save = useMutation({
mutationFn: ({ form, id }) => id
? client.put(`/api/customers/${id}`, form)
: client.post('/api/customers/', form),
onSuccess: (res) => {
qc.invalidateQueries({ queryKey: ['customers'] })
if (selected && res.data?.id === selected.id) {
setSelected(res.data)
}
setModal(null)
toast.success('Αποθηκεύτηκε')
},
onError: () => toast.error('Σφάλμα αποθήκευσης'),
})
const deactivate = useMutation({
mutationFn: (id) => client.delete(`/api/customers/${id}`),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['customers'] })
if (selected?.id) setSelected(null)
toast.success('Αποενεργοποιήθηκε')
},
onError: () => toast.error('Σφάλμα'),
})
const activeCount = customers.length
return (
{/* Left: list */}
{/* Header */}
{/* List */}
{isLoading &&
Φόρτωση…
}
{!isLoading && customers.length === 0 && (
{search ? 'Δεν βρέθηκαν αποτελέσματα.' : 'Δεν υπάρχουν πελάτες ακόμα.'}
)}
{customers.map(c => (
))}
{/* Right: detail panel */}
{selected && (
setModal({ customer: selected })}
onClose={() => setSelected(null)}
/>
)}
{modal && (
setModal(null)}
isPending={save.isPending}
onSave={(form) => save.mutate({
form: {
name: form.name,
nickname: form.nickname || null,
phone: form.phone || null,
email: form.email || null,
notes: form.notes || null,
},
id: modal.customer?.id,
})}
/>
)}
)
}