/** * Разбор файла (CSV / JSON / Excel) и синхронизация с API. * Зависимости: глобальные Papa, XLSX (CDN на странице). */ (function (global) { function parseExcelToRows(buffer) { if (typeof XLSX === "undefined") { throw new Error("Библиотека Excel не загружена (XLSX). Проверьте сеть и CDN."); } const wb = XLSX.read(buffer, { type: "array" }); if (!wb.SheetNames || !wb.SheetNames.length) { throw new Error("В книге Excel нет листов"); } const ws = wb.Sheets[wb.SheetNames[0]]; return XLSX.utils.sheet_to_json(ws, { defval: "", raw: false, blankrows: false }); } function parseFileToRows(file) { return new Promise(function (resolve, reject) { const lower = file.name.toLowerCase(); const isExcel = lower.endsWith(".xlsx") || lower.endsWith(".xls"); if (isExcel) { const reader = new FileReader(); reader.onload = function (ev) { try { resolve(parseExcelToRows(new Uint8Array(ev.target.result))); } catch (e) { reject(e); } }; reader.onerror = function () { reject(new Error("Не удалось прочитать файл")); }; reader.readAsArrayBuffer(file); return; } const reader = new FileReader(); reader.onload = function (ev) { try { const text = String(ev.target.result || ""); let rows; if (lower.endsWith(".json")) { const data = JSON.parse(text); if (!Array.isArray(data)) throw new Error("JSON должен быть массивом объектов"); rows = data; } else { const parsed = Papa.parse(text, { header: true, skipEmptyLines: true }); if (parsed.errors && parsed.errors.length) console.warn(parsed.errors); rows = parsed.data || []; } resolve(rows); } catch (e) { reject(e); } }; reader.onerror = function () { reject(new Error("Не удалось прочитать файл")); }; reader.readAsText(file, "UTF-8"); }); } function syncRowsToServer(rows) { return fetch("/api/incidents/sync", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rows: rows }) }).then(function (r) { if (!r.ok) { return r.text().then(function (t) { throw new Error(t || "HTTP " + r.status); }); } return r.json(); }); } function fetchIncidents() { return fetch("/api/incidents").then(function (r) { if (!r.ok) return null; return r.json(); }); } global.OmcUpload = { parseFileToRows: parseFileToRows, syncRowsToServer: syncRowsToServer, fetchIncidents: fetchIncidents }; })(typeof window !== "undefined" ? window : this);