151 lines
6.6 KiB
JavaScript
151 lines
6.6 KiB
JavaScript
const page = CarPassPage;
|
||
|
||
const params = new URLSearchParams(window.location.search);
|
||
const state = {
|
||
centers: [],
|
||
vehicles: [],
|
||
selectedCenterId: Number(params.get("service_center_id") || 0) || null,
|
||
};
|
||
|
||
const SERVICE_NAMES = {
|
||
oil_change: "Замена масла",
|
||
diagnostics: "Диагностика",
|
||
maintenance: "Техническое обслуживание",
|
||
tire_service: "Шиномонтаж",
|
||
brakes: "Тормозная система",
|
||
repair: "Ремонт",
|
||
other: "Другое",
|
||
};
|
||
|
||
function selectedCenter() {
|
||
return state.centers.find((item) => item.id === state.selectedCenterId) || null;
|
||
}
|
||
|
||
function renderCenters() {
|
||
const root = document.querySelector("#serviceList");
|
||
root.innerHTML = state.centers.length
|
||
? state.centers.map((center) => `
|
||
<button type="button" class="service-list-card ${center.id === state.selectedCenterId ? "active" : ""}" data-center="${center.id}">
|
||
<strong>${page.escapeHtml(center.display_name || center.name)}</strong>
|
||
<small>${page.escapeHtml([center.city, center.address].filter(Boolean).join(", ") || "Адрес уточняется")}</small>
|
||
<small>${center.nearest_slot_at ? `Ближайшее окно: ${page.formatDateTime(center.nearest_slot_at)}` : "Онлайн-запись по графику СТО"}</small>
|
||
</button>
|
||
`).join("")
|
||
: `<div class="empty">Подходящих СТО не найдено.</div>`;
|
||
root.querySelectorAll("[data-center]").forEach((button) => {
|
||
button.addEventListener("click", async () => {
|
||
state.selectedCenterId = Number(button.dataset.center);
|
||
renderCenters();
|
||
renderBookingHead();
|
||
await loadSlots();
|
||
});
|
||
});
|
||
}
|
||
|
||
function renderBookingHead() {
|
||
const center = selectedCenter();
|
||
document.querySelector("#bookingTitle").textContent = center ? (center.display_name || center.name) : "Выберите сервис";
|
||
document.querySelector("#bookingHint").textContent = center
|
||
? [center.city, center.address, center.working_hours].filter(Boolean).join(" · ") || "Выберите удобное время записи."
|
||
: "Выберите СТО слева, потом автомобиль, услугу и свободное окно.";
|
||
}
|
||
|
||
function renderVehicles() {
|
||
const select = document.querySelector("#vehicleSelect");
|
||
select.innerHTML = state.vehicles.length
|
||
? state.vehicles.map((car) => `<option value="${car.id}">${page.escapeHtml([car.name, car.make, car.model, car.license_plate_display].filter(Boolean).join(" · "))}</option>`).join("")
|
||
: `<option value="">Сначала добавьте автомобиль</option>`;
|
||
select.disabled = !state.vehicles.length;
|
||
}
|
||
|
||
async function loadCenters(filters = {}) {
|
||
const query = new URLSearchParams();
|
||
query.set("has_slots", "true");
|
||
if (filters.city) query.set("city", filters.city);
|
||
if (filters.specialization) query.set("specialization", filters.specialization);
|
||
state.centers = await page.api(`/sto/catalog?${query.toString()}`);
|
||
if (state.selectedCenterId && !state.centers.some((item) => item.id === state.selectedCenterId)) {
|
||
state.centers = [await page.api(`/service-centers/${state.selectedCenterId}`).catch(() => null), ...state.centers].filter(Boolean);
|
||
}
|
||
if (!state.selectedCenterId && state.centers.length) state.selectedCenterId = state.centers[0].id;
|
||
renderCenters();
|
||
renderBookingHead();
|
||
}
|
||
|
||
async function loadVehicles() {
|
||
state.vehicles = await page.api("/my/vehicles");
|
||
renderVehicles();
|
||
}
|
||
|
||
async function loadSlots() {
|
||
const center = selectedCenter();
|
||
const select = document.querySelector("#slotSelect");
|
||
if (!center) {
|
||
select.innerHTML = `<option value="">Выберите СТО</option>`;
|
||
select.disabled = true;
|
||
return;
|
||
}
|
||
const form = document.querySelector("#bookingForm");
|
||
const data = page.formData(form);
|
||
const date = data.date || page.today();
|
||
const serviceType = data.service_type || "maintenance";
|
||
const duration = data.estimated_duration_minutes || "60";
|
||
document.querySelector("#slotHint").textContent = "Проверяю свободные окна...";
|
||
const slots = await page.api(`/sto/${center.id}/available-slots?service_type=${encodeURIComponent(serviceType)}&date_from=${date}&date_to=${date}&duration_minutes=${duration}`);
|
||
select.disabled = !slots.length;
|
||
select.innerHTML = slots.length
|
||
? slots.map((slot) => `<option value="${slot.start_at}">${page.formatDateTime(slot.start_at)} - ${page.formatDateTime(slot.end_at).slice(-5)}</option>`).join("")
|
||
: `<option value="">На эту дату окон нет</option>`;
|
||
document.querySelector("#slotHint").textContent = slots.length ? "Выберите удобное окно." : "Попробуйте другую дату или длительность.";
|
||
}
|
||
|
||
document.querySelector("#filterForm").addEventListener("submit", async (event) => {
|
||
event.preventDefault();
|
||
await page.runAction(event.currentTarget.querySelector("button"), "Ищу СТО...", async () => {
|
||
await loadCenters(page.formData(event.currentTarget));
|
||
await loadSlots();
|
||
});
|
||
});
|
||
|
||
["#serviceTypeSelect", "#durationSelect", "#bookingDateInput"].forEach((selector) => {
|
||
document.querySelector(selector).addEventListener("change", () => {
|
||
loadSlots().catch((error) => page.toast(error.message || "Не удалось обновить окна", "error"));
|
||
});
|
||
});
|
||
|
||
document.querySelector("#bookingForm").addEventListener("submit", async (event) => {
|
||
event.preventDefault();
|
||
const center = selectedCenter();
|
||
const data = page.formData(event.currentTarget);
|
||
if (!center) {
|
||
page.toast("Выберите СТО", "error");
|
||
return;
|
||
}
|
||
if (!data.vehicle_id) {
|
||
page.toast("Добавьте автомобиль перед записью", "error");
|
||
return;
|
||
}
|
||
await page.runAction(document.querySelector("#createBookingBtn"), "Отправляю заявку...", async () => {
|
||
await page.api("/appointments", {
|
||
method: "POST",
|
||
body: JSON.stringify({
|
||
service_center_id: center.id,
|
||
vehicle_id: Number(data.vehicle_id),
|
||
service_type: data.service_type,
|
||
service_name: SERVICE_NAMES[data.service_type] || "Обслуживание",
|
||
requested_start_at: data.slot,
|
||
estimated_duration_minutes: Number(data.estimated_duration_minutes || 60),
|
||
customer_comment: data.customer_comment || null,
|
||
}),
|
||
});
|
||
page.toast("Заявка отправлена в СТО");
|
||
window.setTimeout(() => { window.location.href = "/?section=appointments"; }, 700);
|
||
});
|
||
});
|
||
|
||
page.boot(async () => {
|
||
document.querySelector("#bookingDateInput").value = page.today();
|
||
await Promise.all([loadCenters(), loadVehicles()]);
|
||
await loadSlots();
|
||
});
|