# Спецификация: убрать канал «Push» из портала (находка G4)

## Цель

Убрать неработающий канал уведомлений «Push» из пользовательского интерфейса портала, оставив каналы «В приложении» (inapp) и «Email». Push не доставляется (нет ServiceWorker и пустые VAPID-ключи), поэтому показывать его платящему клиенту нельзя.

## Контекст и границы {#D1}

Канал «Push» — один из трёх каналов в матрице настроек уведомлений на вкладке «Настройки → Уведомления»: матрица из 8 событий, умноженных на 3 канала (inapp / push / email). Канал push никогда не доставляет сообщения. В рамках этой работы убирается ТОЛЬКО пользовательская видимость канала push в этой матрице.

Остаются без изменений: 8 событий, каналы inapp и email, переключатель звуковых алертов, логика сохранения и отмены, backend-эндпоинты, схема БД и уже сохранённые значения `users.notification_preferences`. Эта работа касается одного Vue-компонента и одного его теста.

## Изменение интерфейса {#D2}

В компоненте `app/resources/js/views/settings/NotificationsTab.vue`:

- из массива `CHANNELS` удаляется элемент с `id: 'push'` (строка `{ id: 'push', label: 'Push', description: 'Web-push в браузер (включится в Post-MVP).' },`) — в массиве остаются `inapp` и `email`;
- поясняющий текст «Матрица 8 событий × 3 каналов (соответствует `users.notification_preferences` в БД). Push активируется в Post-MVP.» заменяется на «Матрица 8 событий × 2 каналов (соответствует `users.notification_preferences` в БД).»;
- в `<style scoped>` свойство `grid-template-columns` у `.prefs-head, .prefs-row` меняется с `1fr 110px 110px 130px` (4 колонки: событие + 3 канала) на `1fr 110px 130px` (3 колонки: событие + 2 канала).

Контракт после правки: матрица рендерит для каждого из 8 событий ровно 2 чекбокса-канала (inapp и email); чекбокса/заголовка «Push» в DOM нет.

## Сохранение совместимости данных {#D3}

Тип `NotificationChannel` в `app/resources/js/api/auth.ts` (`'inapp' | 'push' | 'email'`) НЕ меняется. Это сохраняет валидность уже хранимых в БД значений с ключом `push` и mock-данных в тестах. Функция `buildPrefs()` строит prefs по массиву `CHANNELS`, поэтому удаление push из `CHANNELS` автоматически прекращает запись push без правки типа.

Edge-case: пользователь с ранее сохранённым `push: true` — значение остаётся в БД, в интерфейсе не показывается, на доставку не влияет (push и так не доставлялся). Никакой миграции данных не требуется.

## Обновление теста {#D4}

В `app/tests/Frontend/NotificationsTab.spec.ts` тест «содержит ровно 3 канала (inapp/push/email) — НЕ sms» приводится к новому контракту «содержит ровно 2 канала (inapp/email) — без push и sms»: проверяет наличие подстрок «В приложении» и «Email» и отсутствие подстрок «Push» и «SMS».

Конвенция: остальные тесты файла (8 событий, чтение prefs из auth.user, save/reset, звук) не меняются — `push` в их ассертах не участвует. Mock-данные `notification_preferences` с ключом `push` остаются валидными по типу из {#D3}.

## Критерий приёмки {#D5}

1. Прогон `vitest` по файлу `NotificationsTab` — зелёный.
2. Полный фронтовый прогон `vitest` — без новых падений.
3. В отрендеренной вкладке нет заголовка/чекбоксов «Push»; подстроки «В приложении» и «Email» присутствуют; все 8 событий на месте.

```verified-context-json
[
  {"id":"vc1","kind":"EXTRACTED","ref":"app/resources/js/views/settings/NotificationsTab.vue","anchor":"{ id: 'push', label: 'Push'"},
  {"id":"vc2","kind":"EXTRACTED","ref":"app/resources/js/api/auth.ts","anchor":"NotificationChannel = 'inapp' | 'push'"}
]
```
