// fichaje.eu — Screen: Ausencias (vacation/absence request + approvals) const ScreenAusencias = ({ role }) => { const [tab, setTab] = React.useState('mis'); const [reqOpen, setReqOpen] = React.useState(false); return (
{/* Balance summary */}
}/> }/> }/> }/>
} onClick={() => setReqOpen(true)}>Solicitar ausencia
{tab === 'mis' && (
Tipo
Periodo
Días
Aprobador
Estado
{MY_ABSENCES.map(a => (
{a.type === 'Vacaciones' ? : a.type === 'Médico' ? : }
{a.type}
{a.range}
{a.days}
{a.approver.split(' ')[0]}
{a.status}
))}
)} {tab === 'equipo' && role === 'admin' && (
{TEAM_ABSENCE_REQUESTS.map(r => (
{r.name}
{r.type} · {r.range} · {r.days} días
Solicitado {r.requested}
} onClick={async () => { if (!window.api || !r.id) return; await window.api.decideVacation(r.id, 'rechazado'); await window.api.reloadBootstrap(); window.location.reload(); }}>Rechazar } onClick={async () => { if (!window.api || !r.id) return; await window.api.decideVacation(r.id, 'aprobado'); await window.api.reloadBootstrap(); window.location.reload(); }}>Aprobar
))}
)} {tab === 'cal' && ( )}
{[ { name: 'Aitor Llopis', type: 'Vacaciones', range: 'Hasta el 22 may', tone: 'brand' }, { name: 'Pere Aguiló', type: 'Asuntos propios', range: '20 may', tone: 'violet' }, { name: 'Berta Folch', type: 'Vacaciones (pdte)', range: '07 jul → 25 jul', tone: 'warning' }, ].map((p, i) => (
{p.name}
{p.range}
{p.type}
))}
23 días de vacaciones anuales + 4 de asuntos propios.
Solicitudes con 15 días de antelación mínimo.
Aprobación por manager directo y RRHH.
{reqOpen && setReqOpen(false)}/>}
); }; const TeamTimeline = () => { const days = Array.from({ length: 21 }, (_, i) => i + 11); // 11–31 May const people = [ { name: 'Marc Vives', blocks: [] }, { name: 'Aitor Llopis', blocks: [{ s: 0, e: 12, type: 'vacation' }] }, { name: 'Pere Aguiló', blocks: [{ s: 9, e: 11, type: 'personal' }] }, { name: 'Berta Folch', blocks: [] }, { name: 'Mireia Solé', blocks: [{ s: 4, e: 5, type: 'medical' }] }, { name: 'Sofía Marín', blocks: [] }, { name: 'Iván Romero', blocks: [{ s: 16, e: 18, type: 'vacation' }] }, { name: 'Joel Vidal', blocks: [] }, ]; const colors = { vacation: ['var(--brand-100)', 'var(--brand-700)'], personal: ['var(--violet-100)', '#6D28D9'], medical: ['var(--warning-100)', 'var(--warning-600)'], }; return (
{days.map(d => (
{d}
))}
{people.map(p => (
{p.name.split(' ')[0]}
{days.map((d, i) => (
))}
{p.blocks.map((b, i) => (
{b.type === 'vacation' ? 'Vacaciones' : b.type === 'personal' ? 'Personal' : 'Médico'}
))}
))}
); }; const RequestAbsenceModal = ({ onClose }) => { const [type, setType] = React.useState('Vacaciones'); const [from, setFrom] = React.useState('2026-08-11'); const [to, setTo] = React.useState('2026-08-22'); const [reason, setReason] = React.useState(''); const [busy, setBusy] = React.useState(false); const [err, setErr] = React.useState(''); const submit = async () => { if (!window.api) return; setBusy(true); setErr(''); try { const kindMap = { 'Vacaciones': 'vacaciones', 'Asuntos propios': 'personal', 'Médico': 'medico', 'Otros': 'otros' }; const r = await window.api.requestVacation({ kind: kindMap[type] || 'vacaciones', start_date: from, end_date: to, note: reason, days_workdays: businessDays, }); if (r && r.error) { setErr(r.error); setBusy(false); return; } await window.api.reloadBootstrap(); window.location.reload(); } catch (e) { setErr(String(e)); setBusy(false); } }; // calcular días laborables aproximados (lun-vie) const businessDays = (() => { try { const a = new Date(from), b = new Date(to); if (a > b) return 0; let d = new Date(a), n = 0; while (d <= b) { const w = d.getDay(); if (w !== 0 && w !== 6) n++; d.setDate(d.getDate() + 1); } return n; } catch { return 0; } })(); return (
e.stopPropagation()} style={{ background: '#fff', borderRadius: 'var(--r-xl)', width: 540, padding: 28, boxShadow: 'var(--shadow-lg)', }}>
Solicitar ausencia
Tu manager recibirá la solicitud para aprobar.
{[ { v: 'Vacaciones', i: }, { v: 'Asuntos propios', i: }, { v: 'Médico', i: }, { v: 'Otros', i: }, ].map(o => ( ))}
setFrom(e.target.value)} icon={}/> setTo(e.target.value)} icon={}/>