Add grid auto-build functionality with profile management and preview capabilities
Implemented new API endpoints and frontend components for grid auto-building based on predefined profiles. Added functionality to list grid profiles, preview generated grids, and generate grids with specified parameters. Enhanced the TemplateEndpoints and related services to support these features, improving the user experience for channel scheduling. Localization updates were made to accommodate new features in both English and Russian.
This commit is contained in:
@@ -24,6 +24,13 @@ export const qk = {
|
||||
issues: (id: string) => ['admin', 'channels', id, 'issues'] as const,
|
||||
diff: (id: string) => ['admin', 'channels', id, 'diff'] as const,
|
||||
preview: (id: string, days: number) => ['admin', 'channels', id, 'preview', days] as const,
|
||||
/** План автосборки — свой на каждую комбинацию профиля и режима. */
|
||||
gridPlan: (id: string, profile: string, mode: string) =>
|
||||
['admin', 'channels', id, 'grid-plan', profile, mode] as const,
|
||||
},
|
||||
|
||||
gridProfiles: {
|
||||
all: ['admin', 'grid-profiles'] as const,
|
||||
},
|
||||
|
||||
entries: {
|
||||
|
||||
@@ -109,6 +109,8 @@ export type MediaStatsDto = {
|
||||
queued: number
|
||||
processing: number
|
||||
averageProcessingSeconds: number | null
|
||||
/** Оценка времени до конца очереди, сек; null — очередь пуста либо среднее ещё не набралось. */
|
||||
estimatedRemainingSeconds: number | null
|
||||
}
|
||||
|
||||
// ── Библиотека (жанры) ─────────────────────────────────────────────────────
|
||||
@@ -692,6 +694,50 @@ export type CopyTemplateResultDto = {
|
||||
droppedBumperRefs: number
|
||||
}
|
||||
|
||||
// ── Автосборка сетки ──────────────────────────────────────────────────────
|
||||
|
||||
/** Архетип сетки: с какого типа канала списан ритм суток. */
|
||||
export type GridProfileKind = 'Mixed' | 'Animation' | 'Sitcom' | 'Music' | 'Movies' | 'Kids'
|
||||
|
||||
/** Что делать с тем, что в сетке уже есть. */
|
||||
export type GridGenerationMode = 'FillGaps' | 'Rebuild'
|
||||
|
||||
export type GridProfileDto = {
|
||||
kind: GridProfileKind
|
||||
name: string
|
||||
/** На что похоже — «как у 2×2». */
|
||||
reference: string
|
||||
description: string
|
||||
}
|
||||
|
||||
/** Строка предпросмотра: будущий слот до того, как он создан. */
|
||||
export type GridPlanSlotDto = {
|
||||
layer: 'Main' | 'Weekend'
|
||||
weekday: number | null
|
||||
start: string
|
||||
durationMinutes: number
|
||||
title: string
|
||||
daypart: Daypart
|
||||
slotKind: SlotKind
|
||||
groupName: string | null
|
||||
/** Человеческое описание блока: «4 подряд», «до конца слота». */
|
||||
block: string
|
||||
}
|
||||
|
||||
export type GridPlanDto = {
|
||||
profile: GridProfileKind
|
||||
profileName: string
|
||||
slots: GridPlanSlotDto[]
|
||||
notes: string[]
|
||||
/** Больше нуля только при пересборке — столько слотов будет снесено. */
|
||||
slotsToRemove: number
|
||||
fallbackGroupName: string | null
|
||||
freeMinutes: number
|
||||
coveredMinutes: number
|
||||
}
|
||||
|
||||
export type GenerateGridResultDto = { created: number; removed: number }
|
||||
|
||||
/** Проверки сетки по правилам, до генерации (см. 5.1). */
|
||||
type TemplateIssueKind =
|
||||
| 'GroupEmpty'
|
||||
|
||||
@@ -318,6 +318,11 @@ export const en = {
|
||||
processingShort: 'Processing',
|
||||
average: 'Average processing time (recent)',
|
||||
averageShort: 'Avg time',
|
||||
eta: 'Estimated time until the queue drains: work left at the average rate, divided by the number of parallel transcodes',
|
||||
etaShort: 'Left',
|
||||
etaSoon: 'under a minute',
|
||||
hoursShort: 'h',
|
||||
minutesShort: 'min',
|
||||
},
|
||||
},
|
||||
gallery: {
|
||||
@@ -423,6 +428,27 @@ export const en = {
|
||||
newLayerName: 'New layer',
|
||||
addSlotHere: 'Add slot',
|
||||
newSlot: 'New slot',
|
||||
/** Grid auto-build by profile: plan preview and creation. */
|
||||
generate: {
|
||||
action: 'Build grid',
|
||||
title: 'Grid auto-build',
|
||||
hint: 'A profile sets the rhythm of the day: block length, what airs in prime time and what fills the night. Groups are picked from the existing ones by rating, content type and how many units they hold.',
|
||||
modes: {
|
||||
FillGaps: 'Fill gaps',
|
||||
Rebuild: 'Rebuild the week',
|
||||
},
|
||||
modeHints: {
|
||||
FillGaps: 'Existing slots are left alone — only uncovered time is filled.',
|
||||
Rebuild:
|
||||
'All template slots are removed; the weekend is built as a separate layer on top.',
|
||||
},
|
||||
summary: 'Slots to create: {{slots}} — that is {{percent}}% of the free week.',
|
||||
willRemove: 'Slots to remove: {{count}}.',
|
||||
fallback: 'The fallback group will be "{{name}}" — it covers pauses between slots.',
|
||||
nothing: 'Nothing to create: there is no free time in the grid.',
|
||||
create: 'Build',
|
||||
done: 'Created {{created}} slots, removed {{removed}}.',
|
||||
},
|
||||
editSlot: 'Slot',
|
||||
slotTitle: 'Block title',
|
||||
slotStart: 'Start',
|
||||
|
||||
@@ -319,6 +319,11 @@ export const ru = {
|
||||
processingShort: 'В обработке',
|
||||
average: 'Среднее время обработки (по недавним)',
|
||||
averageShort: 'Ср. время',
|
||||
eta: 'Примерное время до конца очереди: оставшаяся работа по среднему, делённая на число параллельных транскодов',
|
||||
etaShort: 'Осталось',
|
||||
etaSoon: 'меньше минуты',
|
||||
hoursShort: 'ч',
|
||||
minutesShort: 'мин',
|
||||
},
|
||||
},
|
||||
gallery: {
|
||||
@@ -424,6 +429,26 @@ export const ru = {
|
||||
newLayerName: 'Новый слой',
|
||||
addSlotHere: 'Добавить слот',
|
||||
newSlot: 'Новый слот',
|
||||
/** Автосборка сетки по профилю: предпросмотр плана и его создание. */
|
||||
generate: {
|
||||
action: 'Собрать сетку',
|
||||
title: 'Автосборка сетки',
|
||||
hint: 'Профиль задаёт ритм суток: длину блоков, что стоит в прайме и чем закрыта ночь. Группы под каждую полосу подбираются из существующих — по рейтингу, типу контента и запасу серий.',
|
||||
modes: {
|
||||
FillGaps: 'Заполнить дыры',
|
||||
Rebuild: 'Собрать неделю с нуля',
|
||||
},
|
||||
modeHints: {
|
||||
FillGaps: 'Существующие слоты не трогаются — застраивается только незакрытое время.',
|
||||
Rebuild: 'Все слоты шаблона сносятся; выходные строятся отдельным слоем поверх будних.',
|
||||
},
|
||||
summary: 'Будет создано слотов: {{slots}} — это {{percent}}% свободного времени недели.',
|
||||
willRemove: 'Будет снесено слотов: {{count}}.',
|
||||
fallback: 'Аварийной группой станет «{{name}}» — ею закрываются паузы между слотами.',
|
||||
nothing: 'Создавать нечего: свободного времени в сетке нет.',
|
||||
create: 'Собрать',
|
||||
done: 'Создано слотов: {{created}}, снесено: {{removed}}.',
|
||||
},
|
||||
editSlot: 'Слот',
|
||||
slotTitle: 'Название блока',
|
||||
slotStart: 'Начало',
|
||||
|
||||
Reference in New Issue
Block a user