Refactor menu flows into dedicated pages
Some checks failed
ci / test (push) Has been cancelled

This commit is contained in:
VPN SaaS Dev
2026-05-16 11:59:09 +09:00
parent 01a69fc42d
commit ecfb5aa949
20 changed files with 2415 additions and 97 deletions

View File

@@ -0,0 +1,96 @@
const page = CarPassPage;
let selectedPayload = null;
function summaryItems(counts = {}) {
const labels = {
vehicles: "Автомобили",
fuel_entries: "Заправки",
service_entries: "ТО и ремонт",
expense_entries: "Расходы",
appointments: "Записи в СТО",
service_visits: "Заказ-наряды",
};
return Object.entries(labels).map(([key, label]) => `
<div class="stack-item">
<strong>${label}</strong>
<small>${page.escapeHtml(counts[key] ?? 0)}</small>
</div>
`).join("");
}
function countExport(payload) {
const vehicles = payload.vehicles || [];
return {
vehicles: vehicles.length,
fuel_entries: vehicles.reduce((sum, item) => sum + (item.fuel_entries || []).length, 0),
service_entries: vehicles.reduce((sum, item) => sum + (item.service_entries || []).length, 0),
expense_entries: vehicles.reduce((sum, item) => sum + (item.expense_entries || []).length, 0),
appointments: vehicles.reduce((sum, item) => sum + (item.appointments || []).length, 0),
service_visits: vehicles.reduce((sum, item) => sum + (item.service_visits || []).length, 0),
};
}
function renderPreview(preview) {
document.querySelector("#importSummary").innerHTML = `
${summaryItems(preview.counts)}
${preview.warnings?.length ? `<div class="tip-card warning">${preview.warnings.map(page.escapeHtml).join("<br />")}</div>` : ""}
`;
}
async function readSelectedFile() {
const file = document.querySelector("#importFile").files[0];
if (!file) throw new Error("Выберите JSON-файл");
selectedPayload = JSON.parse(await file.text());
return selectedPayload;
}
document.querySelector("#exportBtn").addEventListener("click", async (event) => {
await page.runAction(event.currentTarget, "Готовлю файл...", async () => {
const payload = await page.api("/my/export");
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, "-");
link.href = url;
link.download = `carpass-export-${stamp}.json`;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
document.querySelector("#exportSummary").innerHTML = summaryItems(countExport(payload));
page.toast("Экспорт подготовлен");
});
});
document.querySelector("#previewBtn").addEventListener("click", async (event) => {
await page.runAction(event.currentTarget, "Проверяю файл...", async () => {
const payload = await readSelectedFile();
const preview = await page.api("/my/import/preview", {
method: "POST",
body: JSON.stringify(payload),
});
renderPreview(preview);
page.toast("Файл проверен");
});
});
document.querySelector("#importForm").addEventListener("submit", async (event) => {
event.preventDefault();
await page.runAction(document.querySelector("#importBtn"), "Импортирую...", async () => {
const payload = selectedPayload || await readSelectedFile();
const result = await page.api("/my/import", {
method: "POST",
body: JSON.stringify(payload),
});
renderPreview(result.preview);
document.querySelector("#importSummary").insertAdjacentHTML("afterbegin", `
<div class="tip-card">
Импортировано: авто ${result.imported.vehicles_created}, заправок ${result.imported.fuel_entries}, сервисных записей ${result.imported.service_entries}, расходов ${result.imported.expense_entries}.
</div>
`);
page.toast("Импорт завершен");
});
});
page.boot(async () => {});