import { createFileRoute } from '@tanstack/react-router' import { useTranslation } from 'react-i18next' import { useAuth } from 'react-oidc-context' import { Button, EmptyState, LoadingBlock, PageHeader, Panel, QueryErrorState, Switch, } from '@/ui' import { useMyNotificationPreferences } from '@/api/queries' import { useUpdateNotificationPreferences } from '@/api/mutations' import type { NotificationPreferences } from '@/api/client' export const Route = createFileRoute('/me/notifications/preferences')({ component: NotificationPreferencesPage, }) /** * Phase B — UI для per-channel notification opt-in/out. * *

Email — единственный actually wired channel в Phase A/B (через Spring * \`spring.mail.host\`). Express + Telegram toggles disabled с подсказкой * "Скоро" — backend хранит preference (на случай early opt-in), но dispatcher * никогда не пошлёт пока channel bean не зарегистрирован. * *

Optimistic update — toggle flips мгновенно, network roundtrip ~200ms * почти не виден. onError refetch восстанавливает state если PUT 4xx/5xx. */ function NotificationPreferencesPage() { const { t } = useTranslation() const auth = useAuth() const prefsQ = useMyNotificationPreferences() const updateMut = useUpdateNotificationPreferences() if (!auth.isAuthenticated) { return ( auth.signinRedirect()}> {t('auth.login', { defaultValue: 'Войти' })} } /> ) } if (prefsQ.isLoading) { return } if (prefsQ.error) { return prefsQ.refetch()} /> } const prefs: NotificationPreferences = prefsQ.data ?? { email: true, express: false, telegram: false, } const handleToggle = (channel: keyof NotificationPreferences, next: boolean) => { updateMut.mutate({ ...prefs, [channel]: next }) } return (

handleToggle('email', v)} /> handleToggle('express', v)} disabled comingSoon /> handleToggle('telegram', v)} disabled comingSoon />
{updateMut.isError && (
{t('prefs.saveError', { defaultValue: 'Не удалось сохранить настройки. Попробуйте ещё раз.', })}
)} ) } type ToggleRowProps = { label: string description: string checked: boolean onChange: (v: boolean) => void disabled?: boolean comingSoon?: boolean } function ToggleRow({ label, description, checked, onChange, disabled, comingSoon, }: ToggleRowProps) { const { t } = useTranslation() return (
{label} {comingSoon && ( {t('prefs.comingSoon', { defaultValue: 'Скоро' })} )}

{description}

) }