/** * Admin Story Generator page * Allows admin to generate stories for levels using AI */ import { useEffect, useState, useCallback } from 'react'; import { useAuth } from '@/stores/authStore'; import { AdminLayout } from '@/components/features/admin/AdminLayout'; import { getLevelsWithStories, generateStory, getStorySegmentsByLevel, deleteStorySegment, generateSegmentAudio, } from '@/lib/api/admin'; import type { LevelWithStories, StorySegment, StoryGenerationRequest, StoryGenerationResponse } from '@/types/api/admin'; export function AdminStoryGeneratorPage() { const { token } = useAuth(); const [levels, setLevels] = useState([]); const [selectedLevel, setSelectedLevel] = useState(null); const [theme, setTheme] = useState(''); const [segmentCount, setSegmentCount] = useState(5); const [customPrompt, setCustomPrompt] = useState(''); const [segments, setSegments] = useState([]); const [loading, setLoading] = useState(true); const [generating, setGenerating] = useState(false); const [generationProgress, setGenerationProgress] = useState(''); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); const [confirmDelete, setConfirmDelete] = useState(null); const fetchLevels = useCallback(async () => { if (!token) return; try { setLoading(true); setError(null); const data = await getLevelsWithStories(token); setLevels(data); if (data.length > 0) { setSelectedLevel(data[0].id); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load levels'); } finally { setLoading(false); } }, [token]); useEffect(() => { fetchLevels(); }, [fetchLevels]); const fetchSegments = useCallback(async (levelId: number) => { if (!token) return; try { setLoading(true); setError(null); const data = await getStorySegmentsByLevel(token, levelId); setSegments(data); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load story segments'); } finally { setLoading(false); } }, [token]); useEffect(() => { if (selectedLevel) { fetchSegments(selectedLevel); } }, [selectedLevel, fetchSegments]); const handleGenerateStory = async () => { if (!token || !selectedLevel) return; if (!theme.trim()) { setError('Please enter a theme'); return; } if (segmentCount < 1 || segmentCount > 20) { setError('Segment count must be between 1 and 20'); return; } try { setGenerating(true); setError(null); setSuccess(null); setGenerationProgress('Starting story generation...'); const request: StoryGenerationRequest = { levelId: selectedLevel, theme, segmentCount, customPrompt: customPrompt.trim() || undefined, }; setGenerationProgress('Calling AI service to generate story...'); const response: StoryGenerationResponse = await generateStory(token, selectedLevel, request); setGenerationProgress(`Story generated! ${response.segments.length} segments created.`); setSuccess(`Successfully generated story with ${response.segments.length} segments for ${response.theme}`); // Refresh segments await fetchSegments(selectedLevel); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to generate story'); } finally { setGenerating(false); setGenerationProgress(''); } }; const handleGenerateAudio = async (segmentId: number) => { if (!token) return; try { setError(null); setSuccess(null); setSegments(segments.map(s => s.id === segmentId ? { ...s, audioUrl: 'Generating...' } : s )); const updatedSegment = await generateSegmentAudio(token, segmentId); setSegments(segments.map(s => s.id === segmentId ? updatedSegment : s)); setSuccess(`Audio generated for segment ${segmentId}`); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to generate audio'); } }; const handleDeleteSegment = async (segmentId: number) => { if (!token) return; try { setError(null); await deleteStorySegment(token, segmentId); setSegments(segments.filter(s => s.id !== segmentId)); setConfirmDelete(null); setSuccess(`Segment ${segmentId} deleted successfully`); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to delete segment'); } }; const formatDate = (dateString: string): string => { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }); }; if (loading && levels.length === 0) { return (

Loading levels...

); } return (

Story Generator

Generate AI-powered stories for German language learning

{error && (
{error}
)} {success && (
{success}
)} {/* Story Generation Form */}

Generate New Story

setTheme(e.target.value)} placeholder="e.g., A Week in Berlin, Summer Vacation, Starting a New Job" disabled={generating} className="form-input" />
setSegmentCount(Math.max(1, Math.min(20, Number(e.target.value))))} min="1" max="20" disabled={generating} className="form-input" />