/* ═══════════════════════════════════════════════════════════
TransferModal (internal movements) + QuarantineSection
═══════════════════════════════════════════════════════════ */
(function () {
const { useState: useStateT } = React;
const {
GUNS: GUNS_T, parseKey: parseKeyT, keyLabel: keyLabelT, ammoQtyBadge: badgeT,
genId: genIdT, fmtDatetime: fmtT, Modal: ModalT, PasswordGate: PasswordGateT,
} = window;
function TransferModal({ state, dispatch, onClose }) {
const [mode, setMode] = useStateT('p2g');
const regs = state.registeredTypes;
function P2GForm() {
const [palletId, setPalletId] = useStateT('');
const [gunSel, setGunSel] = useStateT({});
const [qtys, setQtys] = useStateT({});
const pallet = state.bunker.find(p => p.id === Number(palletId));
const palletRegs = regs.filter(r => (pallet?.ammo[r.key] || 0) > 0);
const selGuns = GUNS_T.filter(g => gunSel[g]);
const setQty = (gun, key, v) => setQtys(p => ({ ...p, [gun + '::' + key]: Math.max(0, parseInt(v) || 0) }));
const getQty = (gun, key) => qtys[gun + '::' + key] || 0;
const sumForKey = key => selGuns.reduce((s, g) => s + getQty(g, key), 0);
const keyValid = key => !pallet || sumForKey(key) <= (pallet.ammo[key] || 0);
const canSub = selGuns.length > 0 && pallet && palletRegs.length > 0 && palletRegs.every(r => keyValid(r.key));
const submit = () => {
selGuns.forEach(gun => {
const items = palletRegs.map(r => ({ key: r.key, qty: getQty(gun, r.key) })).filter(i => i.qty > 0);
if (items.length) dispatch({ type: 'TRANSFER_PALLET_TO_GUN', palletId: Number(palletId), gun, items });
});
onClose();
};
return (
{pallet && <>
בחר צוותים מקבלים:
{GUNS_T.map(g => (
))}
{selGuns.length > 0 && palletRegs.length > 0 && (
)}
{selGuns.length === 0 &&
בחר לפחות צוות אחד ↑
}
>}
);
}
function G2PForm() {
const [palletId, setPalletId] = useStateT('');
const [gunSel, setGunSel] = useStateT({});
const [qtys, setQtys] = useStateT({});
const selGuns = GUNS_T.filter(g => gunSel[g]);
const setQty = (gun, key, v) => setQtys(p => ({ ...p, [gun + '::' + key]: Math.max(0, parseInt(v) || 0) }));
const getQty = (gun, key) => qtys[gun + '::' + key] || 0;
const availKeys = regs.filter(r => selGuns.some(g => (state.guns[g]?.[r.key] || 0) > 0));
const gunValid = (gun, key) => getQty(gun, key) <= (state.guns[gun]?.[key] || 0);
const canSub = palletId && selGuns.length > 0 && availKeys.length > 0
&& selGuns.every(g => availKeys.every(r => gunValid(g, r.key)))
&& selGuns.some(g => availKeys.some(r => getQty(g, r.key) > 0));
const submit = () => {
selGuns.forEach(gun => {
const items = availKeys.map(r => ({ key: r.key, qty: getQty(gun, r.key) })).filter(i => i.qty > 0);
if (items.length) dispatch({ type: 'TRANSFER_GUN_TO_PALLET', gun, palletId: Number(palletId), items });
});
onClose();
};
return (
בחר צוותים מעבירים:
{GUNS_T.map(g => (
))}
{selGuns.length > 0 && availKeys.length > 0 && (
)}
{selGuns.length === 0 &&
בחר לפחות צוות אחד ↑
}
);
}
function G2GForm() {
const [src, setSrc] = useStateT('');
const [dst, setDst] = useStateT('');
const [qtys, setQtys] = useStateT({});
const srcAmmo = src ? state.guns[src] || {} : {};
const srcKeys = regs.filter(r => (srcAmmo[r.key] || 0) > 0);
const setQty = (key, v) => setQtys(p => ({ ...p, [key]: Math.max(0, Math.min(srcAmmo[key] || 0, parseInt(v) || 0)) }));
const items = srcKeys.map(r => ({ key: r.key, qty: qtys[r.key] || 0 })).filter(i => i.qty > 0);
const canSub = src && dst && src !== dst && items.length > 0;
const submit = () => { dispatch({ type: 'TRANSFER_GUN_TO_GUN', srcGun: src, dstGun: dst, items }); onClose(); };
return (
→
{src && srcKeys.length === 0 &&
אין מלאי לצוות {src}
}
{src && srcKeys.length > 0 && (
{srcKeys.map(r => (
{r.label}
זמין: {srcAmmo[r.key]} {r.unit}
setQty(r.key, e.target.value)} style={{ width: '68px', textAlign: 'center' }} />
))}
)}
);
}
function QuarantineForm({ srcType }) {
const [sel, setSel] = useStateT('');
const [qtys, setQtys] = useStateT({});
const [reason, setReason] = useStateT('');
const [approver, setApprover] = useStateT('');
const [imgB64, setImgB64] = useStateT('');
const isGun = srcType === 'gun';
const container = isGun ? (sel ? state.guns[sel] || {} : {}) : (state.bunker.find(p => p.id === Number(sel))?.ammo || {});
const srcKeys = regs.filter(r => (container[r.key] || 0) > 0);
const setQty = (key, v) => setQtys(p => ({ ...p, [key]: Math.max(0, Math.min(container[key] || 0, parseInt(v) || 0)) }));
const items = srcKeys.map(r => ({ key: r.key, qty: qtys[r.key] || 0 })).filter(i => i.qty > 0);
const canSub = sel && items.length > 0 && reason.trim() && approver.trim();
const handleImg = e => { const f = e.target.files[0]; if (!f) return; const rd = new FileReader(); rd.onload = ev => setImgB64(ev.target.result); rd.readAsDataURL(f); };
const submit = () => {
dispatch(isGun
? { type: 'ADD_TO_QUARANTINE', srcType: 'gun', gunId: sel, items, reason: reason.trim(), approver: approver.trim(), imageB64: imgB64 }
: { type: 'ADD_TO_QUARANTINE', srcType: 'pallet', palletId: Number(sel), items, reason: reason.trim(), approver: approver.trim(), imageB64: imgB64 });
onClose();
};
return (
{sel && srcKeys.length === 0 &&
{isGun ? 'אין מלאי' : 'משטח ריק'}
}
{sel && srcKeys.length > 0 && (
{srcKeys.map(r => (
{r.label}
זמין: {container[r.key]} {r.unit}
setQty(r.key, e.target.value)} style={{ width: '68px', textAlign: 'center' }} />
))}
)}
{sel && srcKeys.length > 0 && <>
>}
);
}
const modes = [
{ id: 'p2g', label: 'מרמסע לצוותים' },
{ id: 'g2p', label: 'צוותים לרמסע' },
{ id: 'g2g', label: 'צוות לצוות' },
{ id: 'g2q', label: 'מצוות לפינת פסולים' },
{ id: 'p2q', label: 'מרמסע לפינת פסולים' },
];
return (
{modes.map(mo => (
))}
{mode === 'p2g' && }
{mode === 'g2p' && }
{mode === 'g2g' && }
{mode === 'g2q' && }
{mode === 'p2q' && }
);
}
// ── Quarantine section (full panel) ──────────────────────────
function QuarantineSection({ state, regs, dispatch }) {
const [viewImg, setViewImg] = useStateT(null);
const [authPending, setAuthPending] = useStateT(null);
const [actionPending, setActionPending] = useStateT(null);
const [dstGun, setDstGun] = useStateT('');
const log = state.quarantineLog || [];
function confirmAction() {
if (!actionPending) return;
if (actionPending.type === 'return') {
if (!dstGun) return;
dispatch({ type: 'RETURN_FROM_QUARANTINE', entryId: actionPending.entry.id, dstGun });
} else {
dispatch({ type: 'SCRAP_QUARANTINE', entryId: actionPending.entry.id });
}
setActionPending(null); setDstGun('');
}
return (
⛔ פינת פסולים
רשומות: {log.length}
{log.length === 0 ? (
) : (
| תאריך ושעה | מקור | תחמושת | סיבת פסילה | גורם מאשר | תמונה | פעולות בוחן |
{[...log].reverse().map(r => (
| {fmtT(r.datetime)} |
{r.src} |
{(r.items || []).map((it, j) => (
{keyLabelT(it.key, regs)}
×{it.qty}
))}
|
{r.reason} |
{r.approver} |
{r.imageB64 ? : —} |
|
))}
)}
{authPending && (
{ setActionPending(authPending); setAuthPending(null); setDstGun(''); }} onCancel={() => setAuthPending(null)} />
)}
{actionPending && (
{ setActionPending(null); setDstGun(''); }}>
תחמושת מהרשומה:
{(actionPending.entry.items || []).map((it, j) => (
{keyLabelT(it.key, regs)}
×{it.qty}
))}
מקור מקורי: {actionPending.entry.src}
{actionPending.type === 'return' && (
)}
{actionPending.type === 'scrap' && (
⚠ התחמושת תסולק לצמיתות מהמערכת.
)}
)}
{viewImg && (
setViewImg(null)}>
e.stopPropagation()}>
)}
);
}
window.TransferModal = TransferModal;
window.QuarantineSection = QuarantineSection;
})();