Add reset template functionality to channel management
Implemented a new endpoint for resetting the channel template, which clears the grid and removes future scheduled entries while preserving past and current recordings. Updated the frontend to include a reset button in the channel management interface, along with corresponding localization strings in English and Russian. Enhanced integration tests to verify the reset functionality, ensuring that the expected number of slots, layers, and entries are accurately reported after the reset operation.
This commit is contained in:
@@ -16,6 +16,7 @@ import type {
|
||||
GridProfileKind,
|
||||
LayerApplicability,
|
||||
PlanningRules,
|
||||
ResetTemplateResultDto,
|
||||
RestoreTemplateResultDto,
|
||||
ScheduleDiffDto,
|
||||
ScheduleEntryDto,
|
||||
@@ -89,6 +90,16 @@ export function restoreChannelTemplate(channelId: string) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Полный сброс: снимает сетку и удаляет будущий эфир, собранный по ней. Прошлое и идущая сейчас
|
||||
* запись остаются — их нельзя вырезать из-под зрителя.
|
||||
*/
|
||||
export function resetChannelTemplate(channelId: string) {
|
||||
return apiRequest<ResetTemplateResultDto>(`/admin/channels/${channelId}/template/reset`, {
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
/** Проверки сетки по правилам — считаются по шаблону, без прогона генератора. */
|
||||
export function getTemplateIssues(channelId: string) {
|
||||
return apiRequest<TemplateIssueDto[]>(`/admin/channels/${channelId}/template/issues`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { FileJson, Plus, Wand2 } from 'lucide-react'
|
||||
import { Eraser, FileJson, Plus, Wand2 } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { HttpError } from '@/shared/api/client'
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
createSlot,
|
||||
deleteLayer,
|
||||
listChannels,
|
||||
resetChannelTemplate,
|
||||
toSlotBody,
|
||||
updateLayer,
|
||||
updateSlot,
|
||||
@@ -142,6 +143,28 @@ export function GridTab({
|
||||
onError,
|
||||
})
|
||||
|
||||
/**
|
||||
* Сброс сетки и собранного по ней эфира. Прошлое остаётся, поэтому «полностью» здесь — про
|
||||
* будущее: то, что уже показано зрителю, не переписывается.
|
||||
*/
|
||||
const resetMutation = useMutation({
|
||||
mutationFn: () => resetChannelTemplate(channelId),
|
||||
onSuccess: (result) => {
|
||||
toast.success(
|
||||
t('admin.channels.resetDone', {
|
||||
slots: result.slots,
|
||||
layers: result.layers,
|
||||
entries: result.entries,
|
||||
}),
|
||||
)
|
||||
// Пустая сетка отдаёт эфир аварийной группе целиком: без предупреждения админ решит, что
|
||||
// сброс не сработал, увидев в предпросмотре те же шоу через минуту после него.
|
||||
if (template?.fallbackGroupId) toast.message(t('admin.channels.resetHint'))
|
||||
onChanged()
|
||||
},
|
||||
onError,
|
||||
})
|
||||
|
||||
const moveSlotMutation = useMutation({
|
||||
mutationFn: ({
|
||||
slot,
|
||||
@@ -316,6 +339,20 @@ export function GridTab({
|
||||
<FileJson className="h-4 w-4" />
|
||||
{t('admin.channels.transfer.action')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-red-500 hover:text-red-400"
|
||||
disabled={resetMutation.isPending}
|
||||
onClick={() => {
|
||||
// Сетку и хвост эфира назад не вернёшь — спрашиваем, как и перед заменой сетки.
|
||||
if (!window.confirm(t('admin.channels.resetConfirm'))) return
|
||||
resetMutation.mutate()
|
||||
}}
|
||||
>
|
||||
<Eraser className="h-4 w-4" />
|
||||
{t('admin.channels.reset')}
|
||||
</Button>
|
||||
<span className="text-muted-foreground">{t('admin.channels.showForDate')}</span>
|
||||
<Input
|
||||
type="date"
|
||||
|
||||
@@ -914,6 +914,9 @@ export type GridPromptDto = {
|
||||
/** Итог отката сетки: сколько слотов осталось и сколько ссылок восстановить не удалось. */
|
||||
export type RestoreTemplateResultDto = { slots: number; droppedRefs: number }
|
||||
|
||||
/** Итог сброса: сколько снято слотов и слоёв и сколько будущих записей эфира удалено. */
|
||||
export type ResetTemplateResultDto = { slots: number; layers: number; entries: number }
|
||||
|
||||
/** Проверки сетки по правилам, до генерации (см. 5.1). */
|
||||
type TemplateIssueKind =
|
||||
| 'GroupEmpty'
|
||||
|
||||
@@ -666,6 +666,13 @@ export const en = {
|
||||
'This channel grid will be replaced with the chosen channel grid. Continue?',
|
||||
templateCopied: 'Copied: {{layers}} layers, {{slots}} slots',
|
||||
copyDroppedBumpers: 'Breaks left without a bumper block: {{count}} — set them up by hand',
|
||||
// Not «Reset»: that is already the revert-to-aired action (the restore key).
|
||||
reset: 'Clear grid',
|
||||
resetConfirm:
|
||||
'The whole grid — every layer and slot — will be removed, and the future air built from it deleted. The past and the currently playing entry stay. Continue?',
|
||||
resetDone: 'Reset: {{layers}} layers, {{slots}} slots, {{entries}} air entries',
|
||||
resetHint:
|
||||
'While the channel is on, the tape rebuilds itself from the empty grid — with the fallback group. To keep the air empty, clear it in the rules or turn the channel off.',
|
||||
fallbackGroup: 'Fallback group',
|
||||
fallbackGroupNone: 'None — channel filler only',
|
||||
fallbackGroupHint:
|
||||
|
||||
@@ -661,6 +661,13 @@ export const ru = {
|
||||
'Текущая сетка этого канала будет заменена сеткой выбранного канала. Продолжить?',
|
||||
templateCopied: 'Скопировано: слоёв {{layers}}, слотов {{slots}}',
|
||||
copyDroppedBumpers: 'Врезок без блока заставки: {{count}} — донастройте руками',
|
||||
// Не «Сбросить»: так уже называется откат правок к эфирному состоянию (ключ restore).
|
||||
reset: 'Очистить сетку',
|
||||
resetConfirm:
|
||||
'Сетка канала будет снята полностью — все слои и слоты, — а будущий эфир, собранный по ней, удалён. Прошлое и идущая сейчас запись останутся. Продолжить?',
|
||||
resetDone: 'Сброшено: слоёв {{layers}}, слотов {{slots}}, записей эфира {{entries}}',
|
||||
resetHint:
|
||||
'Пока канал включён, лента отстроится заново по пустой сетке — аварийной группой. Чтобы эфир остался пустым, снимите её в правилах или выключите канал.',
|
||||
fallbackGroup: 'Аварийная группа',
|
||||
fallbackGroupNone: 'Нет — только филлер канала',
|
||||
fallbackGroupHint:
|
||||
|
||||
Reference in New Issue
Block a user