/* ============================================================
Детальная карточка сборки — #/c/assembly/:id
Доступна клиенту, менеджеру, мастеру.
============================================================ */
const AssemblyDetailScreen = (function () {
function escHtml(s) {
return String(s == null ? "" : s)
.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
}
function fmtDate(iso) {
if (!iso) return null;
try {
return new Date(iso).toLocaleDateString("ru-RU", {
day: "numeric", month: "long", year: "numeric",
hour: "2-digit", minute: "2-digit"
});
} catch { return iso.slice(0, 16).replace("T", " "); }
}
async function _api(path, body = {}) {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), 15000);
try {
const res = await fetch(`${BACKEND_URL}/api/${path}`, {
method: "POST", signal: ctrl.signal,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ initData: tg?.initData || "", initDataUnsafe: tg?.initDataUnsafe || null, ...body }),
});
if (!res.ok) throw new Error(`Ошибка сервера (${res.status})`);
return await res.json();
} catch (e) {
if (e.name === "AbortError") throw new Error("Сервер не отвечает");
throw e;
} finally { clearTimeout(t); }
}
const STATUS = {
created: { icon: "🆕", text: "Создана", color: "#8e8e8e" },
scheduled: { icon: "📅", text: "Запланирована", color: "#2980B9" },
in_progress: { icon: "🔨", text: "В процессе", color: "#F39C12" },
done: { icon: "✅", text: "Завершена", color: "#27AE60" },
cancelled: { icon: "❌", text: "Отменена", color: "#C0392B" },
};
function row(label, value, opts = {}) {
if (!value) return "";
return `
${escHtml(label)}
${opts.html ? value : escHtml(value)}
`;
}
async function mount(container, assemblyId) {
container.innerHTML = "";
document.body.classList.remove("has-bottom-nav");
const oldNav = document.getElementById("bottom-nav");
if (oldNav) oldNav.remove();
// Header
const h = document.createElement("header");
h.className = "podbor-header";
h.innerHTML = `
Сборка кухни
`;
h.querySelector(".podbor-back").addEventListener("click", () => {
haptic && haptic("impact");
history.back();
});
container.appendChild(h);
const screen = document.createElement("div");
screen.className = "podbor-screen";
screen.innerHTML = ``;
container.appendChild(screen);
try {
const data = await _api("assembly_detail", { assembly_id: assemblyId });
if (data.error) {
screen.innerHTML = `${escHtml(data.error)}
`;
return;
}
const sl = STATUS[data.status] || { icon: "🔧", text: data.status, color: "#8e8e8e" };
// Статус-баннер
const statusBanner = `
${sl.icon}
${escHtml(sl.text)}
ID: ${escHtml(data.id)}
`;
// Основные данные
const mainBlock = `
${row("Адрес", data.address)}
${data.kitchen_price ? row("Стоимость кухни", Number(data.kitchen_price).toLocaleString("ru-RU") + " ₽") : ""}
${data.kitchen_price ? row("Стоимость сборки", Number(Math.round(data.kitchen_price * 0.09)).toLocaleString("ru-RU") + " ₽", {color: "var(--accent)"}) : ""}
${row("Объём работ", data.scope_of_work)}
${row("Дата сборки", fmtDate(data.scheduled_at))}
${row("Начало", fmtDate(data.started_at))}
${row("Завершение", fmtDate(data.completed_at))}
`;
// Заметка менеджера
const noteBlock = data.manager_note ? `
Заметка
${escHtml(data.manager_note)}
` : "";
// Фото результата
const photosAfter = (data.photos_after || []).filter(Boolean);
const photosBlock = photosAfter.length ? `
Фото результата
${photosAfter.map(u => `
`).join("")}
` : "";
// Подпись
const signBlock = data.signed_by_name ? `
Принято клиентом
${escHtml(data.signed_by_name)}
${escHtml(fmtDate(data.signed_at) || "")}
` : "";
// Кнопка Google Calendar
const calBtn = data.gcal_event_url ? `
` : "";
screen.innerHTML = statusBanner + mainBlock + noteBlock + photosBlock + signBlock + calBtn +
``;
} catch (e) {
screen.innerHTML = `Ошибка: ${escHtml(e.message)}
`;
}
}
return { mount };
})();