fix(features): пустой блок флешкарт, лаба в сайдбаре, мигание (FOUC)

Три проблемы UX отключения модулей:
1) Пустой блок флешкарт на дашборде: виджет #w-flashcard — <div>, а скрытие шло
   только по [href]. Добавлена карта FEATURE_WIDGETS (флешкарты→#w-flashcard) —
   контейнер прячется целиком.
2) Лаборатория не уходила из сайдбара: не было ГЛОБАЛЬНОГО тумблера lab (только
   free-student). Добавлен в whitelist updateFeatures + GAME_FEATURES; map уже знал
   lab→/lab. Теперь выключение скрывает пункт и редиректит со страницы.
3) Мигание выключенных модулей (FOUC): hideDisabledFeatures асинхронный. Теперь
   loadFeatures кэширует /api/features в localStorage, а _applyFeatureCss инъектит
   <style id=ls-feat-hide> синхронно из кэша на ранней загрузке (api.js идёт до
   sidebar.js) — сайдбар/виджеты строятся уже скрытыми. Геймификация: класс
   no-gamification ставится на <html> (раньше body), ls.css правило body.no-gamification
   → .no-gamification (работает и для html, и для body).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-22 17:41:11 +03:00
parent d5fbd0168e
commit 83f0ba9c04
4 changed files with 87 additions and 51 deletions
+24 -24
View File
@@ -1039,7 +1039,7 @@ body {
body.no-class #lb-section { display: none !important; }
/* Gamification kill-switch.
When admin turns off the feature, body.no-gamification is set by
When admin turns off the feature, .no-gamification is set by
api.js/hideDisabledFeatures and EVERY XP / coin / streak / shop /
achievement / frame element must vanish — across the whole app,
not just the dashboard. The rules below cover:
@@ -1050,32 +1050,32 @@ body.no-class #lb-section { display: none !important; }
• a catch-all [data-gamified] hook that wraps any future block —
authors of new pages should wrap XP UI in a <div data-gamified>
instead of inventing new classes. */
body.no-gamification .gam-bar,
body.no-gamification .lb-widget,
body.no-gamification .achievements-section,
body.no-gamification #tab-btn-achievements,
body.no-gamification #tab-btn-shop,
body.no-gamification #tab-achievements,
body.no-gamification #tab-shop,
body.no-gamification #frames-section,
body.no-gamification .hero-xp-badge,
body.no-gamification .po-xp,
body.no-gamification .xp-card,
body.no-gamification .xp-bar,
body.no-gamification .xp-pill,
body.no-gamification .xp-badge,
.no-gamification .gam-bar,
.no-gamification .lb-widget,
.no-gamification .achievements-section,
.no-gamification #tab-btn-achievements,
.no-gamification #tab-btn-shop,
.no-gamification #tab-achievements,
.no-gamification #tab-shop,
.no-gamification #frames-section,
.no-gamification .hero-xp-badge,
.no-gamification .po-xp,
.no-gamification .xp-card,
.no-gamification .xp-bar,
.no-gamification .xp-pill,
.no-gamification .xp-badge,
/* challenges / еженедельные испытания (dashboard) */
body.no-gamification .ch-widget,
body.no-gamification #ch-section,
.no-gamification .ch-widget,
.no-gamification #ch-section,
/* серия/стрик: календарь, стат-кольцо, чипы на карточке питомца */
body.no-gamification .streak-cal,
body.no-gamification #sr-streak,
body.no-gamification .hc-pet .chip-streak,
body.no-gamification .hc-pet .chip-goal,
.no-gamification .streak-cal,
.no-gamification #sr-streak,
.no-gamification .hc-pet .chip-streak,
.no-gamification .hc-pet .chip-goal,
/* монеты (профиль) и xp-прогресс */
body.no-gamification #p-coins-row,
body.no-gamification .gam-progress,
body.no-gamification [data-gamified] { display: none !important; }
.no-gamification #p-coins-row,
.no-gamification .gam-progress,
.no-gamification [data-gamified] { display: none !important; }
/* ══════════════════════════════════════════
RESPONSIVE — SMALL PHONES (≤ 480px)