ux(admin): lock-icons на admin-only табах + LS.state helpers

1) LOCK-ICONS на admin-only табах
   Раньше: 7 табов (Магазин, Геймификация, Шаблоны, Симуляции, Игры,
   Доступные тесты, Права доступа) скрывались от учителей через
   display:none. Учитель не знал что они существуют — discoverability 0.

   Теперь:
   - Все табы видны всем, но для не-админа добавлен .locked класс
   - .locked: opacity .42, cursor not-allowed, lock-icon справа
   - title=\"Только для администраторов\" — нативный tooltip
   - switchTab() при клике на .locked показывает toast вместо
     переключения

   Эффект: учитель видит границы своих прав; знает что есть в системе,
   но не доступно ему — может попросить админа дать доступ.

2) LS.state — общий helper для loading/empty/error состояний
   api.js:381 — добавлен LS.state с тремя методами:

   LS.state.loading(el, msg?)           — спиннер + опц. текст
   LS.state.empty(el, msg, icon='inbox') — пустое состояние с иконкой
   LS.state.error(el, err, retryFn?)    — красная иконка + текст
                                          + опц. кнопка «Повторить»

   Все три используют один CSS (.ls-state*) с одним визуальным языком.
   inject стилей лениво (id=ls-state-style).

   Демо-миграция: 3 error-handler'а в admin.js (Stats / Users /
   Sessions) переписаны на LS.state.error с retry-функцией. Юзер
   теперь может нажать «Повторить» вместо перезагрузки страницы.

   Остальные 20+ inline error/empty/spinner'ов в admin.js — для
   постепенной миграции (паттерн установлен).
This commit is contained in:
Maxim Dolgolyov
2026-05-16 19:56:58 +03:00
parent ffd7bac0ac
commit 6b7d0355b6
3 changed files with 88 additions and 11 deletions
+6
View File
@@ -229,6 +229,12 @@
font-weight: 700;
}
.admin-nav-item.active svg { opacity: 1; color: var(--violet); }
.admin-nav-item.locked { opacity: .42; cursor: not-allowed; }
.admin-nav-item.locked:hover { background: transparent; color: var(--text-3); }
.admin-nav-item.locked svg { opacity: .55; }
.admin-nav-item.locked .adm-lock {
margin-left: auto; width: 11px; height: 11px; opacity: .8; flex-shrink: 0;
}
.admin-main { flex: 1; min-width: 0; padding-left: 28px; }