Add debug export functionality for channel analysis
Implemented a new endpoint for exporting debug data related to channel scheduling, allowing users to download an archive containing channel settings, slot states, and trace information. Updated the frontend to include a button for triggering the export, along with necessary API adjustments for handling the download. Enhanced localization strings to support the new debug export feature in both English and Russian. Updated .gitignore to include debug export files while ensuring the directory structure is maintained for development.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { ChevronLeft, Send, Undo2 } from 'lucide-react'
|
||||
import { Bug, ChevronLeft, Send, Undo2 } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { listAllMedia } from '@/features/admin/media/api'
|
||||
@@ -12,7 +12,13 @@ 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, restoreChannelTemplate } from './api'
|
||||
import {
|
||||
applyChannelTemplate,
|
||||
exportChannelDebug,
|
||||
getChannel,
|
||||
getChannelTemplate,
|
||||
restoreChannelTemplate,
|
||||
} from './api'
|
||||
import { AirSchedule } from './components/AirSchedule'
|
||||
import { ApplyDialog } from './components/ApplyDialog'
|
||||
import { ApplyReportDialog } from './components/ApplyReportDialog'
|
||||
@@ -55,6 +61,12 @@ export function ChannelDetail({ channelId }: Readonly<{ channelId: string }>) {
|
||||
}
|
||||
const onError = useApiError()
|
||||
|
||||
const debugMutation = useMutation({
|
||||
mutationFn: () => exportChannelDebug(channelId),
|
||||
onSuccess: (fileName) => toast.success(fileName),
|
||||
onError,
|
||||
})
|
||||
|
||||
const applyMutation = useMutation({
|
||||
mutationFn: () => applyChannelTemplate(channelId),
|
||||
onSuccess: (result) => {
|
||||
@@ -96,6 +108,17 @@ export function ChannelDetail({ channelId }: Readonly<{ channelId: string }>) {
|
||||
<h2 className="crt-glow text-xl font-semibold">{channel.name}</h2>
|
||||
{channel.number !== null && <Badge variant="muted">№ {channel.number}</Badge>}
|
||||
{!channel.isEnabled && <Badge variant="muted">{t('admin.channels.disabled')}</Badge>}
|
||||
{/* Дамп для разбора «почему сетка построилась так»: архив уезжает в браузер, а копия
|
||||
остаётся на сервере, если каталог настроен. */}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
title={t('admin.channels.debugExportHint')}
|
||||
disabled={debugMutation.isPending}
|
||||
onClick={() => debugMutation.mutate()}
|
||||
>
|
||||
<Bug className="h-4 w-4" /> {t('admin.channels.debugExport')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { apiRequest } from '@/shared/api/client'
|
||||
import { apiDownload, apiRequest } from '@/shared/api/client'
|
||||
import type {
|
||||
ApplyResultDto,
|
||||
ChannelDto,
|
||||
@@ -245,3 +245,25 @@ export function getSchedule(id: string, from: Date, to: Date) {
|
||||
const query = new URLSearchParams({ from: from.toISOString(), to: to.toISOString() })
|
||||
return apiRequest<ScheduleEntryDto[]>(`/admin/channels/${id}/schedule?${query.toString()}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Отладочный дамп канала: вход планировщика, состояние слотов, лента и сухой прогон одним архивом.
|
||||
* Тянем через fetch, а не ссылкой: эндпоинт закрыт Bearer'ом, и `<a href>` заголовок не отправит.
|
||||
*/
|
||||
export async function exportChannelDebug(channelId: string) {
|
||||
const { blob, fileName } = await apiDownload(
|
||||
`/admin/channels/${channelId}/debug-export`,
|
||||
'telewave-debug.zip',
|
||||
{ method: 'POST' },
|
||||
)
|
||||
const url = URL.createObjectURL(blob)
|
||||
try {
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = fileName
|
||||
link.click()
|
||||
} finally {
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user