/* @jsx React.createElement */ const { useState, useEffect, useRef, useMemo } = React; /* ============================================================ Yappari — EVENTS (realtime timeline) ============================================================ */ function EventsView({ machines, selectedMachine }) { const Y = window.YAPPARI; const ET = Y.EVENT_TYPES; const [events, setEvents] = useState(() => Y.buildEvents(38)); const [typeFilter, setTypeFilter] = useState('all'); const [machFilter, setMachFilter] = useState(selectedMachine || 'all'); const [paused, setPaused] = useState(false); const idRef = useRef(700000); useEffect(() => { setMachFilter(selectedMachine || 'all'); }, [selectedMachine]); useEffect(() => { if (paused) return; const types = ['door', 'refill', 'dispense_fail', 'temp_alert', 'error', 'door']; const id = setInterval(() => { const m = Y.pick(machines); const t = Y.pick(types); idRef.current++; const ev = { id: 'ev' + idRef.current, machineId: m.id, machineName: m.name, type: t, minsAgo: 0, desc: Y.EVENT_TEMPLATES[t](m), fresh: true }; setEvents(prev => [ev, ...prev.map(e => ({ ...e, fresh: false }))].slice(0, 80)); }, 4500); return () => clearInterval(id); }, [paused, machines]); const shown = events.filter(e => (typeFilter === 'all' || e.type === typeFilter) && (machFilter === 'all' || e.machineId === machFilter)); const relT = (m) => m === 0 ? 'baru saja' : m < 60 ? m + ' mnt lalu' : Math.floor(m / 60) + ' jam lalu'; return React.createElement('div', { className: 'view' }, React.createElement('div', { className: 'card card-pad', style: { marginBottom: 'var(--sp-5)', display: 'flex', gap: 'var(--sp-4)', alignItems: 'center', flexWrap: 'wrap' } }, React.createElement('div', { className: 'row gap-2', style: { color: 'var(--fg-2)', fontWeight: 700, fontSize: 13 } }, React.createElement(Icon, { name: 'filter', size: 16 }), 'Filter'), React.createElement('select', { className: 'select-native', style: { width: 'auto', minWidth: 180 }, value: machFilter, onChange: e => setMachFilter(e.target.value) }, React.createElement('option', { value: 'all' }, 'Semua mesin'), machines.map(m => React.createElement('option', { key: m.id, value: m.id }, m.name))), React.createElement('div', { className: 'row gap-2', style: { flexWrap: 'wrap' } }, React.createElement('button', { className: 'chip ' + (typeFilter === 'all' ? 'active' : ''), onClick: () => setTypeFilter('all') }, 'Semua tipe'), Object.entries(ET).filter(([k]) => k !== 'online' && k !== 'offline').map(([k, v]) => React.createElement('button', { key: k, className: 'chip ' + (typeFilter === k ? 'active' : ''), onClick: () => setTypeFilter(k), style: typeFilter === k ? null : { color: v.color } }, React.createElement('span', { style: { width: 8, height: 8, borderRadius: '50%', background: typeFilter === k ? '#fff' : v.color } }), v.label))), React.createElement('div', { style: { flex: 1 } }), React.createElement('button', { className: 'btn btn-ghost btn-sm', onClick: () => setPaused(p => !p) }, React.createElement('span', { style: { width: 8, height: 8, borderRadius: '50%', background: paused ? 'var(--fg-3)' : 'var(--st-online)', display: 'inline-block' }, className: paused ? '' : '' }), paused ? 'Dijeda' : 'Live'), ), React.createElement(SectionCard, { title: 'Log aktivitas', sub: shown.length + ' event', motif: true, right: React.createElement('span', { className: 'badge ' + (paused ? 'badge-soft' : 'badge-online') }, !paused && React.createElement('span', { className: 'b-dot', style: { animation: 'ping 2s infinite' } }), paused ? 'Jeda' : 'Realtime'), bodyStyle: { padding: '8px 0', maxHeight: 620, overflowY: 'auto' }, }, React.createElement('div', { style: { position: 'relative', padding: '8px 24px 8px 0' } }, // timeline vertical line React.createElement('div', { style: { position: 'absolute', left: 43, top: 0, bottom: 0, width: 2, background: 'var(--border-subtle)' } }), shown.map(e => { const v = ET[e.type]; return React.createElement('div', { key: e.id, className: e.fresh ? 'feed-new' : '', style: { display: 'flex', gap: 16, padding: '12px 0 12px 24px', position: 'relative' } }, React.createElement('div', { style: { width: 38, height: 38, borderRadius: 10, background: 'var(--bg-elev)', border: '2px solid ' + v.color, color: v.color, display: 'grid', placeItems: 'center', flexShrink: 0, zIndex: 1 } }, React.createElement(Icon, { name: v.icon, size: 18 })), React.createElement('div', { style: { flex: 1, minWidth: 0 } }, React.createElement('div', { className: 'row gap-2', style: { flexWrap: 'wrap' } }, React.createElement('span', { className: 'badge', style: { color: v.color, background: 'var(--bg-sunken)' } }, v.label), React.createElement('span', { style: { fontWeight: 700, fontSize: 13.5, color: 'var(--fg-1)' } }, e.machineName)), React.createElement('div', { style: { fontSize: 13.5, color: 'var(--fg-2)', marginTop: 4 } }, e.desc)), React.createElement('span', { style: { fontSize: 12, color: 'var(--fg-4)', whiteSpace: 'nowrap', fontFamily: 'var(--font-mono)' } }, relT(e.minsAgo))); }), shown.length === 0 && React.createElement('div', { style: { padding: 40, textAlign: 'center', color: 'var(--fg-3)', marginLeft: 24 } }, 'Tidak ada event untuk filter ini.'), ), ), ); } Object.assign(window, { EventsView });