// Jóváhagyó (Approver) nézet const ApproverHome = ({ user, setView, openApproval, openPayout }) => { const pending = window.WORK_LOGS.filter(l => l.status === 'pending'); const totalPending = window.totalPending(); const totalApproved = window.totalSpent(); const totalPaid = window.totalPaid(); const totalDebt = totalApproved - totalPaid; return (
Jóváhagyó · {user.name}

{pending.length > 0 ? `${pending.length} jóváhagyásra vár` : 'Minden naprakész'}

{/* Összegek */}
{/* Jóváhagyásra váró */} {pending.length > 0 && (

Jóváhagyásra vár

{pending.slice(0, 3).map(l => openApproval(l)}/>)}
)} {/* Vállalkozói egyenlegek */}

Egyenlegek

{window.getContractors().slice(0, 3).map(c => { const b = window.contractorBalance(c.id); if (b.balance <= 0) return null; return openPayout(c)}/>; })}
); }; const StatCard = ({ label, value, sub, accent = 'ink' }) => { const colors = { ink: 'var(--ink)', warn: '#8A6200', ok: 'var(--ok)', danger: 'var(--danger)' }; return (
{label}
{value}
{sub &&
{sub}
}
); }; const ApprovalCard = ({ log, onClick }) => { const c = window.getUser(log.contractorId); const phase = window.getPhase(log.phaseId); const task = window.getParentTask(log.parentTaskId); const { total } = window.computeWorkLog(log); const taskBudget = task.budget; const taskSpent = window.parentTaskSpent(log.parentTaskId); const remaining = taskBudget - taskSpent; const overrun = total > remaining; return (
{c.name}
{window.formatDateShort(log.date)} · {phase?.code}
{window.formatHufShort(total)} Ft
{task.name}
{log.description}
{(() => { const s = window.workLogStats(log); if (s.isPurchase || !s.hasLabor) return Beszerzés; return {s.totalWorkers}fő × {s.totalHours}ó; })()} {log.materialCost > 0 && <> · anyag {window.formatHufShort(log.materialCost)}} {(() => { const list = Array.isArray(log.photoList) ? log.photoList : (Array.isArray(log.photos) ? log.photos : null); const n = list ? list.length : (typeof log.photos === 'number' ? log.photos : 0); return n > 0 ? <> · {n} : null; })()}
{overrun ? '!' : ''} keret: {window.formatHufShort(remaining)}
); }; const ContractorBalanceCard = ({ contractor, balance, onClick }) => (
{contractor.name}
{contractor.skill}
{window.formatHufShort(balance.balance)} Ft
kifizetésre vár
); const ApproverInbox = ({ openApproval }) => { const [filter, setFilter] = React.useState('pending'); const all = window.WORK_LOGS.slice().reverse(); const filtered = filter === 'all' ? all : all.filter(l => l.status === filter); const counts = { all: all.length, pending: all.filter(l => l.status === 'pending').length, approved: all.filter(l => l.status === 'approved').length, rejected: all.filter(l => l.status === 'rejected').length, }; return (

Bejövő teljesítések

{[ { k: 'pending', l: 'Várakozik' }, { k: 'approved', l: 'Jóváhagyva' }, { k: 'rejected', l: 'Elutasítva' }, { k: 'all', l: 'Mind' }, ].map(t => ( ))}
{filtered.map(l => openApproval(l)}/>)} {filtered.length === 0 &&
Nincs ilyen tétel.
}
); }; // Részletes jóváhagyási nézet const ApprovalDetail = ({ log, onClose, onDecide }) => { const c = window.getUser(log.contractorId); const phase = window.getPhase(log.phaseId); const task = window.getParentTask(log.parentTaskId); const { labor, total } = window.computeWorkLog(log); const taskBudget = task.budget; const taskSpent = window.parentTaskSpent(log.parentTaskId); const remaining = taskBudget - taskSpent - (log.status === 'pending' ? 0 : 0); const [approvedAmount, setApprovedAmount] = React.useState(total); const [note, setNote] = React.useState(''); const [showReject, setShowReject] = React.useState(false); return (

Teljesítés #{String(log.id).padStart(4, '0')}

{/* Kontaktor + dátum */}
{c.name}
{c.skill} · {window.formatHuf(c.hourlyRate)}/óra
{window.formatDate(log.date)}
{/* Mit */}
{task.name}
{log.description}
{/* Számítás */}
Számítás
{(() => { const s = window.workLogStats(log); if (s.hasLabor) { return ; } return null; })()} {log.materialCost > 0 && }
{/* Költségvetés állapot */}
Főfeladat költségvetése
0 ? 'var(--ok)' : 'var(--danger)'}/> {total > remaining && (
⚠ A tétel {window.formatHuf(total - remaining)}-tal túllépi a fennmaradó keretet.
)}
{/* Fotók */} {(() => { const photoArr = Array.isArray(log.photoList) ? log.photoList : (Array.isArray(log.photos) ? log.photos : []); const photoCount = photoArr.length || (typeof log.photos === 'number' ? log.photos : 0); return (
Fotók ({photoCount})
{photoArr.length > 0 ? photoArr.map(p => ( )) : Array.from({ length: photoCount }).map((_, i) => (
))} {photoCount === 0 &&
Nincs feltöltött fotó.
}
); })()} {log.status === 'pending' && ( <>
setApprovedAmount(parseInt(e.target.value) || 0)}/> Ft
Lehet kevesebb, mint a kért összeg.