/* ============================================================
Клиентский кабинет — #/c/cabinet
Доступен только роли client.
============================================================ */
const CabinetScreen = (function () {
function escHtml(s) {
return String(s == null ? "" : s)
.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
}
function fmtDate(iso) {
if (!iso) return "—";
try {
return new Date(iso).toLocaleDateString("ru-RU", { day: "numeric", month: "short" });
} catch { return iso.slice(0, 10); }
}
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 }),
});
return await res.json();
} catch (e) {
if (e.name === "AbortError") throw new Error("Сервер не отвечает");
throw e;
} finally { clearTimeout(t); }
}
const STATUS_LABELS = {
draft: "📝 Черновик",
sent: "📨 Отправлен",
reviewed: "✅ Просмотрен",
approved: "🎉 Принят",
rejected: "❌ Отклонён",
created: "🆕 Создана",
scheduled: "📅 Запланирована",
in_progress: "🔨 В работе",
done: "✅ Завершена",
cancelled: "❌ Отменена",
};
function statusChip(status) {
const label = STATUS_LABELS[status] || status || "—";
return `${escHtml(label)} `;
}
// ── Блок «Менеджер» ──────────────────────────────────────────────────────
function renderManagerBlock(mgr) {
if (!mgr?.full_name) return "";
const tgLink = mgr.tg_id ? `📩 Написать ` : "";
return `
Мой менеджер
${escHtml(mgr.full_name)}
${mgr.salon ? `
${escHtml(mgr.salon)}
` : ""}
${tgLink}
`;
}
// ── Блок «Подборы» ───────────────────────────────────────────────────────
function renderProposalsBlock(proposals) {
if (!proposals?.length) {
return `
Мои подборы
Подборов пока нет
🛒 Запросить подбор
`;
}
const items = proposals.slice(0, 3).map(p => `
Подбор от ${escHtml(fmtDate(p.created_at))}
${p.n_categories || 0} категор. · ${p.n_variants || 0} вар.
${statusChip(p.status)}
`).join("");
return `
Мои подборы
${proposals.length > 3 ? `Все ${proposals.length} ` : ""}
${items}
🛒 Новый подбор
`;
}
// ── Блок «Сборки» ────────────────────────────────────────────────────────
function renderAssembliesBlock(assemblies) {
if (!assemblies?.length) {
return `
Мои сборки
Сборок пока нет
`;
}
const items = assemblies.slice(0, 3).map(a => `
${escHtml(a.address || "Адрес не указан")}
${escHtml(fmtDate(a.scheduled_at || a.ts))}
${statusChip(a.status)}
`).join("");
return `
Мои сборки
${items}
${assemblies.length > 3 ? `
+${assemblies.length - 3} ещё
` : ""}
`;
}
async function mount(container) {
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 = `
${(window.ICONS || {}).arrow_left || "‹"}
Мой кабинет
`;
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 [me, proposalsData, assembliesData] = await Promise.all([
_api("me"),
_api("proposal_list").catch(() => ({ proposals: [] })),
_api("assembly_list").catch(() => ({ assemblies: [] })),
]);
screen.innerHTML = "";
if (me.error) {
screen.innerHTML = `${escHtml(me.error)}
`;
return;
}
const u = me.user || {};
const initial = u.avatar_initial || (u.full_name || "К")[0].toUpperCase();
// Аватар + имя
screen.innerHTML = `
${escHtml(initial)}
${escHtml(u.full_name || "Клиент")}
Личный кабинет
${renderManagerBlock(me.manager)}
${renderProposalsBlock(proposalsData.proposals || [])}
${renderAssembliesBlock(assembliesData.assemblies || [])}
`;
// Навигация по data-href
screen.querySelectorAll("[data-href]").forEach(el => {
el.addEventListener("click", () => {
haptic && haptic("impact");
location.hash = el.dataset.href;
});
});
} catch (e) {
screen.innerHTML = `Ошибка: ${escHtml(e.message)}
`;
}
}
return { mount };
})();