/* ═══════════════════════════════════════════════════════════ History views — firing log (missions) + transfer log. Firing rows are editable (re-opens FireMissionModal in edit mode behind a password gate). CSV export for both logs. ═══════════════════════════════════════════════════════════ */ (function () { const { useState: useStateH } = React; const { GUNS: GUNS_H, parseKey: parseKeyH, keyLabel: keyLabelH, fmtDatetime: fmtH, csvExport: csvH, Modal: ModalH, PasswordGate: PasswordGateH, } = window; function comboSummary(items, regs) { // group ammoItems into "shell + charge + fuze ×N" readable lines return (items || []).map((it, i) => { const p = parseKeyH(it.key); const icon = p?.category === 'shell' ? '🔵' : p?.category === 'charge' ? '⚡' : '🟡'; return {icon} {keyLabelH(it.key, regs)} ×{it.qty}; }); } // ── Firing log ─────────────────────────────────────────────── function MissionsView({ state, dispatch }) { const regs = state.registeredTypes; const [editRow, setEditRow] = useStateH(null); const [authRow, setAuthRow] = useStateH(null); // pending edit auth const [deleteRow, setDeleteRow] = useStateH(null); // row waiting for delete auth const [deleteAuthRow, setDeleteAuthRow] = useStateH(null); // row after auth, waiting confirm const [q, setQ] = useStateH(''); const log = state.firingLog || []; const filtered = log.filter(r => { if (!q.trim()) return true; const hay = [r.missionId, r.target, r.unit, r.gun, r.notes].join(' ').toLowerCase(); return hay.includes(q.toLowerCase()); }); const exportCsv = () => { const regs = state.registeredTypes; // Build a unique sorted list of all ammo keys that appear in any mission const usedKeys = new Set(); log.forEach(r => (r.ammoItems || []).forEach(it => usedKeys.add(it.key))); // Sort: shells first, then charges, then fuzes const catOrder = { shell: 0, charge: 1, fuze: 2 }; const sortedKeys = [...usedKeys].sort((a, b) => { const pa = window.parseKey ? window.parseKey(a) : null; const pb = window.parseKey ? window.parseKey(b) : null; const oa = catOrder[pa?.category] ?? 9; const ob = catOrder[pb?.category] ?? 9; if (oa !== ob) return oa - ob; return keyLabelH(a, regs).localeCompare(keyLabelH(b, regs), 'he'); }); // Headers: fixed fields + one column per ammo type + category subtotals const fixedHeaders = ['מזהה משימה', 'תאריך', 'שעה', 'צוות', 'מסתייע', 'מטרה', 'mapiq', 'הערות']; const ammoHeaders = sortedKeys.map(k => keyLabelH(k, regs)); const totalHeaders = ['סה"כ פגזים', 'סה"כ חנ"ה', 'סה"כ מרעומים', 'סה"כ כל התחמושת']; const headers = [...fixedHeaders, ...ammoHeaders, ...totalHeaders]; const rows = log.map(r => { // Build qty map for this mission const qtyMap = {}; (r.ammoItems || []).forEach(it => { qtyMap[it.key] = (qtyMap[it.key] || 0) + (it.qty || 0); }); const fixed = [r.missionId, r.date, r.time, r.gun, r.unit || '', r.target || '', r.mapiq || '', r.notes || '']; const ammo = sortedKeys.map(k => qtyMap[k] || 0); // Category subtotals let totalShells = 0, totalCharges = 0, totalFuzes = 0; sortedKeys.forEach(k => { const p = window.parseKey ? window.parseKey(k) : null; const q = qtyMap[k] || 0; if (p?.category === 'shell') totalShells += q; if (p?.category === 'charge') totalCharges += q; if (p?.category === 'fuze') totalFuzes += q; }); const totals = [totalShells, totalCharges, totalFuzes, totalShells + totalCharges + totalFuzes]; return [...fixed, ...ammo, ...totals]; }); csvH(headers, rows, `firing-log-${new Date().toISOString().slice(0, 10)}.csv`); }; const doDelete = () => { if (!deleteAuthRow) return; dispatch({ type: 'DELETE_MISSION', rowId: deleteAuthRow.id }); setDeleteAuthRow(null); }; return (

יומן משימות אש

{log.length} רשומות — ממוין מהחדש לישן
setQ(e.target.value)} style={{ width: '240px', maxWidth: '60vw' }} />
{filtered.length === 0 ? (
🎯
{log.length ? 'אין תוצאות לחיפוש' : 'אין משימות אש עדיין'}
) : ( {filtered.map(r => ( ))}
מזההתאריך/שעהצוותמסתייעמטרה תחמושת הערותפעולות
{r.missionId} {r.date}
{r.time}
{r.gun} {r.unit} {r.target} {comboSummary(r.ammoItems, regs)} {r.notes || '—'}
)}
{/* Edit auth gate */} {authRow && { setEditRow(authRow); setAuthRow(null); }} onCancel={() => setAuthRow(null)} />} {editRow && setEditRow(null)} />} {/* Delete auth gate — uses PasswordGate (same AUTH_PASSWORD) */} {deleteRow && ( { setDeleteAuthRow(deleteRow); setDeleteRow(null); }} onCancel={() => setDeleteRow(null)} /> )} {/* Delete confirmation modal */} {deleteAuthRow && ( setDeleteAuthRow(null)}>
⚠ משימה {deleteAuthRow.missionId} תימחק לצמיתות.
התחמושת שנוצלה ({(deleteAuthRow.ammoItems || []).map(it => `${keyLabelH(it.key, regs)} ×${it.invDeduction || it.qty}`).join(', ')}) תוחזר למלאי צוות {deleteAuthRow.gun}.
מזהה{deleteAuthRow.missionId}
מטרה{deleteAuthRow.target}
תאריך{deleteAuthRow.date} {deleteAuthRow.time}
)}
); } // ── Transfer log ───────────────────────────────────────────── function TransfersView({ state }) { const regs = state.registeredTypes; const [q, setQ] = useStateH(''); const log = [...(state.transferLog || [])].reverse(); const filtered = log.filter(r => { if (!q.trim()) return true; const hay = [r.src, r.dst].join(' ').toLowerCase(); return hay.includes(q.toLowerCase()); }); const exportCsv = () => { const headers = ['תאריך ושעה', 'מקור', 'יעד', 'תחמושת']; const rows = log.map(r => [ fmtH(r.datetime), r.src, r.dst, (r.items || []).map(it => `${keyLabelH(it.key, regs)} x${it.qty}`).join(' + '), ]); csvH(headers, rows, `transfer-log-${new Date().toISOString().slice(0, 10)}.csv`); }; return (

יומן תנועות מלאי

{log.length} תנועות — קליטות, ניודים, פסילות
setQ(e.target.value)} style={{ width: '220px', maxWidth: '60vw' }} />
{filtered.length === 0 ? (
🔁
{log.length ? 'אין תוצאות' : 'אין תנועות מלאי'}
) : ( {filtered.map(r => ( ))}
תאריך ושעהמקוריעדתחמושת
{fmtH(r.datetime)} {r.src} {r.dst} {(r.items || []).map((it, j) => ( {keyLabelH(it.key, regs)} ×{it.qty} ))}
)}
); } window.MissionsView = MissionsView; window.TransfersView = TransfersView; })();