/* Relay — shared UI components */ const { PLATFORMS, STATUS, FLOW_LABELS } = window.RelayData; // ---------- helpers ---------- function fmtMoney(n) { return n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function minsAgo(ts, now) { return Math.max(0, Math.floor((now - ts) / 60000)); } function timeAgo(ts, now) { const m = minsAgo(ts, now); if (m < 1) return "şimdi"; if (m < 60) return m + " dk önce"; const h = Math.floor(m / 60); return h + "sa " + (m % 60) + "dk önce"; } function clockTime(ts) { return new Date(ts).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" }); } function itemCount(o) { return o.items.reduce((s, it) => s + it.qty, 0); } // ---------- atoms ---------- function PlatformBadge({ pid, withName, size = 26 }) { const p = PLATFORMS[pid]; return ( {p.short} {withName && {p.name}} ); } // Prep timer: counts elapsed since accepted; flags late vs prepMins target function StatusPill({ status, type }) { const s = STATUS[status]; const label = type ? FLOW_LABELS[type][status] || s.label : s.label; return ( {label} ); } function TypeIcon({ type, size = 14 }) { return ( {type === "food" ? : } ); } function PrepTimer({ order, now, compact }) { if (order.status === "completed" || order.status === "cancelled") return null; const base = order.acceptedAt || order.placedAt; const elapsedMs = Math.max(0, now - base); const elapsedMin = Math.floor(elapsedMs / 60000); const elapsedSec = Math.floor(elapsedMs / 1000) % 60; const late = elapsedMin >= order.prepMins; const color = late ? "var(--cancelled)" : order.status === "new" ? "var(--new)" : "var(--text-2)"; const mm = String(elapsedMin).padStart(2, "0"); const ss = String(elapsedSec).padStart(2, "0"); return ( {mm}:{ss} {late && !compact && GECİKTİ} ); } function Btn({ kind = "default", children, onClick, small, full, style, title }) { const base = { display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6, fontFamily: "var(--font)", fontWeight: 600, fontSize: small ? 12.5 : 13.5, padding: small ? "6px 11px" : "9px 14px", borderRadius: "var(--radius-sm)", border: "1px solid transparent", width: full ? "100%" : "auto", transition: "all .12s ease", whiteSpace: "nowrap", ...style, }; const kinds = { default: { background: "var(--surface)", color: "var(--text)", borderColor: "var(--border-strong)" }, primary: { background: "var(--graphite)", color: "#fff" }, accept: { background: "var(--ready)", color: "#fff" }, reject: { background: "var(--surface)", color: "var(--cancelled)", borderColor: "#f0c9c9" }, ghost: { background: "transparent", color: "var(--text-2)" }, }; return ( ); } // next-step CTA based on status function actionFor(order) { const f = FLOW_LABELS[order.type]; switch (order.status) { case "preparing": return { to: "ready", label: f.readyCta }; case "ready": return { to: "completed", label: f.doneCta }; default: return null; } } // ---------- Order Card (grid + kanban) ---------- function OrderCard({ order, now, onOpen, onAccept, onReject, onAdvance, dense }) { const isNew = order.status === "new"; const act = actionFor(order); const shown = order.items.slice(0, dense ? 2 : 3); const extra = order.items.length - shown.length; return (
onOpen(order)} className={order._flash ? "flash-new" : ""} style={{ background: "var(--surface)", border: "1px solid var(--border)", borderLeft: `3px solid ${isNew ? "var(--new)" : "transparent"}`, borderRadius: "var(--radius)", padding: dense ? "11px 13px" : "13px 15px", boxShadow: "var(--shadow-sm)", cursor: "pointer", transition: "box-shadow .12s, transform .12s", display: "flex", flexDirection: "column", gap: dense ? 8 : 10, animation: isNew ? "pulseRing 2.2s ease-out 1" : "none", }} onMouseEnter={(e) => { e.currentTarget.style.boxShadow = "var(--shadow-md)"; e.currentTarget.style.transform = "translateY(-1px)"; }} onMouseLeave={(e) => { e.currentTarget.style.boxShadow = "var(--shadow-sm)"; e.currentTarget.style.transform = "none"; }}> {/* header */}
{order.channelRef}
{/* customer + meta */}
{order.customer}
{order.fulfillment} · {order.area}
{isNew ? : }
{/* items */}
{shown.map((it, i) => (
{it.qty}× {it.name}
))} {extra > 0 &&
+{extra} ürün daha
}
{/* footer */}
{order.currency} {fmtMoney(order.total)} {timeAgo(order.placedAt, now)}
{!isNew && !act && order.status === "completed" && }
{/* actions */} {isNew && (
onReject(order)}>Reddet onAccept(order)}>Kabul Et
)} {act && ( onAdvance(order)}>{act.label} )}
); } // ---------- Order Row (list view) ---------- function OrderRow({ order, now, onOpen, onAccept, onReject, onAdvance, dense }) { const isNew = order.status === "new"; const act = actionFor(order); const pad = dense ? "8px 16px" : "12px 16px"; return (
onOpen(order)} className={order._flash ? "flash-new" : ""} style={{ display: "grid", gridTemplateColumns: "150px 110px 1fr 70px 130px 140px 210px", alignItems: "center", gap: 14, padding: pad, background: "var(--surface)", borderBottom: "1px solid var(--border)", borderLeft: `3px solid ${isNew ? "var(--new)" : "transparent"}`, cursor: "pointer", transition: "background .1s", }} onMouseEnter={(e) => { e.currentTarget.style.background = "var(--surface-2)"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "var(--surface)"; }}> {order.channelRef}
{order.customer}
{order.fulfillment} · {order.area}
{itemCount(order)} ✕ {order.currency} {fmtMoney(order.total)}
{isNew ? : } {clockTime(order.placedAt)}
{isNew && <> onReject(order)}> onAccept(order)}>Kabul Et } {act && onAdvance(order)}>{act.label}} {!isNew && !act && {order.status === "completed" ? "Tamamlandı" : ""}}
); } // ---------- KPI tile ---------- function Kpi({ label, value, sub, accent, icon }) { return (
{icon}{label}
{value} {sub && {sub}}
); } Object.assign(window, { fmtMoney, minsAgo, timeAgo, clockTime, itemCount, actionFor, PlatformBadge, StatusPill, TypeIcon, PrepTimer, Btn, OrderCard, OrderRow, Kpi, });