// Vállalkozó (Contractor) nézet — gyors teljesítés-rögzítés a fókusz const ContractorHome = ({ user, setView, openLogger }) => { const myLogs = window.WORK_LOGS.filter(l => l.contractorId === user.id).slice().reverse(); const balance = window.contractorBalance(user.id); const todayLogs = myLogs.filter(l => l.date === window.TODAY); return (
{/* Üdvözlő blokk */}
Szia, {user.name.split(' ')[0]}

Mit csináltál ma?

{/* Egyenleg kártya */}
Egyenleged
{window.formatHufShort(balance.balance)} Ft
kifizetésre vár
Jóváhagyva
{window.formatHuf(balance.approved)}
Kifizetve
{window.formatHuf(balance.paid)}
{/* Új teljesítés CTA */} {/* Mai aktivitás */} {todayLogs.length > 0 ? (

Mai munka

{todayLogs.length} bejegyzés
{todayLogs.map(l => )}
) : ( Tipp: a leggyorsabb, ha rögtön a munka után rögzíted a teljesítést. 4 koppintás, 1 fotó, kész. )} {/* Utolsó néhány teljesítés */}

Legutóbbi teljesítések

{myLogs.slice(0, 4).map(l => )}
); }; const LogCard = ({ log, onClick }) => { const { labor, total } = window.computeWorkLog(log); const phase = window.getPhase(log.phaseId); const task = window.getParentTask(log.parentTaskId); return (
{window.formatDateShort(log.date)} · {phase?.code}
{task?.name}
{log.description}
{(() => { const stats = window.workLogStats(log); if (stats.isPurchase || !stats.hasLabor) { return Beszerzés; } return ( <> {stats.totalWorkers} fő · {stats.totalHours} óra ); })()} {(() => { 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; })()}
{window.formatHufShort(total)} Ft
{log.status === 'rejected' && log.rejectReason && (
{log.rejectReason}
)}
); }; // === Új teljesítés flow === // Két típus: "Munka" (egy vagy több csapat × óra) vagy "Beszerzés" (csak anyag/számla). const Logger = ({ user, onClose, onSubmit }) => { const [entryType, setEntryType] = React.useState('labor'); // 'labor' | 'purchase' const [phaseId, setPhaseId] = React.useState(null); const [taskId, setTaskId] = React.useState(null); const [date, setDate] = React.useState(window.TODAY); const [tiers, setTiers] = React.useState([{ workers: 2, hours: 8 }]); const [materialCost, setMaterial] = React.useState(0); const [description, setDesc] = React.useState(''); // photos: [{ id, file, previewUrl }] const [photos, setPhotos] = React.useState([]); const photoInputRef = React.useRef(null); const updateTier = (i, patch) => setTiers(tiers.map((t, idx) => idx === i ? { ...t, ...patch } : t)); const addTier = () => setTiers([...tiers, { workers: 1, hours: 8 }]); const removeTier = (i) => setTiers(tiers.filter((_, idx) => idx !== i)); const onPhotoPick = (e) => { const files = Array.from(e.target.files || []); const next = files.map(f => ({ id: Date.now() + Math.random(), file: f, previewUrl: f.type.startsWith('image/') ? URL.createObjectURL(f) : null, })); setPhotos(p => [...p, ...next]); e.target.value = ''; }; const labor = entryType === 'labor' ? tiers.reduce((s, t) => s + (t.workers || 0) * (t.hours || 0) * user.hourlyRate, 0) : 0; const total = labor + materialCost; const totalWorkers = tiers.reduce((s, t) => s + (t.workers || 0), 0); const totalHours = tiers.reduce((s, t) => s + (t.workers || 0) * (t.hours || 0), 0); const tasks = phaseId ? window.PARENT_TASKS.filter(t => t.phaseId === phaseId) : []; const taskBudget = taskId ? window.getParentTask(taskId).budget : 0; const taskSpent = taskId ? window.parentTaskSpent(taskId) : 0; const taskPending = taskId ? window.parentTaskPending(taskId) : 0; const remaining = taskBudget - taskSpent - taskPending; const canSubmit = phaseId && taskId && date && description.trim().length > 0 && ( totalHours > 0 || materialCost > 0 ); return (

Új teljesítés

{user.name}
{/* 1. Fázis */}
1 · Fázis
{phaseId && }
{!phaseId ? (
{window.PHASES.map(p => ( ))}
) : (
{window.getPhase(phaseId).code} {window.getPhase(phaseId).name}
)}
{/* 2. Főfeladat */} {phaseId && (
2 · Főfeladat
{taskId && }
{!taskId ? (
{tasks.map(t => { const sp = window.parentTaskSpent(t.id); const pct = t.budget > 0 ? (sp / t.budget) * 100 : 0; return ( ); })}
) : (
{window.getParentTask(taskId).name}
Keret {window.formatHuf(taskBudget)}
Felhasznált {window.formatHuf(taskSpent)}
Maradvány 0 ? 'var(--ok)' : 'var(--danger)', whiteSpace: 'nowrap' }}>{window.formatHuf(remaining)}
)}
)} {/* 3. Munka + anyag VAGY csak beszerzés */} {taskId && (
3 · Mit rögzítesz?
{/* Típusváltó */}
setDate(e.target.value)} />
{/* Munka csapatok (tiers) */} {entryType === 'labor' && (
{tiers.length > 0 && ( össz. {totalWorkers} fő · {totalHours} óra )}
{tiers.map((t, i) => (
{tiers.length > 1 ? `${i + 1}. csapat` : 'Munkaóra'} {tiers.length > 1 && ( )}
updateTier(i, { workers: v })} min={1} max={20}/>
updateTier(i, { hours: v })} min={1} max={16}/>
{t.workers} × {t.hours}ó × {window.formatHufShort(user.hourlyRate)} Ft {window.formatHuf((t.workers || 0) * (t.hours || 0) * user.hourlyRate)}
))}
)} {/* Anyag / beszerzés ár */}
setMaterial(parseInt(e.target.value) || 0)} style={{ flex: 1, minWidth: 0 }}/> Ft
)} {/* 4. Leírás + fotó */} {taskId && (
4 · Leírás és fotók