// Clock Section — featured artifact: live work-day dial const ClockDial = () => { const [now, setNow] = React.useState(new Date()); React.useEffect(() => { const id = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(id); }, []); // Mock workday: 09:02 entry, currently at simulated 13:45 (live tick). // We base the "live" pointer on real seconds, looping inside the 9-hour window const SHIFT_START = 9 * 60; // 09:00 const SHIFT_END = 18 * 60; // 18:00 // worked = 04:43:s (counter) const baseSec = 4 * 3600 + 43 * 60; const tickSec = (now.getSeconds() + now.getMilliseconds() / 1000); const workedSec = baseSec + Math.floor(tickSec * 5) % 60; // gently advances const workedH = Math.floor(workedSec / 3600); const workedM = Math.floor((workedSec % 3600) / 60); const workedS = workedSec % 60; // Segments [startMin, endMin, type] const segs = [ { s: 9 * 60 + 2, e: 11 * 60 + 15, type: 'work' }, { s: 11 * 60 + 15, e: 11 * 60 + 27, type: 'pause' }, { s: 11 * 60 + 27, e: 14 * 60, type: 'work' }, { s: 14 * 60, e: 14 * 60 + 45, type: 'meal' }, { s: 14 * 60 + 45, e: 18 * 60, type: 'work' }, ]; const colors = { work: '#3B82F6', pause: '#F59E0B', meal: '#A78BFA', }; const R = 150, cx = 190, cy = 190; const SHIFT_LEN = SHIFT_END - SHIFT_START; // Convert minutes (from shift start) → angle. 0 = top, clockwise. const minToAngle = (m) => ((m - SHIFT_START) / SHIFT_LEN) * 360 - 90; const angleToXY = (a, r) => [cx + Math.cos(a * Math.PI / 180) * r, cy + Math.sin(a * Math.PI / 180) * r]; const arcPath = (s, e, r) => { const a1 = minToAngle(s); const a2 = minToAngle(e); const [x1, y1] = angleToXY(a1, r); const [x2, y2] = angleToXY(a2, r); const large = (a2 - a1) > 180 ? 1 : 0; return `M ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2}`; }; // Current position (live) — sweep around the dial slowly for visual life const currentMin = 13 * 60 + 45 + Math.floor(tickSec * 2); const currentAngle = minToAngle(currentMin); const [px, py] = angleToXY(currentAngle, R); return (
{/* outer ring track */} {/* segments */} {segs.map((s, i) => ( ))} {/* hour ticks */} {Array.from({ length: 10 }, (_, i) => { const mins = SHIFT_START + i * 60; const a = minToAngle(mins); const [x1, y1] = angleToXY(a, R - 14); const [x2, y2] = angleToXY(a, R - 6); return ; })} {/* hour labels */} {[9, 12, 15, 18].map(h => { const a = minToAngle(h * 60); const [x, y] = angleToXY(a, R + 22); return {String(h).padStart(2,'0')}; })} {/* live needle */} {/* center dot */} {/* Center counter */}
Trabajado hoy
{String(workedH).padStart(2,'0')}:{String(workedM).padStart(2,'0')}:{String(workedS).padStart(2,'0')}
de 08:00 previstas
); }; const ClockSection = () => (
Control horario en tiempo real

Tu jornada
al segundo exacto.

Cada entrada, pausa y comida se registra al milisegundo. El cronómetro corre en vivo, los segmentos del día se dibujan a medida que avanza tu jornada y los reportes están siempre listos para auditoría.

{[ { v: '5h 17m', l: 'Restantes hoy', c: '#60A5FA' }, { v: '57 min', l: 'Pausas + comida', c: '#FCD34D' }, { v: '+1h 14m', l: 'Saldo semana', c: '#34D399' }, ].map((s, i) => (
{s.v}
{s.l}
))}
{[ { c: '#3B82F6', l: 'Tiempo de trabajo efectivo', v: '4h 31m' }, { c: '#F59E0B', l: 'Pausa café', v: '12 min' }, { c: '#A78BFA', l: 'Comida', v: '45 min' }, ].map((row, i) => (
{row.l} {row.v}
))}
{/* floating accents around clock */}
EN VIVO
Cumple RDL 8/2019
); Object.assign(window, { ClockSection });