diff --git a/manager_dashboard/src/pages/reports/operations/PrinterHistory.jsx b/manager_dashboard/src/pages/reports/operations/PrinterHistory.jsx index 56b5e20..536c470 100644 --- a/manager_dashboard/src/pages/reports/operations/PrinterHistory.jsx +++ b/manager_dashboard/src/pages/reports/operations/PrinterHistory.jsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { useQuery } from '@tanstack/react-query' -import { Printer, AlertCircle, Trophy } from 'lucide-react' +import { Printer, AlertCircle, Hash, Euro, FileText, X, ChevronDown, Loader2 } from 'lucide-react' +import toast from 'react-hot-toast' import client from '../../../api/client' import { FilterBar, FilterSelect, FilterDateInput, WorkDayDateToggle } from '../shared/FilterBar' import { Panel, DataTable, THead, TH, TR, TD, StatusBadge } from '../shared/TablePrimitives' @@ -8,17 +9,263 @@ import StatCard from '../shared/StatCard' import EmptyState from '../shared/EmptyState' import SkeletonTable from '../shared/SkeletonTable' import ExportButton from '../shared/ExportButton' -import { fmtNum, fmtDate, fmtTime, fmtDateTime } from '../shared/reportDesignTokens' +import { fmtNum, fmtEUR, fmtDate, fmtTime, fmtDateTime } from '../shared/reportDesignTokens' function today() { return new Date().toISOString().slice(0, 10) } function monthAgo() { const d = new Date(); d.setDate(d.getDate() - 30); return d.toISOString().slice(0, 10) } +// ─── Print Summary Modal ──────────────────────────────────────────────────── + +const PRINT_TYPES = [ + { + id: 'quick', + label: 'Γρήγορη Σύνοψη', + desc: 'Συγκεντρωτικά στατιστικά — λίγο χαρτί', + }, + { + id: 'orders', + label: 'Όλες οι Παραγγελίες', + desc: 'Κάθε παραγγελία με σύνολο, χωρίς ανάλυση ειδών', + }, + { + id: 'detailed', + label: 'Πλήρης Ανάλυση', + desc: 'Κάθε παραγγελία + όλα τα είδη — καταναλώνει πολύ χαρτί', + }, +] + +function PrintSummaryModal({ onClose, logs, stats, printers, printerF, queryParams }) { + const [printType, setPrintType] = useState('quick') + const [targetPrinter, setTargetPrinter] = useState('browser') + const [printing, setPrinting] = useState(false) + + const printerOptions = [ + { value: 'browser', label: 'Εκτύπωση μέσω browser' }, + ...printers.map(p => ({ value: String(p.id), label: p.name })), + ] + + async function handlePrint() { + if (targetPrinter !== 'browser') { + await handleThermalPrint() + return + } + handleBrowserPrint() + } + + async function handleThermalPrint() { + // Map frontend print types to backend modes + // quick/orders → simple, detailed → extensive + const mode = printType === 'detailed' ? 'extensive' : 'simple' + const printerTargetId = printerF === 'all' ? 0 : parseInt(printerF, 10) + + const fromDt = queryParams.from || (new Date(Date.now() - 30 * 86400000).toISOString().slice(0, 10) + 'T00:00:00') + const toDt = queryParams.to || (new Date().toISOString().slice(0, 10) + 'T23:59:59') + + setPrinting(true) + try { + await client.post('/api/reports/print/printer', { + printer_target_id: printerTargetId, + printer_id: parseInt(targetPrinter, 10), + mode, + from_dt: fromDt, + to_dt: toDt, + }) + toast.success('Η εκτύπωση στάλθηκε στον εκτυπωτή') + onClose() + } catch { + toast.error('Αποτυχία αποστολής στον εκτυπωτή') + } finally { + setPrinting(false) + } + } + + function handleBrowserPrint() { + const win = window.open('', '_blank', 'width=800,height=700') + if (!win) { onClose(); return } + + const styles = ` + + ` + + const filterLabel = printerF === 'all' + ? 'Όλοι οι Εκτυπωτές' + : (printers.find(p => String(p.id) === printerF)?.name || printerF) + + let body = '' + + if (printType === 'quick') { + const topItems = Object.entries(stats.itemCounts).sort((a, b) => b[1] - a[1]).slice(0, 3) + const topWaiters = Object.entries(stats.waiterCounts).sort((a, b) => b[1] - a[1]).slice(0, 3) + const avgItems = stats.total > 0 ? (stats.totalItemQty / stats.total).toFixed(1) : '—' + + body = ` +

Σύνοψη Εκτυπωτή

+
+
Εκτυπωτής${filterLabel}
+
Περίοδος${stats.periodLabel}
+
+

Στατιστικά

+
+
Συνολικές Εκτυπώσεις${fmtNum(stats.total)}
+
Αποτυχημένες Εκτυπώσεις${fmtNum(stats.failed)}
+ ${stats.totalAmount != null ? `
Συνολικό Ποσό Παραγγελιών${fmtEUR(stats.totalAmount)}
` : ''} +
Μέσος Αριθμός Ειδών ανά Παραγγελία${avgItems}
+
+

Top 3 Πιο Εκτυπωμένα Είδη

+
+
    + ${topItems.length ? topItems.map(([name, qty], i) => `
  1. ${i + 1}. ${name} — ${qty} τεμ.
  2. `).join('') : '
  3. '} +
+
+ ${topWaiters.length ? ` +

Εκτυπώσεις ανά Σερβιτόρο

+
+ ${topWaiters.map(([name, cnt]) => `
${name}${cnt}
`).join('')} +
` : ''} + ` + } else if (printType === 'orders') { + body = ` +

Λίστα Παραγγελιών

+
+
Εκτυπωτής${filterLabel}
+
Σύνολο Εγγραφών${logs.length}
+
+

Παραγγελίες

+ ${logs.map(j => ` +
+
+ #${j.order_id} · ${j.table || '—'} + ${fmtDateTime(j.printed_at)} +
+
Εκτυπωτής${j.printer_name}
+
Αποτέλεσμα${j.success ? '✓ Επιτυχία' : '✗ Αποτυχία'}
+ ${j.order_total != null ? `
Σύνολο${fmtEUR(j.order_total)}
` : ''} +
+ `).join('')} + ` + } else { + body = ` +

Πλήρης Ανάλυση Εκτυπώσεων

+
+
Εκτυπωτής${filterLabel}
+
Σύνολο Εγγραφών${logs.length}
+
+ ${logs.map(j => ` +
+
+ #${j.order_id} · ${j.table || '—'} + ${fmtDateTime(j.printed_at)} +
+
Εκτυπωτής${j.printer_name}
+
Αποτέλεσμα${j.success ? '✓ Επιτυχία' : '✗ Αποτυχία'}
+ ${(j.items || []).map(i => `
${i.name}×${i.quantity}
`).join('')} +
+ `).join('')} + ` + } + + win.document.write(`Ιστορικό Εκτυπωτή${styles}${body}`) + win.document.close() + win.focus() + win.print() + onClose() + } // end handleBrowserPrint + + return ( +
{ if (e.target === e.currentTarget) onClose() }} + > +
+
+

Εκτύπωση Σύνοψης

+ +
+ +
+
+
Τύπος Εκτύπωσης
+
+ {PRINT_TYPES.map(pt => ( + + ))} +
+
+ +
+
Εκτυπωτής Προορισμού
+
+ + +
+
+ +
+ + +
+
+
+
+ ) +} + +// ─── Main Component ───────────────────────────────────────────────────────── + export default function PrinterHistory() { const [mode, setMode] = useState('range') const [from, setFrom] = useState(monthAgo()) const [to, setTo] = useState(today()) const [businessDayId, setBusinessDayId] = useState('all') const [printerF, setPrinterF] = useState('all') + const [showPrintModal, setShowPrintModal] = useState(false) const { data: printersData } = useQuery({ queryKey: ['meta-printers'], queryFn: () => client.get('/api/reports/meta/printers').then(r => r.data), staleTime: 5 * 60 * 1000 }) const { data: bdData } = useQuery({ queryKey: ['business-days-list'], queryFn: () => client.get('/api/reports/business-days').then(r => r.data), staleTime: 60 * 1000 }) @@ -35,17 +282,35 @@ export default function PrinterHistory() { staleTime: 60 * 1000, }) - const printerOptions = [{ value: 'all', label: 'Όλοι οι Εκτυπωτές' }, ...((printersData?.printers || []).map(p => ({ value: String(p.id), label: p.name })))] + const printers = printersData?.printers || [] + const printerOptions = [{ value: 'all', label: 'Όλοι οι Εκτυπωτές' }, ...printers.map(p => ({ value: String(p.id), label: p.name }))] const bdOptions = [{ value: 'all', label: 'Όλες οι Εργάσιμες Μέρες' }, ...((bdData?.business_days || []).map(bd => ({ value: String(bd.id), label: `${fmtDate(bd.opened_at)} · ${fmtTime(bd.opened_at)}` })))] const logs = data?.logs || [] const total = data?.total || 0 const failed = data?.failed || 0 - // Find most printed item + // Derived stats for StatCards + Print modal const itemCounts = {} - logs.forEach(l => (l.items || []).forEach(i => { itemCounts[i.name] = (itemCounts[i.name] || 0) + i.quantity })) + const waiterCounts = {} + let totalItemQty = 0 + let totalAmount = null + + logs.forEach(l => { + if (l.waiter) waiterCounts[l.waiter] = (waiterCounts[l.waiter] || 0) + 1 + if (l.order_total != null) totalAmount = (totalAmount || 0) + l.order_total + ;(l.items || []).forEach(i => { + itemCounts[i.name] = (itemCounts[i.name] || 0) + i.quantity + totalItemQty += i.quantity + }) + }) + const topItem = Object.entries(itemCounts).sort((a, b) => b[1] - a[1])[0] + const periodLabel = mode === 'workday' + ? (bdOptions.find(o => o.value === businessDayId)?.label || 'Εργάσιμη Μέρα') + : `${from} → ${to}` + + const stats = { total, failed, totalAmount, itemCounts, waiterCounts, totalItemQty, periodLabel, topItem } if (isLoading) return
if (isError) return ( @@ -58,7 +323,16 @@ export default function PrinterHistory() { return (
+
+ + +
}> {mode === 'workday' ? ( @@ -73,10 +347,17 @@ export default function PrinterHistory() {
-
+
0 ? 'ελέγξτε τον εκτυπωτή' : 'όλα καλά'} icon={AlertCircle} /> - + +
@@ -115,6 +396,17 @@ export default function PrinterHistory() { )}
+ + {showPrintModal && ( + setShowPrintModal(false)} + logs={logs} + stats={stats} + printers={printers} + printerF={printerF} + queryParams={queryParams} + /> + )}
) }