diff --git a/k-tv-frontend/app/(main)/dashboard/components/channel-card.tsx b/k-tv-frontend/app/(main)/dashboard/components/channel-card.tsx index 6955034..3f9adb3 100644 --- a/k-tv-frontend/app/(main)/dashboard/components/channel-card.tsx +++ b/k-tv-frontend/app/(main)/dashboard/components/channel-card.tsx @@ -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 = diff --git a/k-tv-frontend/app/(main)/dashboard/components/edit-channel-sheet.tsx b/k-tv-frontend/app/(main)/dashboard/components/edit-channel-sheet.tsx index 9396f36..856b418 100644 --- a/k-tv-frontend/app/(main)/dashboard/components/edit-channel-sheet.tsx +++ b/k-tv-frontend/app/(main)/dashboard/components/edit-channel-sheet.tsx @@ -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 }; 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, 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, + }, recycle_policy: form.recyclePolicy, auto_schedule: form.autoSchedule, access_mode: form.accessMode !== "public" ? form.accessMode : "public", diff --git a/k-tv-frontend/app/(main)/dashboard/page.tsx b/k-tv-frontend/app/(main)/dashboard/page.tsx index 01c9684..4560e0b 100644 --- a/k-tv-frontend/app/(main)/dashboard/page.tsx +++ b/k-tv-frontend/app/(main)/dashboard/page.tsx @@ -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 }; recycle_policy: RecyclePolicy; auto_schedule: boolean; access_mode?: import("@/lib/types").AccessMode; diff --git a/k-tv-frontend/hooks/use-channel-form.ts b/k-tv-frontend/hooks/use-channel-form.ts index 558420b..eec99a8 100644 --- a/k-tv-frontend/hooks/use-channel-form.ts +++ b/k-tv-frontend/hooks/use-channel-form.ts @@ -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"); diff --git a/k-tv-frontend/hooks/use-import-channel.ts b/k-tv-frontend/hooks/use-import-channel.ts index 12a1ca8..343cf12 100644 --- a/k-tv-frontend/hooks/use-import-channel.ts +++ b/k-tv-frontend/hooks/use-import-channel.ts @@ -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, + }, recycle_policy: data.recycle_policy, }, token, diff --git a/k-tv-frontend/lib/channel-export.ts b/k-tv-frontend/lib/channel-export.ts index 36f8708..de3b058 100644 --- a/k-tv-frontend/lib/channel-export.ts +++ b/k-tv-frontend/lib/channel-export.ts @@ -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)], { diff --git a/k-tv-frontend/lib/schemas.ts b/k-tv-frontend/lib/schemas.ts index 43ff6c9..e07ef76 100644 --- a/k-tv-frontend/lib/schemas.ts +++ b/k-tv-frontend/lib/schemas.ts @@ -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[]> + ), recycle_policy: z.object({ cooldown_days: z.number().int().min(0).nullable().optional(), cooldown_generations: z.number().int().min(0).nullable().optional(), diff --git a/k-tv-frontend/lib/types.ts b/k-tv-frontend/lib/types.ts index 31fb9f3..24debdb 100644 --- a/k-tv-frontend/lib/types.ts +++ b/k-tv-frontend/lib/types.ts @@ -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 = { + monday: 'Mon', tuesday: 'Tue', wednesday: 'Wed', thursday: 'Thu', + friday: 'Fri', saturday: 'Sat', sunday: 'Sun', +} + export interface ScheduleConfig { - blocks: ProgrammingBlock[]; + day_blocks: Record +} + +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