import React, { useState, useMemo } from 'react'; import { Script } from "@/interfaces/exam"; import Message from './Message'; import AutoExpandingTextArea from '@/components/Low/AutoExpandingTextarea'; import { Card, CardContent } from '@/components/ui/card'; import Input from '@/components/Low/Input'; import { FaFemale, FaMale, FaPlus } from 'react-icons/fa'; import clsx from 'clsx'; export interface Speaker { id: number; name: string; gender: 'male' | 'female'; color: string; position: 'left' | 'right'; } type Gender = 'male' | 'female'; export interface ScriptLine { name: string; gender: Gender; text: string; voice?: string; } interface MessageWithPosition extends ScriptLine { position: 'left' | 'right'; } interface Props { section: number; editing?: boolean; local: Script; setLocal: (script: Script) => void; } const colorOptions = [ 'red', 'blue', 'green', 'purple', 'pink', 'indigo', 'teal', 'orange', 'cyan', 'emerald', 'sky', 'violet', 'fuchsia', 'rose', 'lime', 'slate' ]; const ScriptEditor: React.FC = ({ section, editing = false, local, setLocal }) => { const isConversation = [1, 3].includes(section); const [selectedSpeaker, setSelectedSpeaker] = useState(''); const [newMessage, setNewMessage] = useState(''); const speakerCount = section === 1 ? 2 : 4; const [speakers, setSpeakers] = useState(() => { const existingScript = local as ScriptLine[]; const existingSpeakers = new Set(); const speakerGenders = new Map(); if (Array.isArray(existingScript)) { existingScript.forEach(line => { existingSpeakers.add(line.name); speakerGenders.set(line.name, line.gender.toLowerCase() === 'female' ? 'female' : 'male' as 'male' | 'female'); }); } const speakerArray = Array.from(existingSpeakers); const totalNeeded = Math.max(speakerCount, speakerArray.length); return Array.from({ length: totalNeeded }, (_, index) => { if (index < speakerArray.length) { return { id: index, name: speakerArray[index], gender: speakerGenders.get(speakerArray[index]) || 'male', color: colorOptions[index], position: index % 2 === 0 ? 'left' : 'right' }; } return { id: index, name: '', gender: 'male', color: colorOptions[index], position: index % 2 === 0 ? 'left' : 'right' }; }); }); const speakerProperties = useMemo(() => { return speakers.reduce((acc, speaker) => { if (speaker.name) { acc[speaker.name] = { color: speaker.color, gender: speaker.gender }; } return acc; }, {} as Record); }, [speakers]); const allSpeakersConfigured = useMemo(() => { return speakers.every(speaker => speaker.name.trim() !== ''); }, [speakers]); const updateSpeaker = (index: number, updates: Partial) => { const updatedSpeakers = speakers.map((speaker, i) => { if (i === index) { return { ...speaker, ...updates }; } return speaker; }); setSpeakers(updatedSpeakers); if (Array.isArray(local)) { if ('name' in updates && speakers[index].name) { const oldName = speakers[index].name; const newName = updates.name || ''; const updatedScript = local.map(line => { if (line.name === oldName) { return { ...line, name: newName }; } return line; }); setLocal(updatedScript); } if ('gender' in updates && speakers[index].name && updates.gender) { const name = speakers[index].name; const newGender = updates.gender; const updatedScript = local.map(line => { if (line.name === name) { return { ...line, gender: newGender }; } return line; }); setLocal(updatedScript); } } if ('name' in updates && speakers[index].name === selectedSpeaker) { setSelectedSpeaker(updates.name || ''); } }; const addMessage = () => { if (!isConversation || !selectedSpeaker || !newMessage.trim()) return; if (!Array.isArray(local)) return; const speaker = speakers.find(s => s.name === selectedSpeaker); if (!speaker) return; const newLine: ScriptLine = { name: selectedSpeaker, gender: speaker.gender, text: newMessage.trim() }; const updatedScript = [...local, newLine]; setLocal(updatedScript); setNewMessage(''); }; const updateMessage = (index: number, newText: string) => { if (!Array.isArray(local)) return; const updatedScript = [...local]; updatedScript[index] = { ...updatedScript[index], text: newText }; setLocal(updatedScript); }; const deleteMessage = (index: number) => { if (!Array.isArray(local)) return; const updatedScript = local.filter((_, i) => i !== index); setLocal(updatedScript); }; const updateMonologue = (text: string) => { setLocal(text); }; const messages = useMemo(() => { if (typeof local === 'string' || !Array.isArray(local)) return []; return local.reduce((acc, line, index) => { const normalizedLine = { ...line, gender: line.gender.toLowerCase() === 'female' ? 'female' : 'male' } as ScriptLine; if (index === 0) { acc.push({ ...normalizedLine, position: 'left' }); } else { const prevMsg = acc[index - 1]; const position = line.name === prevMsg.name ? prevMsg.position : (prevMsg.position === 'left' ? 'right' : 'left'); acc.push({ ...normalizedLine, position }); } return acc; }, []); }, [local]); if (!isConversation) { return (
{editing ? ( ) : (
{(local as string) || "Edit, generate or import your own audio."}
)}
); } return (
{editing && (

Edit Conversation

{speakers.map((speaker, index) => (
updateSpeaker(index, { name: text })} placeholder="Speaker name" />
{speaker.gender === 'male' ? ( ) : ( )}
))}
)}
{messages.map((message, index) => { const properties = speakerProperties[message.name]; if (!properties) return null; return ( updateMessage(index, text)} onDelete={() => deleteMessage(index)} /> ); })}
); }; export default ScriptEditor;