This commit is contained in:
179
web/static/sto_settings.js
Normal file
179
web/static/sto_settings.js
Normal file
@@ -0,0 +1,179 @@
|
||||
const page = CarPassPage;
|
||||
|
||||
const APPROVED_STATUSES = new Set(["approved", "verified"]);
|
||||
const MANAGER_ROLES = new Set(["owner", "manager"]);
|
||||
const state = {
|
||||
centers: [],
|
||||
activeCenterId: null,
|
||||
catalog: null,
|
||||
};
|
||||
|
||||
function activeCenter() {
|
||||
return state.centers.find((item) => item.id === state.activeCenterId) || null;
|
||||
}
|
||||
|
||||
function roleLabel(role) {
|
||||
return { owner: "Владелец", manager: "Менеджер", receptionist: "Администратор", mechanic: "Механик" }[role] || role || "СТО";
|
||||
}
|
||||
|
||||
function timeValue(value) {
|
||||
return String(value || "").slice(0, 5);
|
||||
}
|
||||
|
||||
function setScheduleForm(settings) {
|
||||
const form = document.querySelector("#bookingSettingsForm");
|
||||
form.open_time.value = timeValue(settings.open_time || "09:00");
|
||||
form.close_time.value = timeValue(settings.close_time || "18:00");
|
||||
form.lunch_break_start.value = timeValue(settings.lunch_break_start || "");
|
||||
form.lunch_break_end.value = timeValue(settings.lunch_break_end || "");
|
||||
form.slot_duration_minutes.value = settings.slot_duration_minutes ?? 30;
|
||||
form.booking_buffer_minutes.value = settings.booking_buffer_minutes ?? 0;
|
||||
form.max_parallel_bookings.value = settings.max_parallel_bookings ?? 1;
|
||||
form.timezone.value = settings.timezone || "Asia/Seoul";
|
||||
form.accepts_online_booking.checked = settings.accepts_online_booking !== false;
|
||||
const days = new Set(settings.working_days || [0, 1, 2, 3, 4]);
|
||||
form.querySelectorAll('[name="working_days"]').forEach((input) => {
|
||||
input.checked = days.has(Number(input.value));
|
||||
});
|
||||
}
|
||||
|
||||
function schedulePayload(form, centerId) {
|
||||
const data = page.formData(form);
|
||||
const workingDays = [...form.querySelectorAll('[name="working_days"]:checked')].map((input) => Number(input.value));
|
||||
return {
|
||||
service_center_id: centerId,
|
||||
working_days: workingDays,
|
||||
open_time: data.open_time || "09:00",
|
||||
close_time: data.close_time || "18:00",
|
||||
lunch_break_start: data.lunch_break_start || null,
|
||||
lunch_break_end: data.lunch_break_end || null,
|
||||
timezone: data.timezone || "Asia/Seoul",
|
||||
slot_duration_minutes: Number(data.slot_duration_minutes || 30),
|
||||
booking_buffer_minutes: Number(data.booking_buffer_minutes || 0),
|
||||
max_parallel_bookings: Number(data.max_parallel_bookings || 1),
|
||||
accepts_online_booking: Boolean(data.accepts_online_booking),
|
||||
};
|
||||
}
|
||||
|
||||
function catalogPayload(form, centerId) {
|
||||
const data = page.formData(form);
|
||||
const isWork = data.item_type === "work";
|
||||
return {
|
||||
service_center_id: centerId,
|
||||
item_type: data.item_type,
|
||||
title: data.title,
|
||||
category: data.category || null,
|
||||
description: data.description || null,
|
||||
work_type: isWork ? (data.category || "other") : null,
|
||||
product_type: isWork ? null : (data.category || "other"),
|
||||
brand: data.brand || null,
|
||||
sku: data.sku || null,
|
||||
unit: data.unit || (isWork ? "pcs" : "pcs"),
|
||||
default_quantity: page.numberOrNull(data.default_quantity) || 1,
|
||||
default_unit_price: page.numberOrNull(data.default_unit_price) || 0,
|
||||
viscosity: data.viscosity || null,
|
||||
specification: data.specification || null,
|
||||
};
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
const center = activeCenter();
|
||||
document.querySelector("#centerSelect").innerHTML = state.centers
|
||||
.map((item) => `<option value="${item.id}">${page.escapeHtml(item.display_name || item.name)}</option>`)
|
||||
.join("");
|
||||
if (center) document.querySelector("#centerSelect").value = String(center.id);
|
||||
document.querySelector("#settingsTitle").textContent = center ? (center.display_name || center.name) : "Нет доступной СТО";
|
||||
document.querySelector("#settingsHint").textContent = center
|
||||
? [center.city, center.address].filter(Boolean).join(", ") || "Заполните график и каталог для команды."
|
||||
: "Настройки доступны владельцу или менеджеру подтвержденной СТО.";
|
||||
document.querySelector("#roleBadge").textContent = center ? roleLabel(center.employee_role) : "Нет доступа";
|
||||
}
|
||||
|
||||
function renderCatalog() {
|
||||
const root = document.querySelector("#catalogList");
|
||||
const centerId = activeCenter()?.id;
|
||||
const items = (state.catalog?.items || []).filter((item) => item.service_center_id === centerId);
|
||||
root.innerHTML = items.length
|
||||
? items.map((item) => `
|
||||
<div class="stack-item">
|
||||
<strong>${page.escapeHtml(item.title)}</strong>
|
||||
<small>${page.escapeHtml([item.item_type === "work" ? "Работа" : "Материал", item.category, item.brand, item.sku].filter(Boolean).join(" · "))}</small>
|
||||
<small>${page.escapeHtml(item.default_quantity)} ${page.escapeHtml(item.unit)} · ${page.escapeHtml(item.default_unit_price)}</small>
|
||||
</div>
|
||||
`).join("")
|
||||
: `<div class="empty">Пока нет собственных позиций. Системный каталог уже доступен в заказ-нарядах.</div>`;
|
||||
}
|
||||
|
||||
async function loadCenters() {
|
||||
const centers = await page.api("/service-centers/my");
|
||||
state.centers = centers.filter((center) =>
|
||||
APPROVED_STATUSES.has(center.verification_status) && MANAGER_ROLES.has(center.employee_role || "owner"),
|
||||
);
|
||||
if (!state.activeCenterId && state.centers.length) state.activeCenterId = state.centers[0].id;
|
||||
if (state.activeCenterId && !state.centers.some((item) => item.id === state.activeCenterId)) {
|
||||
state.activeCenterId = state.centers[0]?.id || null;
|
||||
}
|
||||
renderHeader();
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
const center = activeCenter();
|
||||
if (!center) {
|
||||
document.querySelector("#bookingSettingsForm").classList.add("hidden");
|
||||
document.querySelector("#catalogForm").classList.add("hidden");
|
||||
document.querySelector("#catalogList").innerHTML = `<div class="empty">Нет подтвержденной СТО с ролью владельца или менеджера.</div>`;
|
||||
return;
|
||||
}
|
||||
document.querySelector("#bookingSettingsForm").classList.remove("hidden");
|
||||
document.querySelector("#catalogForm").classList.remove("hidden");
|
||||
const [settings, catalog] = await Promise.all([
|
||||
page.api(`/sto/settings/booking?service_center_id=${center.id}`),
|
||||
page.api(`/work-orders/catalog?service_center_id=${center.id}`),
|
||||
]);
|
||||
state.catalog = catalog;
|
||||
setScheduleForm(settings);
|
||||
renderCatalog();
|
||||
}
|
||||
|
||||
document.querySelector("#centerSelect").addEventListener("change", async (event) => {
|
||||
state.activeCenterId = Number(event.currentTarget.value);
|
||||
renderHeader();
|
||||
await loadSettings();
|
||||
});
|
||||
|
||||
document.querySelector("#bookingSettingsForm").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const center = activeCenter();
|
||||
if (!center) return;
|
||||
await page.runAction(document.querySelector("#saveScheduleBtn"), "Сохраняю график...", async () => {
|
||||
const settings = await page.api("/sto/settings/booking", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(schedulePayload(event.currentTarget, center.id)),
|
||||
});
|
||||
setScheduleForm(settings);
|
||||
page.toast("График сохранен");
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector("#catalogForm").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const center = activeCenter();
|
||||
if (!center) return;
|
||||
await page.runAction(document.querySelector("#saveCatalogBtn"), "Добавляю позицию...", async () => {
|
||||
await page.api("/work-orders/catalog", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(catalogPayload(event.currentTarget, center.id)),
|
||||
});
|
||||
event.currentTarget.reset();
|
||||
event.currentTarget.default_quantity.value = 1;
|
||||
event.currentTarget.default_unit_price.value = 0;
|
||||
state.catalog = await page.api(`/work-orders/catalog?service_center_id=${center.id}`);
|
||||
renderCatalog();
|
||||
page.toast("Позиция добавлена");
|
||||
});
|
||||
});
|
||||
|
||||
page.boot(async () => {
|
||||
await loadCenters();
|
||||
await loadSettings();
|
||||
});
|
||||
Reference in New Issue
Block a user