feat: ShiftDetailModal rework, Today cancellation events, shift details icon button

- Backend: add cancellation_events (row count) to _enrich_shift and shift summary
- Backend: add cancellation_events field to current business day endpoint
- Today tab: hero number now shows cancellation_events (distinct cancel actions),
  sub-label shows 'X παραγγελίες / Y είδη'
- ShiftsOverview: Λεπτομέρειες button is now icon-only (Eye, light blue) matching
  the delete button style
- ShiftDetailModal: modal widened to 1080px; ΠΑΡΑΔΟΘΗΚΑΝ KPI replaced with
  Ακυρώσεις showing 'X events / Y items'; all filter labels renamed (Πλήρης
  Παραγγελία, Μόνο Πληρώθηκε, Μόνο Παρήγγειλε, Ανοιχτό ακόμη); new Ακυρώθηκε
  filter added; cancelled items shown in red, Πληρώθηκε line hidden for cancelled;
  legend updated; classify() handles cancelled status before other checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 17:48:30 +03:00
parent 8533a1452b
commit 02ec1aa28f
5 changed files with 120 additions and 65 deletions

View File

@@ -1263,12 +1263,17 @@ def current_business_day(
# Count cancelled items (individual items cancelled, across all orders in this day) # Count cancelled items (individual items cancelled, across all orders in this day)
all_order_ids = [o.id for o in orders] all_order_ids = [o.id for o in orders]
cancelled_items_qty = 0 cancelled_items_qty = 0
cancellation_events = 0
if all_order_ids: if all_order_ids:
from sqlalchemy import func as sqlfunc from sqlalchemy import func as sqlfunc
cancelled_items_qty = db.query(sqlfunc.sum(OrderItem.quantity)).filter( cancelled_items_qty = db.query(sqlfunc.sum(OrderItem.quantity)).filter(
OrderItem.order_id.in_(all_order_ids), OrderItem.order_id.in_(all_order_ids),
OrderItem.status == "cancelled", OrderItem.status == "cancelled",
).scalar() or 0 ).scalar() or 0
cancellation_events = db.query(sqlfunc.count(OrderItem.id)).filter(
OrderItem.order_id.in_(all_order_ids),
OrderItem.status == "cancelled",
).scalar() or 0
return { return {
"business_day": { "business_day": {
@@ -1283,6 +1288,7 @@ def current_business_day(
"active_waiters": len(active_shifts), "active_waiters": len(active_shifts),
"cancellations": len(cancelled_orders), "cancellations": len(cancelled_orders),
"cancelled_items": int(cancelled_items_qty), "cancelled_items": int(cancelled_items_qty),
"cancellation_events": int(cancellation_events),
"top_product": top_product, "top_product": top_product,
} }
} }

View File

@@ -84,6 +84,7 @@ def _enrich_shift(shift: WaiterShift, db: Session) -> dict:
if shift.ended_at: if shift.ended_at:
cancelled_q = cancelled_q.filter(OrderItem.added_at <= shift.ended_at) cancelled_q = cancelled_q.filter(OrderItem.added_at <= shift.ended_at)
cancelled_rows = cancelled_q.all() cancelled_rows = cancelled_q.all()
cancellation_events = len(cancelled_rows)
cancellations = sum(r.quantity for r in cancelled_rows) cancellations = sum(r.quantity for r in cancelled_rows)
cancellation_value = round(sum(r.unit_price * r.quantity for r in cancelled_rows), 2) cancellation_value = round(sum(r.unit_price * r.quantity for r in cancelled_rows), 2)
return { return {
@@ -101,6 +102,7 @@ def _enrich_shift(shift: WaiterShift, db: Session) -> dict:
"hourly_rate_snapshot": shift.hourly_rate_snapshot, "hourly_rate_snapshot": shift.hourly_rate_snapshot,
"duration_hours": pay_data["duration_hours"], "duration_hours": pay_data["duration_hours"],
"shift_pay": pay_data["shift_pay"], "shift_pay": pay_data["shift_pay"],
"cancellation_events": cancellation_events,
"cancellations": cancellations, "cancellations": cancellations,
"cancellation_value": cancellation_value, "cancellation_value": cancellation_value,
# Phase 2E # Phase 2E
@@ -417,7 +419,7 @@ def get_shift_summary(
waiter_id = shift.waiter_id waiter_id = shift.waiter_id
# Collect all relevant items: paid in this shift OR added by this waiter # Collect all relevant items: paid in this shift OR added by this waiter (any status)
paid_items = db.query(OrderItem).options( paid_items = db.query(OrderItem).options(
joinedload(OrderItem.product), joinedload(OrderItem.product),
joinedload(OrderItem.order), joinedload(OrderItem.order),
@@ -446,6 +448,22 @@ def get_shift_summary(
items_by_id[item.id] = item items_by_id[item.id] = item
all_items = list(items_by_id.values()) all_items = list(items_by_id.values())
# Also include items cancelled within this shift window (may not be in ordered_items
# if they were cancelled after the shift ended or deduped out)
cancelled_items_for_summary = db.query(OrderItem).options(
joinedload(OrderItem.product),
joinedload(OrderItem.order),
).filter(
OrderItem.added_by == waiter_id,
OrderItem.status == "cancelled",
OrderItem.added_at >= shift.started_at,
OrderItem.added_at <= upper_bound,
).all()
for item in cancelled_items_for_summary:
if item.id not in items_by_id:
items_by_id[item.id] = item
all_items = list(items_by_id.values())
# Build lookup maps # Build lookup maps
all_waiter_ids = set() all_waiter_ids = set()
for item in all_items: for item in all_items:

View File

@@ -121,8 +121,12 @@ export default function Today() {
)} )}
<StatCard <StatCard
label="Ακυρώσεις" label="Ακυρώσεις"
value={fmtNum(bd.cancellations)} value={fmtNum(bd.cancellation_events ?? bd.cancellations)}
sub={bd.cancelled_items > 0 ? `${fmtNum(bd.cancelled_items)} είδη` : 'παραγγελίες'} sub={
(bd.cancellations > 0 || bd.cancelled_items > 0)
? `${fmtNum(bd.cancellations)} παραγγελίες / ${fmtNum(bd.cancelled_items)} είδη`
: 'καμία ακύρωση'
}
icon={XCircle} icon={XCircle}
/> />
{bd.total_cost > 0 && ( {bd.total_cost > 0 && (

View File

@@ -5,30 +5,41 @@ import client from '../../../api/client'
import { fmtEUR, fmtDateTime, fmtTime, fmtDate } from './reportDesignTokens' import { fmtEUR, fmtDateTime, fmtTime, fmtDate } from './reportDesignTokens'
// ── colour rules ──────────────────────────────────────────────────────────── // ── colour rules ────────────────────────────────────────────────────────────
// Given the shift's waiter_id and one item, return { key, label, colors }
function classify(item, shiftWaiterId) { function classify(item, shiftWaiterId) {
if (item.status === 'cancelled')
return { key: 'cancelled', label: 'Ακυρώθηκε', dot: '#dc2626', bg: '#fef2f2', border: '#fecaca', text: '#991b1b' }
const orderedByMe = item.added_by_id === shiftWaiterId const orderedByMe = item.added_by_id === shiftWaiterId
const paidToMe = item.paid_by_id === shiftWaiterId const paidToMe = item.paid_by_id === shiftWaiterId
const isPaid = item.status === 'paid' const isPaid = item.status === 'paid'
if (orderedByMe && paidToMe && isPaid) if (orderedByMe && paidToMe && isPaid)
return { key: 'both', label: 'Παρήγγειλε + Πληρώθηκε', dot: '#16a34a', bg: '#f0fdf4', border: '#bbf7d0', text: '#15803d' } return { key: 'both', label: λήρης Παραγγελία', dot: '#16a34a', bg: '#f0fdf4', border: '#bbf7d0', text: '#15803d' }
if (!orderedByMe && paidToMe && isPaid) if (!orderedByMe && paidToMe && isPaid)
return { key: 'paid', label: 'Πληρώθηκε (άλλος παρήγγειλε)', dot: '#2563eb', bg: '#eff6ff', border: '#bfdbfe', text: '#1d4ed8' } return { key: 'paid', label: 'Μόνο Πληρώθηκε', dot: '#2563eb', bg: '#eff6ff', border: '#bfdbfe', text: '#1d4ed8' }
if (orderedByMe && isPaid && !paidToMe) if (orderedByMe && isPaid && !paidToMe)
return { key: 'ordered', label: 'Παρήγγειλε (Πληρώθηκε άλλος)', dot: '#ca8a04', bg: '#fefce8', border: '#fef08a', text: '#854d0e' } return { key: 'ordered', label: 'Μόνο Παρήγγειλε', dot: '#ca8a04', bg: '#fefce8', border: '#fef08a', text: '#854d0e' }
if (orderedByMe && !isPaid) if (orderedByMe && !isPaid)
return { key: 'unpaid', label: 'Παρήγγειλε (απλήρωτο)', dot: '#ea580c', bg: '#fff7ed', border: '#fed7aa', text: '#c2410c' } return { key: 'unpaid', label: 'Ανοιχτό ακόμη', dot: '#ea580c', bg: '#fff7ed', border: '#fed7aa', text: '#c2410c' }
// fallback: paid to me but status not 'paid' — data anomaly return { key: 'anomaly', label: 'Πρόβλημα', dot: '#dc2626', bg: '#fef2f2', border: '#fecaca', text: '#b91c1c' }
return { key: 'anomaly', label: 'Ανωμαλία δεδομένων', dot: '#dc2626', bg: '#fef2f2', border: '#fecaca', text: '#b91c1c' }
} }
const FILTER_OPTIONS = [ const FILTER_OPTIONS = [
{ key: 'all', label: 'Όλα' }, { key: 'all', label: 'Όλα' },
{ key: 'both', label: 'Παρήγγειλε + Πληρώθηκε' }, { key: 'both', label: λήρης Παραγγελία' },
{ key: 'paid', label: 'Πληρώθηκε (ξένη παραγγελία)' }, { key: 'paid', label: 'Μόνο Πληρώθηκε' },
{ key: 'ordered', label: 'Παρήγγειλε (Πληρώθηκε άλλος)' }, { key: 'ordered', label: 'Μόνο Παρήγγειλε' },
{ key: 'unpaid', label: 'Απλήρωτα' }, { key: 'unpaid', label: 'Ανοιχτό ακόμη' },
{ key: 'cancelled', label: 'Ακυρώθηκε' },
{ key: 'anomaly', label: 'Πρόβλημα' },
]
const LEGEND = [
{ dot: '#16a34a', label: 'Πλήρης Παραγγελία' },
{ dot: '#2563eb', label: 'Μόνο Πληρώθηκε' },
{ dot: '#ca8a04', label: 'Μόνο Παρήγγειλε' },
{ dot: '#ea580c', label: 'Ανοιχτό ακόμη' },
{ dot: '#dc2626', label: 'Ακυρώθηκε / Πρόβλημα' },
] ]
function fmtMins(mins) { function fmtMins(mins) {
@@ -40,31 +51,35 @@ function fmtMins(mins) {
function ItemRow({ item, shiftWaiterId }) { function ItemRow({ item, shiftWaiterId }) {
const c = classify(item, shiftWaiterId) const c = classify(item, shiftWaiterId)
const isCancelled = item.status === 'cancelled'
return ( return (
<div style={{ <div style={{
display: 'flex', alignItems: 'flex-start', gap: 10, padding: '7px 14px', display: 'flex', alignItems: 'flex-start', gap: 10, padding: '7px 14px',
borderTop: '1px solid #f4f4f2', background: c.bg, borderTop: '1px solid #f4f4f2', background: c.bg,
}}> }}>
{/* colour dot */}
<div style={{ width: 8, height: 8, borderRadius: '50%', background: c.dot, flexShrink: 0, marginTop: 5 }} /> <div style={{ width: 8, height: 8, borderRadius: '50%', background: c.dot, flexShrink: 0, marginTop: 5 }} />
<div style={{ flex: 1, minWidth: 0 }}> <div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
<span style={{ fontSize: 13, fontWeight: 600, color: '#111315' }}> <span style={{ fontSize: 13, fontWeight: 600, color: isCancelled ? '#991b1b' : '#111315' }}>
{item.product_name} {item.product_name}
<span style={{ fontWeight: 400, color: '#8a9099', marginLeft: 4 }}>×{item.quantity}</span> <span style={{ fontWeight: 400, color: '#8a9099', marginLeft: 4 }}>×{item.quantity}</span>
</span> </span>
<span style={{ fontSize: 13, fontWeight: 700, color: '#111315', flexShrink: 0 }}>{fmtEUR(item.subtotal)}</span> <span style={{ fontSize: 13, fontWeight: 700, color: isCancelled ? '#b91c1c' : '#111315', flexShrink: 0, textDecoration: isCancelled ? 'line-through' : 'none' }}>
{fmtEUR(item.subtotal)}
</span>
</div> </div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '2px 12px', marginTop: 2 }}> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '2px 12px', marginTop: 2 }}>
<span style={{ fontSize: 11, color: '#5a6169' }}> <span style={{ fontSize: 11, color: '#5a6169' }}>
Παρήγγειλε: <span style={{ fontWeight: 600, color: '#374151' }}>{item.added_by_name ?? '—'}</span> Παρήγγειλε: <span style={{ fontWeight: 600, color: '#374151' }}>{item.added_by_name ?? '—'}</span>
{item.added_at ? <span style={{ color: '#9ca3af' }}> · {fmtDateTime(item.added_at)}</span> : null} {item.added_at ? <span style={{ color: '#9ca3af' }}> · {fmtDateTime(item.added_at)}</span> : null}
</span> </span>
{!isCancelled && (
<span style={{ fontSize: 11, color: '#5a6169' }}> <span style={{ fontSize: 11, color: '#5a6169' }}>
Πληρώθηκε: <span style={{ fontWeight: 600, color: item.paid_by_name ? '#374151' : '#9ca3af' }}>{item.paid_by_name ?? '—'}</span> Πληρώθηκε: <span style={{ fontWeight: 600, color: item.paid_by_name ? '#374151' : '#9ca3af' }}>{item.paid_by_name ?? '—'}</span>
{item.paid_at ? <span style={{ color: '#9ca3af' }}> · {fmtDateTime(item.paid_at)}</span> : null} {item.paid_at ? <span style={{ color: '#9ca3af' }}> · {fmtDateTime(item.paid_at)}</span> : null}
{item.payment_method ? <span style={{ color: '#9ca3af' }}> ({item.payment_method})</span> : null} {item.payment_method ? <span style={{ color: '#9ca3af' }}> ({item.payment_method})</span> : null}
</span> </span>
)}
</div> </div>
<div style={{ marginTop: 2 }}> <div style={{ marginTop: 2 }}>
<span style={{ <span style={{
@@ -90,7 +105,6 @@ function OrderGroup({ order, shiftWaiterId, activeFilter }) {
return ( return (
<div style={{ borderRadius: 10, border: '1px solid #edeff1', marginBottom: 8, overflow: 'hidden' }}> <div style={{ borderRadius: 10, border: '1px solid #edeff1', marginBottom: 8, overflow: 'hidden' }}>
{/* Order header */}
<div style={{ padding: '8px 14px', background: '#f9fafb', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 4 }}> <div style={{ padding: '8px 14px', background: '#f9fafb', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 4 }}>
<div style={{ fontSize: 12, fontWeight: 600, color: '#374151' }}> <div style={{ fontSize: 12, fontWeight: 600, color: '#374151' }}>
Παραγγελία #{order.order_id} Παραγγελία #{order.order_id}
@@ -130,8 +144,7 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
staleTime: 30 * 1000, staleTime: 30 * 1000,
}) })
// Count items per filter key for badge counts const counts = { all: 0, both: 0, paid: 0, ordered: 0, unpaid: 0, cancelled: 0, anomaly: 0 }
const counts = { all: 0, both: 0, paid: 0, ordered: 0, unpaid: 0 }
const waiterIdToUse = shiftWaiterId ?? summary?.waiter_id const waiterIdToUse = shiftWaiterId ?? summary?.waiter_id
if (summary?.orders) { if (summary?.orders) {
for (const o of summary.orders) { for (const o of summary.orders) {
@@ -151,7 +164,7 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
onClick={onClose} onClick={onClose}
> >
<div style={{ <div style={{
background: 'white', borderRadius: 20, width: '100%', maxWidth: 900, background: 'white', borderRadius: 20, width: '100%', maxWidth: 1080,
maxHeight: '92vh', display: 'flex', flexDirection: 'column', maxHeight: '92vh', display: 'flex', flexDirection: 'column',
boxShadow: '0 24px 64px rgba(0,0,0,0.25)', boxShadow: '0 24px 64px rgba(0,0,0,0.25)',
}} }}
@@ -180,8 +193,6 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
{/* KPI grid — 2 rows × 4 cols */} {/* KPI grid — 2 rows × 4 cols */}
{(() => { {(() => {
const totalOrders = summary.orders?.length ?? 0 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 durationHours = summary.duration_hours ?? (summary.duration_minutes != null ? summary.duration_minutes / 60 : null)
const durationLabel = durationHours != null const durationLabel = durationHours != null
? (() => { const h = Math.floor(durationHours); const m = Math.round((durationHours - h) * 60); return h > 0 ? `${h}ω ${m}λ` : `${m}λ` })() ? (() => { const h = Math.floor(durationHours); const m = Math.round((durationHours - h) * 60); return h > 0 ? `${h}ω ${m}λ` : `${m}λ` })()
@@ -204,10 +215,19 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
? undefined ? undefined
: Math.abs(discrepancy) < 0.005 ? '#16a34a' : '#dc2626' : Math.abs(discrepancy) < 0.005 ? '#16a34a' : '#dc2626'
const cancelEvents = summary.cancellation_events ?? 0
const cancelItemsQty = summary.orders
? summary.orders.reduce((s, o) => s + o.items.filter(i => i.status === 'cancelled').reduce((ss, i) => ss + i.quantity, 0), 0)
: 0
const row2 = [ const row2 = [
{ label: 'Σύνολο Παραγγελιών', value: String(totalOrders) }, { label: 'Σύνολο Παραγγελιών', value: String(totalOrders) },
{ label: 'Εισπράξεις', value: fmtEUR(summary.total_collected), accent: '#2f9e5e' }, { label: 'Εισπράξεις', value: fmtEUR(summary.total_collected), accent: '#2f9e5e' },
{ label: 'Παραδόθηκαν', value: summary.counted_cash_end != null ? fmtEUR(summary.counted_cash_end) : '—', accent: '#3758c9' }, {
label: 'Ακυρώσεις',
value: cancelEvents > 0 ? `${cancelEvents} / ${cancelItemsQty} είδη` : '—',
accent: cancelEvents > 0 ? '#dc2626' : undefined,
},
{ label: 'Ταμείο', value: discLabel, accent: discAccent }, { label: 'Ταμείο', value: discLabel, accent: discAccent },
] ]
return ( return (
@@ -228,14 +248,8 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
{/* Colour legend */} {/* Colour legend */}
<div style={{ display: 'flex', gap: 12, padding: '8px 24px', background: '#fafafa', borderBottom: '1px solid #edeff1', flexShrink: 0, flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: 12, padding: '8px 24px', background: '#fafafa', borderBottom: '1px solid #edeff1', flexShrink: 0, flexWrap: 'wrap' }}>
{[ {LEGEND.map(l => (
{ dot: '#16a34a', label: 'Παρήγγειλε + Πληρώθηκε' }, <div key={l.label} style={{ display: 'flex', alignItems: 'center', gap: 5, fontSize: 11, color: '#5a6169' }}>
{ dot: '#2563eb', label: 'Πληρώθηκε (παρήγγηλε άλλος)' },
{ dot: '#ca8a04', label: 'Παρήγγειλε (Πληρώθηκε άλλος)' },
{ dot: '#ea580c', label: 'Παρήγγειλε (απλήρωτο)' },
{ dot: '#dc2626', label: 'Πρόβλημα Δεδομένων' },
].map(l => (
<div key={l.dot} style={{ display: 'flex', alignItems: 'center', gap: 5, fontSize: 11, color: '#5a6169' }}>
<div style={{ width: 8, height: 8, borderRadius: '50%', background: l.dot, flexShrink: 0 }} /> <div style={{ width: 8, height: 8, borderRadius: '50%', background: l.dot, flexShrink: 0 }} />
{l.label} {l.label}
</div> </div>
@@ -244,29 +258,41 @@ export default function ShiftDetailModal({ shiftId, shiftWaiterId, onClose }) {
{/* Filter bar */} {/* Filter bar */}
<div style={{ display: 'flex', gap: 6, padding: '10px 24px', borderBottom: '1px solid #edeff1', flexShrink: 0, overflowX: 'auto' }}> <div style={{ display: 'flex', gap: 6, padding: '10px 24px', borderBottom: '1px solid #edeff1', flexShrink: 0, overflowX: 'auto' }}>
{FILTER_OPTIONS.map(opt => ( {FILTER_OPTIONS.map(opt => {
const isCancelOpt = opt.key === 'cancelled'
const isActive = activeFilter === opt.key
return (
<button <button
key={opt.key} key={opt.key}
onClick={() => setActiveFilter(opt.key)} onClick={() => setActiveFilter(opt.key)}
style={{ style={{
padding: '4px 12px', borderRadius: 20, fontSize: 12, fontWeight: 600, cursor: 'pointer', whiteSpace: 'nowrap', padding: '4px 12px', borderRadius: 20, fontSize: 12, fontWeight: 600, cursor: 'pointer', whiteSpace: 'nowrap',
border: activeFilter === opt.key ? '1.5px solid #3758c9' : '1.5px solid #e5e7eb', border: isActive
background: activeFilter === opt.key ? '#eff6ff' : 'white', ? isCancelOpt ? '1.5px solid #dc2626' : '1.5px solid #3758c9'
color: activeFilter === opt.key ? '#3758c9' : '#5a6169', : '1.5px solid #e5e7eb',
background: isActive
? isCancelOpt ? '#fef2f2' : '#eff6ff'
: 'white',
color: isActive
? isCancelOpt ? '#991b1b' : '#3758c9'
: '#5a6169',
}} }}
> >
{opt.label} {opt.label}
{counts[opt.key] > 0 && ( {counts[opt.key] > 0 && (
<span style={{ <span style={{
marginLeft: 5, padding: '1px 5px', borderRadius: 8, fontSize: 10, marginLeft: 5, padding: '1px 5px', borderRadius: 8, fontSize: 10,
background: activeFilter === opt.key ? '#3758c9' : '#e5e7eb', background: isActive
color: activeFilter === opt.key ? 'white' : '#5a6169', ? isCancelOpt ? '#dc2626' : '#3758c9'
: '#e5e7eb',
color: isActive ? 'white' : '#5a6169',
}}> }}>
{counts[opt.key]} {counts[opt.key]}
</span> </span>
)} )}
</button> </button>
))} )
})}
</div> </div>
{/* Orders + items */} {/* Orders + items */}

View File

@@ -1,6 +1,6 @@
import { useState } from 'react' import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { UserRoundX, ChevronRight, ExternalLink, Trash2 } from 'lucide-react' import { UserRoundX, ChevronRight, ExternalLink, Trash2, Eye } from 'lucide-react'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import ShiftDetailModal from '../shared/ShiftDetailModal' import ShiftDetailModal from '../shared/ShiftDetailModal'
import DeleteConfirmModal from '../../../ui/DeleteConfirmModal' import DeleteConfirmModal from '../../../ui/DeleteConfirmModal'
@@ -194,9 +194,10 @@ export default function ShiftsOverview({ onNavigate } = {}) {
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-2">
<button <button
onClick={e => { e.stopPropagation(); setDetailShift({ id: s.id, waiter_id: s.waiter_id }) }} onClick={e => { e.stopPropagation(); setDetailShift({ id: s.id, waiter_id: s.waiter_id }) }}
className="rounded border border-slate-200 bg-white px-2 py-0.5 text-[11px] font-medium text-slate-600 hover:bg-slate-50" className="rounded border border-sky-200 bg-white p-0.5 text-sky-400 hover:bg-sky-50 hover:text-sky-600"
title="Λεπτομέρειες βάρδιας"
> >
Λεπτομέρειες <Eye size={13} />
</button> </button>
{!s.is_active && ( {!s.is_active && (
<button <button