feat(frontend): ScheduleConfig V2 types, weekday schema, export update

This commit is contained in:
2026-03-17 14:39:19 +01:00
parent 20e80ac28e
commit bd498b9bcb
8 changed files with 63 additions and 10 deletions

View File

@@ -71,7 +71,9 @@ export function ChannelCard({
onMoveDown,
}: ChannelCardProps) {
const [confirmOpen, setConfirmOpen] = useState(false);
const blockCount = channel.schedule_config.blocks.length;
const blockCount = Object.values(channel.schedule_config.day_blocks).reduce(
(sum, blocks) => sum + blocks.length, 0
);
const { status, label } = useScheduleStatus(channel.id);
const scheduleColor =

View File

@@ -27,7 +27,9 @@ import type {
MediaFilter,
ProviderInfo,
RecyclePolicy,
Weekday,
} from "@/lib/types";
import { WEEKDAYS } from "@/lib/types";
// ---------------------------------------------------------------------------
// Local shared primitives (only used inside this file)
@@ -334,7 +336,7 @@ interface EditChannelSheetProps {
name: string;
description: string;
timezone: string;
schedule_config: { blocks: ProgrammingBlock[] };
schedule_config: { day_blocks: Record<Weekday, ProgrammingBlock[]> };
recycle_policy: RecyclePolicy;
auto_schedule: boolean;
access_mode?: AccessMode;
@@ -373,7 +375,9 @@ export function EditChannelSheet({
name: form.name,
description: form.description,
timezone: form.timezone,
blocks: form.blocks,
day_blocks: Object.fromEntries(
WEEKDAYS.map(d => [d, d === 'monday' ? form.blocks : []])
) as Record<Weekday, ProgrammingBlock[]>,
recycle_policy: form.recyclePolicy,
auto_schedule: form.autoSchedule,
access_mode: form.accessMode,
@@ -390,7 +394,11 @@ export function EditChannelSheet({
name: form.name,
description: form.description,
timezone: form.timezone,
schedule_config: { blocks: form.blocks },
schedule_config: {
day_blocks: Object.fromEntries(
WEEKDAYS.map(d => [d, d === 'monday' ? form.blocks : []])
) as Record<Weekday, ProgrammingBlock[]>,
},
recycle_policy: form.recyclePolicy,
auto_schedule: form.autoSchedule,
access_mode: form.accessMode !== "public" ? form.accessMode : "public",

View File

@@ -32,6 +32,7 @@ import type {
ChannelResponse,
ProgrammingBlock,
RecyclePolicy,
Weekday,
} from "@/lib/types";
export default function DashboardPage() {
@@ -84,7 +85,7 @@ export default function DashboardPage() {
name: string;
description: string;
timezone: string;
schedule_config: { blocks: ProgrammingBlock[] };
schedule_config: { day_blocks: Record<Weekday, ProgrammingBlock[]> };
recycle_policy: RecyclePolicy;
auto_schedule: boolean;
access_mode?: import("@/lib/types").AccessMode;

View File

@@ -84,7 +84,7 @@ export function useChannelForm(channel: ChannelResponse | null) {
setName(channel.name);
setDescription(channel.description ?? "");
setTimezone(channel.timezone);
setBlocks(channel.schedule_config.blocks);
setBlocks(channel.schedule_config.day_blocks['monday'] ?? []);
setRecyclePolicy(channel.recycle_policy);
setAutoSchedule(channel.auto_schedule);
setAccessMode(channel.access_mode ?? "public");

View File

@@ -4,6 +4,8 @@ import { useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { api } from "@/lib/api";
import type { ChannelImportData } from "@/app/(main)/dashboard/components/import-channel-dialog";
import { WEEKDAYS } from "@/lib/types";
import type { Weekday } from "@/lib/types";
export function useImportChannel(token: string | null) {
const queryClient = useQueryClient();
@@ -26,7 +28,11 @@ export function useImportChannel(token: string | null) {
await api.channels.update(
created.id,
{
schedule_config: { blocks: data.blocks },
schedule_config: {
day_blocks: Object.fromEntries(
WEEKDAYS.map(d => [d, d === 'monday' ? data.blocks : []])
) as Record<Weekday, typeof data.blocks>,
},
recycle_policy: data.recycle_policy,
},
token,

View File

@@ -5,7 +5,7 @@ export function exportChannel(channel: ChannelResponse): void {
name: channel.name,
description: channel.description ?? undefined,
timezone: channel.timezone,
blocks: channel.schedule_config.blocks,
day_blocks: channel.schedule_config.day_blocks,
recycle_policy: channel.recycle_policy,
};
const blob = new Blob([JSON.stringify(payload, null, 2)], {

View File

@@ -1,4 +1,10 @@
import { z } from "zod";
import { WEEKDAYS } from "@/lib/types";
import type { Weekday } from "@/lib/types";
const weekdaySchema = z.enum([
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
]);
export const mediaFilterSchema = z.object({
content_type: z.enum(["movie", "episode", "short"]).nullable().optional(),
@@ -53,7 +59,10 @@ export const channelFormSchema = z.object({
name: z.string().min(1, "Name is required"),
timezone: z.string().min(1, "Timezone is required"),
description: z.string().optional(),
blocks: z.array(blockSchema),
day_blocks: z.record(weekdaySchema, z.array(blockSchema))
.default(() =>
Object.fromEntries(WEEKDAYS.map(d => [d, []])) as unknown as Record<Weekday, z.infer<typeof blockSchema>[]>
),
recycle_policy: z.object({
cooldown_days: z.number().int().min(0).nullable().optional(),
cooldown_generations: z.number().int().min(0).nullable().optional(),

View File

@@ -91,8 +91,35 @@ export interface ProgrammingBlock {
access_password?: string;
}
export type Weekday =
| 'monday' | 'tuesday' | 'wednesday' | 'thursday'
| 'friday' | 'saturday' | 'sunday'
export const WEEKDAYS: Weekday[] = [
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
]
export const WEEKDAY_LABELS: Record<Weekday, string> = {
monday: 'Mon', tuesday: 'Tue', wednesday: 'Wed', thursday: 'Thu',
friday: 'Fri', saturday: 'Sat', sunday: 'Sun',
}
export interface ScheduleConfig {
blocks: ProgrammingBlock[];
day_blocks: Record<Weekday, ProgrammingBlock[]>
}
export interface ConfigSnapshot {
id: string
version_num: number
label: string | null
created_at: string
}
export interface ScheduleHistoryEntry {
id: string
generation: number
valid_from: string
valid_until: string
}
// Config