add vehicle service profile settings

This commit is contained in:
VPN SaaS Dev
2026-05-12 04:52:42 +09:00
parent b5012ec6e7
commit e75697f83e
10 changed files with 496 additions and 5 deletions

View File

@@ -509,18 +509,25 @@ async function ensureUser() {
function hideAuthOverlay() {
document.querySelector("#authOverlay")?.classList.add("hidden");
document.body.classList.remove("auth-required");
}
async function showTelegramLogin() {
const overlay = document.querySelector("#authOverlay");
const slot = document.querySelector("#telegramLoginSlot");
const link = document.querySelector("#telegramLoginLink");
overlay?.classList.remove("hidden");
document.body.classList.add("auth-required");
if (!slot || slot.dataset.ready) return;
const botUsername = state.authConfig?.bot_username;
if (!botUsername) {
slot.textContent = "Telegram Login временно недоступен";
return;
}
if (link) {
link.href = `https://t.me/${botUsername}?start=web_login`;
link.classList.remove("hidden");
}
window.onTelegramAuth = async (user) => {
state.user = await api("/users/telegram-login", {
method: "POST",
@@ -538,6 +545,9 @@ async function showTelegramLogin() {
script.setAttribute("data-radius", "8");
script.setAttribute("data-request-access", "write");
script.setAttribute("data-onauth", "onTelegramAuth(user)");
script.addEventListener("error", () => {
slot.textContent = "Кнопка Telegram не загрузилась. Используй вход ниже.";
});
slot.dataset.ready = "true";
slot.appendChild(script);
}
@@ -565,6 +575,10 @@ function selectedCar() {
return state.cars.find((car) => car.id === state.selectedCarId) || null;
}
function numberOrNull(value) {
return value === "" || value == null ? null : Number(value);
}
function shiftMonths(base, count) {
const copy = new Date(base);
copy.setMonth(copy.getMonth() + count);
@@ -690,7 +704,7 @@ function updateHero(stats) {
const car = selectedCar();
document.querySelector("#selectedCarTitle").textContent = car?.name || t("Не выбран");
document.querySelector("#selectedCarMeta").textContent = car
? [car.make, car.model, car.trim, car.year].filter(Boolean).join(" ") || t("Без деталей")
? [car.make, car.model, car.trim, car.year, car.fuel_type].filter(Boolean).join(" ") || t("Без деталей")
: t("Добавь авто или выбери из списка");
document.querySelector("#summaryTotal").textContent = money(stats?.total_cost);
document.querySelector("#summaryConsumption").textContent = stats?.avg_consumption_l_per_100km
@@ -717,7 +731,7 @@ function renderCars() {
<span class="car-badge">${(car.make || car.name).slice(0, 2).toUpperCase()}</span>
<span>
<strong>${car.name}</strong>
<small>${[car.make, car.model, car.trim, car.year].filter(Boolean).join(" ") || t("Без деталей")}</small>
<small>${[car.make, car.model, car.trim, car.year, car.fuel_type].filter(Boolean).join(" ") || t("Без деталей")}</small>
</span>
</button>
`,
@@ -728,6 +742,45 @@ function renderCars() {
});
}
function setInputValue(form, name, value) {
if (form?.elements[name]) form.elements[name].value = value ?? "";
}
function fillCarProfileForm() {
const form = document.querySelector("#carProfileForm");
const hint = document.querySelector("#carProfileHint");
const car = selectedCar();
form.querySelectorAll("input, select, button").forEach((node) => {
node.disabled = !car;
});
if (!car) {
form.reset();
hint.textContent = t("Выбери автомобиль, чтобы настроить жидкости, расход и сервисные нормы.");
return;
}
hint.textContent = [car.make, car.model, car.trim, car.year].filter(Boolean).join(" ") || car.name;
[
"fuel_type",
"target_consumption_l_per_100km",
"fuel_tank_volume_l",
"engine_oil_type",
"engine_oil_volume_l",
"transmission_fluid_type",
"transmission_fluid_volume_l",
"coolant_type",
"brake_fluid_type",
"tire_pressure_front_bar",
"tire_pressure_rear_bar",
].forEach((name) => setInputValue(form, name, car[name]));
}
function openCarProfile() {
document.querySelector("#userDrawer").classList.remove("hidden");
document.querySelector("#carProfileSection").classList.remove("hidden");
fillCarProfileForm();
document.querySelector("#carProfileSection").scrollIntoView({ behavior: "smooth", block: "start" });
}
function renderStats(stats) {
const root = document.querySelector("#stats");
if (!stats) {
@@ -1051,6 +1104,7 @@ async function loadCars() {
state.selectedCarId = state.cars[0]?.id || null;
}
renderCars();
fillCarProfileForm();
await loadSelectedCar();
} finally {
document.body.classList.remove("loading");
@@ -1061,6 +1115,7 @@ async function loadCars() {
async function selectCar(carId) {
state.selectedCarId = carId;
renderCars();
fillCarProfileForm();
await loadSelectedCar();
}
@@ -1145,6 +1200,41 @@ document.querySelector("#carForm").addEventListener("submit", async (event) => {
});
});
document.querySelector("#carProfileForm").addEventListener("submit", async (event) => {
event.preventDefault();
const form = event.currentTarget;
const car = selectedCar();
if (!car) {
toast("Выбери автомобиль", "error");
return;
}
await runAction(form.querySelector('button[type="submit"]'), "Сохраняю...", async () => {
const data = formData(form);
const updated = await api(`/cars/${car.id}`, {
method: "PATCH",
body: JSON.stringify({
fuel_type: data.fuel_type || null,
target_consumption_l_per_100km: numberOrNull(data.target_consumption_l_per_100km),
fuel_tank_volume_l: numberOrNull(data.fuel_tank_volume_l),
engine_oil_type: data.engine_oil_type || null,
engine_oil_volume_l: numberOrNull(data.engine_oil_volume_l),
transmission_fluid_type: data.transmission_fluid_type || null,
transmission_fluid_volume_l: numberOrNull(data.transmission_fluid_volume_l),
coolant_type: data.coolant_type || null,
brake_fluid_type: data.brake_fluid_type || null,
tire_pressure_front_bar: numberOrNull(data.tire_pressure_front_bar),
tire_pressure_rear_bar: numberOrNull(data.tire_pressure_rear_bar),
}),
});
state.cars = state.cars.map((item) => (item.id === updated.id ? updated : item));
renderCars();
fillCarProfileForm();
await loadSelectedCar();
toast("Параметры сохранены");
haptic("success");
});
});
document.querySelector("#settingsForm").addEventListener("submit", async (event) => {
event.preventDefault();
const form = event.currentTarget;
@@ -1272,6 +1362,8 @@ document.querySelector("#openCarFormBtn").addEventListener("click", () => {
document.querySelector("#carFormSection").scrollIntoView({ behavior: "smooth", block: "start" });
});
document.querySelector("#openCarProfileBtn").addEventListener("click", openCarProfile);
document.querySelector("#openSettingsBtn").addEventListener("click", () => {
document.querySelector("#settingsSection").classList.remove("hidden");
document.querySelector("#localeSelect").value = state.user?.locale || "ru";