feat: implement script review workflow in App component

- Add 'review' phase to application state
- Add scriptSegments state variable for storing AI-generated segments
- Add isGeneratingScript loading state for script generation
- Implement handleGenerateScript() to call API and transition to review phase
- Implement handleSkipReview() to bypass review and go directly to generation
- Implement handleSegmentChange() to update speaker assignments in segments
- Update handleGenerate() to use edited segments when available
- Wire up ScriptReviewSection component in review phase
- Update phase transitions: input → assignment → review → generated
- Add loading indicator for script generation

Validates Requirements: 15.1, 15.3, 15.6, 15.7, 15.8
Task: 22. Update App component for script review workflow
This commit is contained in:
2025-12-09 12:09:15 +00:00
parent bc768f4f45
commit 92094479c1
2 changed files with 76 additions and 7 deletions

View File

@@ -424,7 +424,7 @@
- Test skip review button works
- _Requirements: 15.3, 15.4, 15.5, 15.8_
- [ ] 22. Update App component for script review workflow
- [x] 22. Update App component for script review workflow
- Add 'review' phase to application state
- Add scriptSegments state variable
- Add isGeneratingScript loading state

View File

@@ -2,18 +2,21 @@ import { useState } from 'react'
import './App.css'
import TextInputSection from './components/TextInputSection'
import VoiceAssignmentSection from './components/VoiceAssignmentSection'
import ScriptReviewSection from './components/ScriptReviewSection'
import AudioPlayerSection from './components/AudioPlayerSection'
import type { Role } from './api/tts'
import { detectRoles, generateAudiobook } from './api/tts'
import type { Role, ScriptSegment } from './api/tts'
import { detectRoles, generateScript, generateAudiobook } from './api/tts'
function App() {
const [text, setText] = useState('')
const [phase, setPhase] = useState<'input' | 'assignment' | 'generated'>('input')
const [phase, setPhase] = useState<'input' | 'assignment' | 'review' | 'generated'>('input')
const [isLoading, setIsLoading] = useState(false)
const [roles, setRoles] = useState<Role[]>([])
const [voiceAssignments, setVoiceAssignments] = useState<Map<string, string>>(new Map())
const [scriptSegments, setScriptSegments] = useState<ScriptSegment[]>([])
const [language, setLanguage] = useState('a') // Default: American English
const [speed, setSpeed] = useState(1.0) // Default: 1.0x
const [isGeneratingScript, setIsGeneratingScript] = useState(false)
const [isGenerating, setIsGenerating] = useState(false)
const [error, setError] = useState<string | null>(null)
const [audioUrl, setAudioUrl] = useState<string | null>(null)
@@ -60,6 +63,45 @@ function App() {
setSpeed(newSpeed)
}
const handleGenerateScript = async () => {
setIsGeneratingScript(true)
setError(null)
try {
// Generate script preview with AI segmentation
const roleIds = roles.map(role => role.id)
const response = await generateScript({
text,
roles: roleIds
})
setScriptSegments(response.segments)
// Move to review phase
setPhase('review')
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to generate script')
console.error('Script generation error:', err)
} finally {
setIsGeneratingScript(false)
}
}
const handleSkipReview = async () => {
// Skip review and go directly to generation
await handleGenerate()
}
const handleSegmentChange = (segmentId: string, newSpeaker: string) => {
setScriptSegments(prev =>
prev.map(segment =>
segment.id === segmentId
? { ...segment, speaker: newSpeaker }
: segment
)
)
}
const handleGenerate = async () => {
setIsGenerating(true)
setError(null)
@@ -72,8 +114,11 @@ function App() {
}))
// Generate the audiobook
// If we have script segments (from review phase), use them
// Otherwise, fall back to text-based generation
const audioBlob = await generateAudiobook({
text,
segments: scriptSegments.length > 0 ? scriptSegments : undefined,
text: scriptSegments.length > 0 ? undefined : text,
lang: language,
roles: rolesArray,
speed
@@ -149,13 +194,31 @@ function App() {
onLanguageChange={handleLanguageChange}
speed={speed}
onSpeedChange={handleSpeedChange}
onGenerate={handleGenerate}
isGenerating={isGenerating}
onGenerate={handleGenerateScript}
isGenerating={isGeneratingScript}
allRolesAssigned={allRolesAssigned}
/>
</>
)}
{phase === 'review' && (
<>
{isLongText && (
<div className="max-w-4xl mx-auto mb-4 p-3 sm:p-4 bg-yellow-100 border border-yellow-400 text-yellow-800 rounded text-sm sm:text-base">
Generation may take several minutes for very long content.
</div>
)}
<ScriptReviewSection
segments={scriptSegments}
roles={roles}
onSegmentChange={handleSegmentChange}
onGenerate={handleGenerate}
onSkipReview={handleSkipReview}
isGenerating={isGenerating}
/>
</>
)}
{phase === 'generated' && audioUrl && (
<AudioPlayerSection
audioUrl={audioUrl}
@@ -168,6 +231,12 @@ function App() {
Generation may take several minutes for very long content. Please be patient...
</div>
)}
{isGeneratingScript && (
<div className="max-w-4xl mx-auto mt-4 p-3 sm:p-4 bg-blue-100 border border-blue-400 text-blue-800 rounded text-center text-sm sm:text-base">
Generating script preview... This may take a moment.
</div>
)}
</div>
</div>
)