"use client"; import { useState, useEffect } from "react"; import { Trash2, Plus, ChevronDown, ChevronUp } from "lucide-react"; import { Sheet, SheetContent, SheetHeader, SheetTitle, } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import type { ChannelResponse, ProgrammingBlock, BlockContent, FillStrategy, ContentType, MediaFilter, RecyclePolicy, } from "@/lib/types"; // --------------------------------------------------------------------------- // Sub-components (all dumb, no hooks) // --------------------------------------------------------------------------- interface FieldProps { label: string; hint?: string; children: React.ReactNode; } function Field({ label, hint, children }: FieldProps) { return (
{children} {hint &&

{hint}

}
); } function TextInput({ value, onChange, placeholder, required, }: { value: string; onChange: (v: string) => void; placeholder?: string; required?: boolean; }) { return ( onChange(e.target.value)} placeholder={placeholder} className="w-full rounded-md border border-zinc-700 bg-zinc-800 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-600 focus:border-zinc-500 focus:outline-none" /> ); } function NumberInput({ value, onChange, min, max, step, placeholder, }: { value: number | ""; onChange: (v: number | "") => void; min?: number; max?: number; step?: number | "any"; placeholder?: string; }) { return ( onChange(e.target.value === "" ? "" : Number(e.target.value)) } className="w-full rounded-md border border-zinc-700 bg-zinc-800 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-600 focus:border-zinc-500 focus:outline-none" /> ); } function NativeSelect({ value, onChange, children, }: { value: string; onChange: (v: string) => void; children: React.ReactNode; }) { return ( ); } // --------------------------------------------------------------------------- // Block editor // --------------------------------------------------------------------------- function defaultFilter(): MediaFilter { return { content_type: null, genres: [], decade: null, tags: [], min_duration_secs: null, max_duration_secs: null, collections: [], }; } function defaultBlock(): ProgrammingBlock { return { id: crypto.randomUUID(), name: "", start_time: "20:00:00", duration_mins: 60, content: { type: "algorithmic", filter: defaultFilter(), strategy: "random", }, }; } interface BlockEditorProps { block: ProgrammingBlock; onChange: (block: ProgrammingBlock) => void; onRemove: () => void; } function BlockEditor({ block, onChange, onRemove }: BlockEditorProps) { const [expanded, setExpanded] = useState(true); const setField = ( key: K, value: ProgrammingBlock[K], ) => onChange({ ...block, [key]: value }); const content = block.content; const setContentType = (type: "algorithmic" | "manual") => { if (type === "algorithmic") { onChange({ ...block, content: { type: "algorithmic", filter: defaultFilter(), strategy: "random" }, }); } else { onChange({ ...block, content: { type: "manual", items: [] } }); } }; 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 } }); }; return (
{/* Block header */}
{expanded && (
setField("name", v)} placeholder="Evening Sitcoms" /> 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} />
{content.type === "algorithmic" && (

Filter

setFilter({ content_type: v === "" ? null : (v as ContentType), }) } > setStrategy(v as FillStrategy)} >
setFilter({ genres: v .split(",") .map((s) => s.trim()) .filter(Boolean), }) } placeholder="Comedy, Sci-Fi" />
setFilter({ decade: v === "" ? null : (v as number) }) } placeholder="1990" /> setFilter({ min_duration_secs: v === "" ? null : (v as number), }) } placeholder="1200" /> setFilter({ max_duration_secs: v === "" ? null : (v as number), }) } placeholder="3600" />
setFilter({ collections: v .split(",") .map((s) => s.trim()) .filter(Boolean), }) } placeholder="abc123" />
)} {content.type === "manual" && (

Item IDs