// fichaje.eu — Screen: Fichaje (clock in/out, with pause + meal) const ScreenFichaje = () => { // Estado inicial inferido del backend (status del empleado actual) const myStatus = (window.BOOTSTRAP && window.BOOTSTRAP.me && window.BOOTSTRAP.me.status) || 'fuera'; const statusToUi = { fichado: 'working', pausa: 'paused', fuera: 'out', vacaciones: 'out' }; const [state, setStateRaw] = React.useState(statusToUi[myStatus] || 'out'); const [busy, setBusy] = React.useState(false); const [now, setNow] = React.useState(new Date()); React.useEffect(() => { const id = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(id); }, []); // setState con persistencia en backend async function setState(newState) { if (busy) return; setBusy(true); try { let fn = null; if (newState === 'working' && state === 'out') fn = window.api.punchIn; else if (newState === 'working' && (state === 'paused' || state === 'mealing')) fn = window.api.pauseEnd; else if (newState === 'paused') fn = () => window.api.pauseStart('Pausa'); else if (newState === 'mealing') fn = () => window.api.pauseStart('Comida'); else if (newState === 'out') fn = window.api.punchOut; if (fn) { const r = await fn(); if (r && r.ok) { setStateRaw(newState); // Re-cargar bootstrap para refrescar lista de punches del día window.api.reloadBootstrap(); } } else { setStateRaw(newState); } } catch (e) { console.error('[fichaje] error:', e); alert('No se pudo guardar el fichaje. Revisa tu conexión.'); } finally { setBusy(false); } } const timeStr = now.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); const statusConfig = { working: { label: 'Fichando', dot: 'var(--success-500)', bg: 'var(--success-50)', fg: 'var(--success-600)', counter: '04:43:12' }, paused: { label: 'En pausa', dot: 'var(--warning-500)', bg: 'var(--warning-50)', fg: 'var(--warning-600)', counter: '00:03:18' }, mealing: { label: 'En comida', dot: 'var(--violet-500)', bg: 'var(--violet-100)', fg: '#6D28D9', counter: '00:24:05' }, out: { label: 'Fuera', dot: 'var(--slate-400)', bg: 'var(--slate-100)', fg: 'var(--slate-700)', counter: '00:00:00' }, }[state]; return (
{/* Main clock */}
Miércoles, 13 de mayo
{timeStr.slice(0, 5)}:{timeStr.slice(6)}
{statusConfig.label}
{statusConfig.counter}
{state === 'working' ? 'Tiempo trabajado hoy' : 'En este estado'}
{/* Action buttons */}
{/* Timeline */}
Tu día
Trabajo Pausa Comida
{/* Segments — based on 9 hour window 09:00–18:00 */}
{/* Current time marker */}
09:0011:0013:0015:0017:0018:00
{/* Punch list */}
{TODAY_PUNCHES.map((p, i) => (
{p.type === 'in' ? : }
{p.time}
{p.label}
{p.duration && {p.duration} min}
))}
{/* Right column */}
Trabajado
20h 38m
de 24h
Saldo
+1h 14m
superávit acumulado
{[ { d: 'Lunes 11', w: '8h 03m', e: '08:58 → 17:35', sup: '+3m' }, { d: 'Martes 12', w: '7h 50m', e: '09:12 → 17:30', sup: '-10m' }, { d: 'Miércoles 13', w: '4h 43m', e: '09:02 → (en curso)', sup: '—', live: true }, ].map((d, i) => (
{d.d}
{d.w}
{d.e}
{d.live ? en curso : {d.sup}}
))}
{/* Decorative map */} {/* Streets */} {/* Park */} {/* Marker */}
Oficina Valencia · C/ Colón 22
Dispositivo: MacBook Pro · IP autorizada
Verificado
); }; Object.assign(window, { ScreenFichaje });