feat: add payroll + tables served to shift detail and workday staff views
- business_day summary: waiter entries now include hourly_rate_snapshot, duration_hours, shift_pay (cumulative across day's shifts), tables_served (unique table count) - ShiftDetailModal: 4-KPI row replaced with 2×4 grid — Row 1: Διάρκεια | Μισθός/Ώρα | Πληρωμή Βάρδιας | Αρχικά Μετρητά Row 2: Σύνολο Παραγγελιών | Εισπράξεις | Προς Παράδοση | Εξ. Τραπεζιών - WorkdaySummaryModal WaitersTab: collapsed row uses ΠΑΡΑΓΓΕΛΙΕΣ | ΕΞ.ΤΡΑΠΕΖΙΩΝ | ΑΝΤΙΚ.ΕΙΣΠΡ. | ΕΙΣΠΡΑΞΗ; expanded grid is 2 rows: Row 1: ΕΝΑΡΞΗ | ΛΗΞΗ | ΜΙΣΘΟΣ/ΩΡΑ | ΜΙΣΘΟΣ (εως τώρα) Row 2: ΑΡΧΙΚΑ ΜΕΤΡΗΤΑ | ΤΑΜΕΙΟ ΤΩΡΑ | ΑΝΤΙΚ.ΕΙΣΠΡ. | ΕΞ.ΤΡΑΠΕΖΙΩΝ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -177,20 +177,42 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
|
||||
|
||||
{summary && (
|
||||
<>
|
||||
{/* KPI row */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 10, padding: '14px 24px', borderBottom: '1px solid #edeff1', flexShrink: 0 }}>
|
||||
{[
|
||||
{ label: 'Διάρκεια', value: fmtMins(summary.duration_minutes) },
|
||||
{ label: 'Αρχικά μετρητά', value: summary.starting_cash != null ? fmtEUR(summary.starting_cash) : '—' },
|
||||
{ label: 'Είσπραξη', value: fmtEUR(summary.total_collected), accent: '#2f9e5e' },
|
||||
{ label: 'Προς παράδοση', value: fmtEUR(summary.net_to_deliver), accent: '#3758c9' },
|
||||
].map(k => (
|
||||
<div key={k.label} style={{ background: '#f9fafb', borderRadius: 10, padding: '10px 12px', textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 10, fontWeight: 700, color: '#8a9099', textTransform: 'uppercase', letterSpacing: 0.5 }}>{k.label}</div>
|
||||
<div style={{ fontSize: 18, fontWeight: 700, color: k.accent || '#111315', fontFamily: 'ui-monospace,monospace', marginTop: 4 }}>{k.value}</div>
|
||||
{/* KPI grid — 2 rows × 4 cols */}
|
||||
{(() => {
|
||||
const totalOrders = summary.orders?.length ?? 0
|
||||
const tableSet = new Set(summary.orders?.map(o => o.table_name).filter(Boolean))
|
||||
const tablesServed = tableSet.size
|
||||
const durationHours = summary.duration_hours ?? (summary.duration_minutes != null ? summary.duration_minutes / 60 : null)
|
||||
const durationLabel = durationHours != null
|
||||
? (() => { const h = Math.floor(durationHours); const m = Math.round((durationHours - h) * 60); return h > 0 ? `${h}ω ${m}λ` : `${m}λ` })()
|
||||
: fmtMins(summary.duration_minutes)
|
||||
const row1 = [
|
||||
{ label: 'Διάρκεια Βάρδιας', value: durationLabel },
|
||||
{ label: 'Μισθός / Ώρα', value: summary.hourly_rate_snapshot != null ? fmtEUR(summary.hourly_rate_snapshot) : '—' },
|
||||
{ label: 'Πληρωμή Βάρδιας', value: summary.shift_pay != null ? fmtEUR(summary.shift_pay) : 'Δεν παρακολουθείται', accent: summary.shift_pay != null ? '#059669' : undefined },
|
||||
{ label: 'Αρχικά Μετρητά', value: summary.starting_cash != null ? fmtEUR(summary.starting_cash) : '—' },
|
||||
]
|
||||
const row2 = [
|
||||
{ label: 'Σύνολο Παραγγελιών', value: String(totalOrders) },
|
||||
{ label: 'Εισπράξεις', value: fmtEUR(summary.total_collected), accent: '#2f9e5e' },
|
||||
{ label: 'Προς Παράδοση', value: fmtEUR(summary.net_to_deliver), accent: '#3758c9' },
|
||||
{ label: 'Εξ. Τραπεζιών', value: String(tablesServed) },
|
||||
]
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: '14px 24px', borderBottom: '1px solid #edeff1', flexShrink: 0 }}>
|
||||
{[row1, row2].map((row, ri) => (
|
||||
<div key={ri} style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 6 }}>
|
||||
{row.map(k => (
|
||||
<div key={k.label} style={{ background: '#f9fafb', borderRadius: 10, padding: '10px 12px', textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 10, fontWeight: 700, color: '#8a9099', textTransform: 'uppercase', letterSpacing: 0.5 }}>{k.label}</div>
|
||||
<div style={{ fontSize: 16, fontWeight: 700, color: k.accent || '#111315', fontFamily: 'ui-monospace,monospace', marginTop: 4 }}>{k.value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* Colour legend */}
|
||||
<div style={{ display: 'flex', gap: 12, padding: '8px 24px', background: '#fafafa', borderBottom: '1px solid #edeff1', flexShrink: 0, flexWrap: 'wrap' }}>
|
||||
|
||||
Reference in New Issue
Block a user