const page = CarPassPage; const state = { cars: [], catalog: [], selectedCarId: null, }; const params = new URLSearchParams(window.location.search); function selectedCar() { return state.cars.find((item) => item.id === state.selectedCarId) || null; } function ensureOption(select, value) { if (!value) return; if (![...select.options].some((option) => option.value === value)) { select.insertAdjacentHTML("beforeend", ``); } } function selectedModel() { const makeName = document.querySelector("#makeSelect").value; const modelName = document.querySelector("#modelSelect").value; const make = state.catalog.find((item) => item.name === makeName); return make?.models?.find((item) => item.name === modelName) || null; } function syncModels(modelValue = "", trimValue = "") { const makeName = document.querySelector("#makeSelect").value; const modelSelect = document.querySelector("#modelSelect"); const models = state.catalog.find((item) => item.name === makeName)?.models || []; modelSelect.disabled = !models.length; modelSelect.innerHTML = models.length ? `` + models.map((model) => ``).join("") : ``; ensureOption(modelSelect, modelValue); modelSelect.value = modelValue || ""; syncTrims(trimValue); } function syncTrims(trimValue = "") { const trimSelect = document.querySelector("#trimSelect"); const trims = selectedModel()?.trims || []; trimSelect.disabled = !trims.length; trimSelect.innerHTML = trims.length ? `` + trims.map((trim) => ``).join("") : ``; ensureOption(trimSelect, trimValue); trimSelect.value = trimValue || ""; const trim = trims.find((item) => item.name === trimSelect.value); const fuel = document.querySelector('[name="fuel_type"]'); if (trim?.fuel_type && !fuel.value) fuel.value = trim.fuel_type; if (trim?.body_type && !document.querySelector('[name="body_type"]')?.value) { const bodyInput = document.querySelector('[name="body_type"]'); if (bodyInput) bodyInput.value = trim.body_type; } } async function loadCatalog() { state.catalog = await page.api("/catalog/makes"); const makeSelect = document.querySelector("#makeSelect"); const makes = [...state.catalog].sort((a, b) => a.name.localeCompare(b.name, "ru")); makeSelect.innerHTML = `` + makes .map((make) => ``) .join(""); makeSelect.addEventListener("change", () => syncModels()); document.querySelector("#modelSelect").addEventListener("change", () => syncTrims()); document.querySelector("#trimSelect").addEventListener("change", () => syncTrims(document.querySelector("#trimSelect").value)); } async function loadCars() { state.cars = await page.api(`/cars?owner_id=${page.state.user.id}`); const routeCarId = Number(params.get("car_id") || 0); if (params.get("action") === "new") state.selectedCarId = null; else if (routeCarId && state.cars.some((item) => item.id === routeCarId)) state.selectedCarId = routeCarId; else if (!state.selectedCarId && state.cars.length) state.selectedCarId = state.cars[0].id; renderVehicles(); fillForm(); } function renderVehicles() { const root = document.querySelector("#vehicleList"); root.innerHTML = state.cars.length ? state.cars.map((car) => ` `).join("") : `
Автомобилей пока нет. Заполните форму справа.
`; root.querySelectorAll("[data-vehicle]").forEach((button) => { button.addEventListener("click", () => { state.selectedCarId = Number(button.dataset.vehicle); renderVehicles(); fillForm(); }); }); } function setValue(form, name, value) { const input = form.elements[name]; if (!input) return; input.value = value ?? ""; } function fillForm() { const form = document.querySelector("#vehicleProfileForm"); const car = selectedCar(); form.reset(); document.querySelector("#deleteVehicleBtn").classList.toggle("hidden", !car); document.querySelector("#pageTitle").textContent = car ? car.name : "Новый автомобиль"; document.querySelector("#pageHint").textContent = car ? [car.make, car.model, car.year, car.license_plate_display].filter(Boolean).join(" · ") || "Заполните недостающие данные паспорта." : "Создайте карточку, а потом дополняйте ее по мере обслуживания."; if (!car) { syncModels(); return; } [ "name", "year", "plate_number", "vin", "current_odometer", "fuel_type", "engine_volume_l", "transmission", "drive_type", "engine_oil_type", "engine_oil_volume_l", "transmission_fluid_type", "transmission_fluid_volume_l", "coolant_type", "brake_fluid_type", "tire_size", "oil_change_interval_km", "purchase_price", "purchase_date", "purchase_type", "notes", ].forEach((name) => setValue(form, name, car[name])); ensureOption(form.elements.make, car.make); form.elements.make.value = car.make || ""; syncModels(car.model || "", car.trim || ""); } function payloadFromForm(form) { const data = page.formData(form); return { name: data.name, make: data.make || null, model: data.model || null, trim: data.trim || null, year: page.numberOrNull(data.year), plate_number: data.plate_number || null, vin: data.vin || null, current_odometer: page.numberOrNull(data.current_odometer), fuel_type: data.fuel_type || null, engine_volume_l: page.numberOrNull(data.engine_volume_l), transmission: data.transmission || null, drive_type: data.drive_type || null, engine_oil_type: data.engine_oil_type || null, engine_oil_volume_l: page.numberOrNull(data.engine_oil_volume_l), transmission_fluid_type: data.transmission_fluid_type || null, transmission_fluid_volume_l: page.numberOrNull(data.transmission_fluid_volume_l), coolant_type: data.coolant_type || null, brake_fluid_type: data.brake_fluid_type || null, tire_size: data.tire_size || null, oil_change_interval_km: page.numberOrNull(data.oil_change_interval_km), purchase_price: page.numberOrNull(data.purchase_price), purchase_date: data.purchase_date || null, purchase_type: data.purchase_type || "unknown", purchase_currency: page.state.user?.currency || "RUB", currency: page.state.user?.currency || "RUB", notes: data.notes || null, }; } document.querySelector("#newVehicleBtn").addEventListener("click", () => { state.selectedCarId = null; renderVehicles(); fillForm(); }); document.querySelector("#vehicleProfileForm").addEventListener("submit", async (event) => { event.preventDefault(); const form = event.currentTarget; const car = selectedCar(); await page.runAction(document.querySelector("#saveVehicleBtn"), "Сохраняю паспорт...", async () => { const saved = await page.api(car ? `/cars/${car.id}` : "/cars", { method: car ? "PATCH" : "POST", body: JSON.stringify(payloadFromForm(form)), }); state.selectedCarId = saved.id; await loadCars(); page.toast("Паспорт сохранен"); }); }); document.querySelector("#deleteVehicleBtn").addEventListener("click", async (event) => { const car = selectedCar(); if (!car || !window.confirm(`Удалить автомобиль «${car.name}» и все его записи?`)) return; await page.runAction(event.currentTarget, "Удаляю автомобиль...", async () => { await page.api(`/cars/${car.id}`, { method: "DELETE" }); state.selectedCarId = null; await loadCars(); page.toast("Автомобиль удален"); }); }); page.boot(async () => { await loadCatalog(); await loadCars(); });