"use client"; import { useState } from "react"; import { Trash2, Plus } from "lucide-react"; import { Sheet, SheetContent, SheetHeader, SheetTitle, } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { BlockTimeline, BLOCK_COLORS } from "./block-timeline"; import { AlgorithmicFilterEditor } from "./algorithmic-filter-editor"; import { RecyclePolicyEditor } from "./recycle-policy-editor"; import { WebhookEditor } from "./webhook-editor"; import { AccessSettingsEditor } from "./access-settings-editor"; import { LogoEditor } from "./logo-editor"; import { ConfigHistorySheet } from "./config-history-sheet"; import { useChannelForm } from "@/hooks/use-channel-form"; import { channelFormSchema, extractErrors } from "@/lib/schemas"; import type { FieldErrors } from "@/lib/schemas"; import type { AccessMode, ChannelResponse, LogoPosition, ProgrammingBlock, FillStrategy, MediaFilter, ProviderInfo, RecyclePolicy, Weekday, } from "@/lib/types"; import { WEEKDAYS, WEEKDAY_LABELS } from "@/lib/types"; import { cn } from "@/lib/utils"; // --------------------------------------------------------------------------- // Local shared primitives (only used inside this file) // --------------------------------------------------------------------------- function Field({ label, hint, error, children, }: { label: string; hint?: string; error?: string; children: React.ReactNode; }) { return (
{children} {error ? (

{error}

) : hint ? (

{hint}

) : null}
); } function TextInput({ value, onChange, placeholder, required, error, }: { value: string; onChange: (v: string) => void; placeholder?: string; required?: boolean; error?: boolean; }) { return ( onChange(e.target.value)} placeholder={placeholder} className={`w-full rounded-md border bg-zinc-800 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-600 focus:outline-none ${error ? "border-red-500 focus:border-red-400" : "border-zinc-700 focus:border-zinc-500"}`} /> ); } function NumberInput({ value, onChange, min, error, }: { value: number | ""; onChange: (v: number | "") => void; min?: number; error?: boolean; }) { return ( onChange(e.target.value === "" ? "" : Number(e.target.value)) } className={`w-full rounded-md border bg-zinc-800 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-600 focus:outline-none ${error ? "border-red-500 focus:border-red-400" : "border-zinc-700 focus:border-zinc-500"}`} /> ); } function NativeSelect({ value, onChange, children, }: { value: string; onChange: (v: string) => void; children: React.ReactNode; }) { return ( ); } function defaultFilter(): MediaFilter { return { content_type: null, genres: [], decade: null, tags: [], min_duration_secs: null, max_duration_secs: null, collections: [], series_names: [], search_term: null, }; } // --------------------------------------------------------------------------- // BlockEditor — inline because it's only used here and depends on local types // --------------------------------------------------------------------------- interface BlockEditorProps { block: ProgrammingBlock; index: number; errors: FieldErrors; providers: ProviderInfo[]; onChange: (block: ProgrammingBlock) => void; } function BlockEditor({ block, index, errors, providers, onChange }: BlockEditorProps) { const setField = (key: K, value: ProgrammingBlock[K]) => onChange({ ...block, [key]: value }); const content = block.content; const pfx = `blocks.${index}`; const setContentType = (type: "algorithmic" | "manual") => { const pid = content.provider_id ?? ""; onChange({ ...block, content: type === "algorithmic" ? { type: "algorithmic", filter: defaultFilter(), strategy: "random", provider_id: pid } : { type: "manual", items: [], provider_id: pid }, }); }; const setFilter = (patch: Partial) => { if (content.type !== "algorithmic") return; onChange({ ...block, content: { ...content, filter: { ...content.filter, ...patch } } }); }; const setStrategy = (strategy: FillStrategy) => { if (content.type !== "algorithmic") return; onChange({ ...block, content: { ...content, strategy } }); }; const setProviderId = (id: string) => onChange({ ...block, content: { ...content, provider_id: id } }); return (
setField("name", v)} placeholder="Evening Sitcoms" error={!!errors[`${pfx}.name`]} /> setContentType(v as "algorithmic" | "manual")} >
setField("start_time", e.target.value + ":00")} className="w-full rounded-md border border-zinc-700 bg-zinc-800 px-3 py-2 text-sm text-zinc-100 focus:border-zinc-500 focus:outline-none" /> setField("duration_mins", v === "" ? 60 : v)} min={1} error={!!errors[`${pfx}.duration_mins`]} />
{content.type === "algorithmic" && ( <> {content.strategy === "sequential" && (

Sequential options

)} )} {content.type === "manual" && (

Item IDs