'use client' import { useCallback, useEffect, useMemo } from 'react' import { Controller, useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { EpisodeProfile, SpeakerProfile } from '@/lib/types/podcasts' import { useCreateEpisodeProfile, useUpdateEpisodeProfile, } from '@/lib/hooks/use-podcasts' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Textarea } from '@/components/ui/textarea' import { Separator } from '@/components/ui/separator' const episodeProfileSchema = z.object({ name: z.string().min(1, 'Name is required'), description: z.string().optional(), speaker_config: z.string().min(1, 'Speaker profile is required'), outline_provider: z.string().min(1, 'Outline provider is required'), outline_model: z.string().min(1, 'Outline model is required'), transcript_provider: z.string().min(1, 'Transcript provider is required'), transcript_model: z.string().min(1, 'Transcript model is required'), default_briefing: z.string().min(1, 'Default briefing is required'), num_segments: z.number() .int('Must be an integer') .min(3, 'At least 3 segments') .max(20, 'Maximum 20 segments'), }) export type EpisodeProfileFormValues = z.infer interface EpisodeProfileFormDialogProps { mode: 'create' | 'edit' open: boolean onOpenChange: (open: boolean) => void speakerProfiles: SpeakerProfile[] modelOptions: Record initialData?: EpisodeProfile } export function EpisodeProfileFormDialog({ mode, open, onOpenChange, speakerProfiles, modelOptions, initialData, }: EpisodeProfileFormDialogProps) { const createProfile = useCreateEpisodeProfile() const updateProfile = useUpdateEpisodeProfile() const providers = useMemo(() => Object.keys(modelOptions), [modelOptions]) const getDefaults = useCallback((): EpisodeProfileFormValues => { const firstSpeaker = speakerProfiles[0]?.name ?? '' const firstProvider = providers[0] ?? '' const firstModel = firstProvider ? modelOptions[firstProvider]?.[0] ?? '' : '' if (initialData) { return { name: initialData.name, description: initialData.description ?? '', speaker_config: initialData.speaker_config, outline_provider: initialData.outline_provider, outline_model: initialData.outline_model, transcript_provider: initialData.transcript_provider, transcript_model: initialData.transcript_model, default_briefing: initialData.default_briefing, num_segments: initialData.num_segments, } } return { name: '', description: '', speaker_config: firstSpeaker, outline_provider: firstProvider, outline_model: firstModel, transcript_provider: firstProvider, transcript_model: firstModel, default_briefing: '', num_segments: 5, } }, [initialData, modelOptions, providers, speakerProfiles]) const { control, register, handleSubmit, reset, setValue, watch, formState: { errors }, } = useForm({ resolver: zodResolver(episodeProfileSchema), defaultValues: getDefaults(), }) const outlineProvider = watch('outline_provider') const outlineModel = watch('outline_model') const transcriptProvider = watch('transcript_provider') const transcriptModel = watch('transcript_model') const availableOutlineModels = modelOptions[outlineProvider] ?? [] const availableTranscriptModels = modelOptions[transcriptProvider] ?? [] useEffect(() => { if (!open) { return } reset(getDefaults()) }, [open, reset, getDefaults]) useEffect(() => { if (!outlineProvider) { return } const models = modelOptions[outlineProvider] ?? [] if (models.length === 0) { setValue('outline_model', '') return } if (!models.includes(outlineModel)) { setValue('outline_model', models[0]) } }, [outlineProvider, outlineModel, modelOptions, setValue]) useEffect(() => { if (!transcriptProvider) { return } const models = modelOptions[transcriptProvider] ?? [] if (models.length === 0) { setValue('transcript_model', '') return } if (!models.includes(transcriptModel)) { setValue('transcript_model', models[0]) } }, [transcriptProvider, transcriptModel, modelOptions, setValue]) const onSubmit = async (values: EpisodeProfileFormValues) => { const payload = { ...values, description: values.description ?? '', } if (mode === 'create') { await createProfile.mutateAsync(payload) } else if (initialData) { await updateProfile.mutateAsync({ profileId: initialData.id, payload, }) } onOpenChange(false) } const isSubmitting = createProfile.isPending || updateProfile.isPending const disableSubmit = isSubmitting || speakerProfiles.length === 0 || providers.length === 0 const isEdit = mode === 'edit' return ( {isEdit ? 'Edit Episode Profile' : 'Create Episode Profile'} Define how episodes should be generated and which speaker configuration they use by default. {speakerProfiles.length === 0 ? ( No speaker profiles available Create a speaker profile before configuring an episode profile. ) : null} {providers.length === 0 ? ( No language models available Add language models in the Models section to configure outline and transcript generation. ) : null}
{errors.name ? (

{errors.name.message}

) : null}
{errors.num_segments ? (

{errors.num_segments.message}

) : null}