"use client"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { ArrowLeft, ArrowRight, Loader2, Sparkles, User } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; import { TONES, FORMATS, LANGUAGES, LENGTH_OPTIONS, FORMAT_SPEAKERS } from "@/lib/episodes/options"; import { VOICE_CATALOG, DEFAULT_VOICE_IDS } from "@/lib/ai/voices"; import { createEpisodeAction, type CreateEpisodeInput } from "@/app/(app)/episodes/actions"; type Format = "SOLO" | "INTERVIEW" | "MULTI_HOST"; interface SpeakerState { speakerKey: string; displayName: string; elevenVoiceId: string; } function defaultSpeakers(format: Format): SpeakerState[] { return FORMAT_SPEAKERS[format].map((s, i) => ({ speakerKey: s.speakerKey, displayName: s.defaultName, elevenVoiceId: DEFAULT_VOICE_IDS[s.speakerKey] ?? VOICE_CATALOG[i % VOICE_CATALOG.length].id, })); } export function EpisodeWizard({ maxMinutes }: { maxMinutes: number }) { const router = useRouter(); const [step, setStep] = useState(1); const [submitting, setSubmitting] = useState(false); const [title, setTitle] = useState(""); const [topic, setTopic] = useState(""); const [tone, setTone] = useState(TONES[0]); const [format, setFormat] = useState("SOLO"); const [language, setLanguage] = useState("en"); const [length, setLength] = useState(5); const [audience, setAudience] = useState(""); const [speakers, setSpeakers] = useState(defaultSpeakers("SOLO")); const lengths = useMemo(() => LENGTH_OPTIONS.filter((l) => l <= maxMinutes), [maxMinutes]); function changeFormat(f: Format) { setFormat(f); setSpeakers(defaultSpeakers(f)); } function updateSpeaker(idx: number, patch: Partial) { setSpeakers((prev) => prev.map((s, i) => (i === idx ? { ...s, ...patch } : s))); } function next() { if (step === 1 && topic.trim().length < 10) { toast.error("Please describe your topic in a bit more detail."); return; } setStep((s) => Math.min(3, s + 1)); } async function submit() { setSubmitting(true); const input: CreateEpisodeInput = { title: title.trim() || undefined, topic: topic.trim(), tone, format, language, targetLengthMin: length, audience: audience.trim() || undefined, speakers, }; const res = await createEpisodeAction(input); if (!res.ok) { toast.error(res.error); setSubmitting(false); return; } toast.success("Generating your episode…"); router.push(`/episodes/${res.episodeId}`); } return (
{step === 1 && ( <>