import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { Link } from '@tanstack/react-router' import { ChevronLeft, Send, Undo2 } from 'lucide-react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { listAllMedia } from '@/features/admin/media/api' import { qk } from '@/shared/api/query-keys' import { cn } from '@/shared/lib/cn' import { useApiError } from '@/shared/lib/use-api-error' import { Badge } from '@/shared/ui/badge' import { Button } from '@/shared/ui/button' import { Card, CardContent } from '@/shared/ui/card' import { toast } from '@/shared/ui/toast-store' import { applyChannelTemplate, getChannel, getChannelTemplate, getSchedule, restoreChannelTemplate, } from './api' import { ApplyDialog } from './components/ApplyDialog' import { BumperCard } from './components/BumperCard' import { EntryTraceDialog } from './components/EntryTraceDialog' import { GridTab } from './components/GridTab' import { JunctionsCard } from './components/JunctionsCard' import { RulesCard } from './components/RulesCard' import { SchedulePreview } from './components/SchedulePreview' import { SettingsCard } from './components/SettingsCard' import { ViewerCard } from './components/ViewerCard' /** Вкладки экрана канала: настройки первыми — с них канал и начинается. */ const TABS = ['settings', 'grid', 'rules', 'junctions', 'bumpers', 'viewer', 'air'] as const type ChannelTab = (typeof TABS)[number] export function ChannelDetail({ channelId }: Readonly<{ channelId: string }>) { const { t } = useTranslation() const queryClient = useQueryClient() const [applyOpen, setApplyOpen] = useState(false) const [traceEntryId, setTraceEntryId] = useState(null) const [tab, setTab] = useState('settings') const { data: channel, isLoading } = useQuery({ queryKey: qk.channels.detail(channelId), queryFn: () => getChannel(channelId), }) const { data: template, error: templateError } = useQuery({ queryKey: qk.channels.template(channelId), queryFn: () => getChannelTemplate(channelId), }) const { data: ready } = useQuery({ queryKey: qk.media.ready, queryFn: () => listAllMedia({ statuses: ['Ready'] }), }) const { data: schedule } = useQuery({ queryKey: qk.channels.schedule(channelId), queryFn: () => getSchedule(channelId, new Date(), new Date(Date.now() + 12 * 3_600_000)), }) const invalidate = () => { void queryClient.invalidateQueries({ queryKey: qk.channels.detail(channelId) }) } const onError = useApiError() const applyMutation = useMutation({ mutationFn: () => applyChannelTemplate(channelId), onSuccess: (result) => { setApplyOpen(false) toast.success(t('admin.channels.applied', { count: result.added })) // Предупреждения показываем по одному: каждое указывает на конкретный слот. for (const warning of result.warnings) { const kind = t(`admin.channels.warnings.${warning.kind}`) toast.error(`${kind}: ${warning.details}`) } invalidate() }, onError, }) const restoreMutation = useMutation({ mutationFn: () => restoreChannelTemplate(channelId), onSuccess: (result) => { toast.success(t('admin.channels.restored', { count: result.slots })) // Ссылка на удалённую группу или стык вернуться не может — говорим об этом прямо. if (result.droppedRefs > 0) toast.error(t('admin.channels.restoreDropped', { count: result.droppedRefs })) invalidate() }, onError, }) if (isLoading || !channel) return

{t('common.loading')}

return (

{channel.name}

{channel.number !== null && № {channel.number}} {!channel.isEnabled && {t('admin.channels.disabled')}}
{/* Правка правил эфира не двигает — применение отдельной кнопкой. Рядом откат: правки сетки правятся на месте, и без этой кнопки вернуть «как в эфире» было бы нечем. */} {template?.hasPendingChanges && (
{t('admin.channels.pendingChanges')}
{/* Кнопки отката нет, пока сетку ни разу не применяли: возвращаться некуда. */} {template.canRestore && ( )}
)} {/* Вкладки вместо колонки карточек: экран канала перестал помещаться в один свиток. */} {tab === 'settings' && ( )} {tab === 'grid' && ( )} {tab === 'rules' && (template ? ( ) : (

{t('admin.channels.noTemplate')}

))} {tab === 'junctions' && ( )} {tab === 'bumpers' && ( )} {tab === 'viewer' && ( )} {tab === 'air' && ( )} {applyOpen && ( applyMutation.mutate()} onClose={() => setApplyOpen(false)} /> )} {traceEntryId && ( setTraceEntryId(null)} /> )}
) }