refactor: аудит роутера app.js — устранены 3 edge case

1. Дублирование: init() теперь вызывает routeByHash() вместо копии логики.
   #/master, #/me, #/c/cabinet теперь работают при прямом переходе по ссылке.
2. Множественные hashchange listeners: guard _hashListenerAdded.
3. #/picker → #/c/proposal в cabinet.js и me.js (неверный роут).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wasrusgen 2026-05-18 13:39:23 +03:00
parent c773adf05e
commit 7df9cc3901
3 changed files with 10 additions and 73 deletions

View File

@ -1625,9 +1625,13 @@ function hideSplash() {
}, wait); }, wait);
} }
let _hashListenerAdded = false;
async function init() { async function init() {
setupTelegram(); setupTelegram();
if (!_hashListenerAdded) {
window.addEventListener("hashchange", routeByHash); window.addEventListener("hashchange", routeByHash);
_hashListenerAdded = true;
}
const qp = new URLSearchParams(window.location.search); const qp = new URLSearchParams(window.location.search);
// Telegram ставит #tgWebAppData=... в hash при открытии — это НЕ наш роут. // Telegram ставит #tgWebAppData=... в hash при открытии — это НЕ наш роут.
@ -1658,75 +1662,8 @@ async function init() {
try { try {
const me = await fetchMe(); const me = await fetchMe();
window.__zovMe = me; // кешируем профиль для подэкранов window.__zovMe = me; // кешируем профиль для подэкранов
if (location.hash.startsWith("#/podbor")) { // Единая точка роутинга — routeByHash покрывает все маршруты
Podbor.mount(app); routeByHash();
hideSplash();
return;
}
if (location.hash.startsWith("#/clients")) {
Clients.mount(app);
hideSplash();
return;
}
if (location.hash.startsWith("#/measure")) {
Measurements.mount(app);
hideSplash();
return;
}
if (location.hash.startsWith("#/request")) {
MeasurementRequest.mount(app);
hideSplash();
return;
}
if (location.hash.startsWith("#/inbox/")) {
const id = location.hash.replace("#/inbox/", "");
renderInboxDetail(id);
hideSplash();
return;
}
if (location.hash === "#/inbox") {
if (typeof InboxScreen !== "undefined") InboxScreen.mount(app);
hideSplash();
return;
}
if (location.hash.startsWith("#/assembly")) {
Assembly.mount(app);
hideSplash();
return;
}
if (location.hash.startsWith("#/c/proposal")) {
app.innerHTML = "";
document.body.classList.remove("has-bottom-nav");
const oldNav = document.getElementById("bottom-nav");
if (oldNav) oldNav.remove();
if (typeof Proposals !== "undefined") {
Proposals.mountClient(app);
} else {
app.innerHTML = `<div class="error">Модуль подбора не загружен</div>`;
}
hideSplash();
return;
}
if (location.hash.startsWith("#/c/contract")) {
app.innerHTML = "";
document.body.classList.remove("has-bottom-nav");
const oldNavC = document.getElementById("bottom-nav");
if (oldNavC) oldNavC.remove();
if (typeof Proposals !== "undefined") {
Proposals.mountContractReview(app);
} else {
app.innerHTML = `<div class="error">Модуль не загружен</div>`;
}
hideSplash();
return;
}
if (me.role === "staff") {
renderStaff(me);
} else if (me.role === "manager") {
renderManager(me);
} else {
renderClient(me);
}
hideSplash(); hideSplash();
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -76,7 +76,7 @@ const CabinetScreen = (function () {
<div class="block" style="margin:12px 16px 0;"> <div class="block" style="margin:12px 16px 0;">
<div class="block-head">Мои подборы</div> <div class="block-head">Мои подборы</div>
<div style="padding:12px 0;color:var(--muted);font-size:13px;">Подборов пока нет</div> <div style="padding:12px 0;color:var(--muted);font-size:13px;">Подборов пока нет</div>
<button class="btn-primary btn-sm" data-href="#/picker" style="margin-top:4px;">🛒 Запросить подбор</button> <button class="btn-primary btn-sm" data-href="#/c/proposal" style="margin-top:4px;">🛒 Запросить подбор</button>
</div>`; </div>`;
} }
const items = proposals.slice(0, 3).map(p => ` const items = proposals.slice(0, 3).map(p => `
@ -96,7 +96,7 @@ const CabinetScreen = (function () {
${proposals.length > 3 ? `<span style="font-size:12px;color:var(--accent);cursor:pointer;" data-href="#/c/proposal">Все ${proposals.length}</span>` : ""} ${proposals.length > 3 ? `<span style="font-size:12px;color:var(--accent);cursor:pointer;" data-href="#/c/proposal">Все ${proposals.length}</span>` : ""}
</div> </div>
<div style="display:flex;flex-direction:column;gap:8px;padding-top:4px;">${items}</div> <div style="display:flex;flex-direction:column;gap:8px;padding-top:4px;">${items}</div>
<button class="btn-secondary btn-sm" data-href="#/picker" style="margin-top:10px;">🛒 Новый подбор</button> <button class="btn-secondary btn-sm" data-href="#/c/proposal" style="margin-top:10px;">🛒 Новый подбор</button>
</div>`; </div>`;
} }

View File

@ -158,7 +158,7 @@ const MeScreen = (function () {
<div class="block" style="margin:12px 16px 0;"> <div class="block" style="margin:12px 16px 0;">
<div class="podbor-cta-row" style="flex-wrap:wrap;gap:8px;padding-top:4px;"> <div class="podbor-cta-row" style="flex-wrap:wrap;gap:8px;padding-top:4px;">
<button class="btn-primary" data-href="#/c/cabinet">🏠 Мой кабинет</button> <button class="btn-primary" data-href="#/c/cabinet">🏠 Мой кабинет</button>
<button class="btn-secondary" data-href="#/picker">🛒 Подбор</button> <button class="btn-secondary" data-href="#/c/proposal">🛒 Подбор</button>
</div> </div>
</div> </div>
`; `;