BR 6.1 → POS Desktop

Репозиторий: erp-pos-desktop (Tauri 2 + React) Спеки:

Задачи

Hook useIdleTimer

  • apps/desktop/src/hooks/useIdleTimer.ts:
    • Параметры: { minutes: number, enabled: boolean, onIdle: () => void }
    • Подключает глобальные listener’ы: mousemove, keydown, touchstart, click
    • Сбрасывает setTimeout при любой активности
    • На expire — вызывает onIdle()
    • Cleanup на unmount
  • Подключить hook в App.tsx внутри ProtectedRoute + RequireShift обёртки
  • Параметр enabled зависит от текущего route — выключен на /login, /standby, /order/success, /shift/open, /shift/close
  • При срабатывании — navigate('/standby')

Route /standby

  • Добавить route в App.tsx:
    <Route path="/standby" element={<ProtectedRoute><RequireShift><StandbyScreen /></RequireShift></ProtectedRoute>} />
  • Standby не должен требовать никаких дополнительных permissions кроме pos.access (уже есть в RequireShift)

Component StandbyScreen

  • apps/desktop/src/screens/StandbyScreen.tsx:
    • На mount — GET /pos/api/v1/marketing/active
    • Извлекает slides, standby_transition_seconds
    • Карусель с cross-fade transition (CSS opacity + transition: opacity 600ms)
    • setInterval на смену индекса каждые transition_seconds
    • Глобальный handler onClick/onTouchStart/onKeyDown (или на корневом div) → navigate(-1) (возврат на предыдущий route)
    • Empty state с логотипом Альфа ERP если slides.length === 0
    • Preload следующего слайда через new Image().src для устранения flash
  • CSS файл/inline стили: full-screen position: fixed; inset: 0; background: #000, картинки object-fit: cover

Расширение useSSE

  • apps/desktop/src/hooks/useSSE.ts — добавить event-listener:
    es.addEventListener('marketing.invalidate', (e) => {
      marketingChannel.emit('invalidate');
    });
  • В StandbyScreen подписаться на marketingChannel.on('invalidate', refetch) — при получении → reload активных слайдов
  • Корректировать currentIndex если slides.length изменился

API client

  • apps/desktop/src/api/marketing.ts:
    • getActiveMarketing(){ slides, standby_idle_minutes, standby_transition_seconds }
  • Использует существующий fetch-wrapper с JWT

Idle-config flow

  • Получение standby_idle_minutes — из ответа /pos/api/v1/marketing/active. Первый запрос делать при логине (preload), кэшировать в memory store
  • useIdleTimer использует значение из store (или fallback 5 мин если ещё не загружено)

Стили (Alfa)

  • Empty state — логотип «А Альфа ERP» из tokens (см. alfa-restyle)
  • Индикаторы слайда внизу — белые точки на тёмном фоне (минимализм)

Tauri-специфика

  • Standby должен работать как в Tauri-сборке, так и в web-сборке (через base path)
  • Никаких Tauri-инвайков не нужно для этой функциональности

Тесты

  • Manual e2e на test VPS (web-сборка https://erp-test.nirbi.ru/pos/)
  • Проверить на реальной кассе с Tauri-инсталлером (см. build-pos-desktop для сборки)

Зависимости

  • POS BFF — endpoint /marketing/active и SSE event должны работать
  • Store Service — хотя бы 1 активный слайд должен быть в БД для smoke-теста

Готово когда

  • После 5 мин неактивности касса автоматически переходит на /standby
  • Карусель листает слайды с правильным интервалом
  • Клик возвращает на /main
  • При изменении слайда в админке — изменение видно на кассе через ≤ 3 секунды (SSE)
  • Empty state работает (если нет активных слайдов — логотип Альфа)

Side-задачи

  • Понизить idle до 1 мин для удобного e2e теста, после теста — вернуть как было