import React, { useState, useEffect } from 'react'; import { Paper, Typography, Box, Button, Alert, Grid, CircularProgress } from '@mui/material'; import { useStoryWriterState } from '../../../../hooks/useStoryWriterState'; import { storyWriterApi, StoryScene } from '../../../../services/storyWriterApi'; import { triggerSubscriptionError } from '../../../../api/client'; import { StoryParametersSection } from './StoryParametersSection'; import { StoryConfigurationSection } from './StoryConfigurationSection'; import { FeatureCheckboxesSection } from './FeatureCheckboxesSection'; import { GenerationSettingsSection } from './GenerationSettingsSection'; import { AIStorySetupModal } from './AIStorySetupModal'; import { textFieldStyles, paperStyles } from './styles'; import { AUDIENCE_AGE_GROUPS } from './constants'; import { StorySetupProps, CustomValuesState, CustomValuesSetters } from './types'; const StorySetup: React.FC = ({ state, onNext }) => { const [isRegeneratingPremise, setIsRegeneratingPremise] = useState(false); const [isGeneratingOutline, setIsGeneratingOutline] = useState(false); const [error, setError] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); // Track custom values from AI-generated options const [customWritingStyles, setCustomWritingStyles] = useState([]); const [customStoryTones, setCustomStoryTones] = useState([]); const [customNarrativePOVs, setCustomNarrativePOVs] = useState([]); const [customAudienceAgeGroups, setCustomAudienceAgeGroups] = useState([]); const [customContentRatings, setCustomContentRatings] = useState([]); const [customEndingPreferences, setCustomEndingPreferences] = useState([]); const customValues: CustomValuesState = { customWritingStyles, customStoryTones, customNarrativePOVs, customAudienceAgeGroups, customContentRatings, customEndingPreferences, }; const handleGenerateOutlineAndProceed = async () => { if (!state.premise) { setError('Please generate a premise before generating the outline'); return; } setIsGeneratingOutline(true); setError(null); try { const request = state.getRequest(); const response = await storyWriterApi.generateOutline(state.premise, request); if (response.success && response.outline) { if (response.is_structured && Array.isArray(response.outline)) { const scenes = response.outline as StoryScene[]; state.setOutlineScenes(scenes); state.setIsOutlineStructured(true); const formattedOutline = scenes .map((scene, idx) => `Scene ${scene.scene_number || idx + 1}: ${scene.title}\n${scene.description}`) .join('\n\n'); state.setOutline(formattedOutline); } else { state.setOutline(typeof response.outline === 'string' ? response.outline : String(response.outline)); state.setOutlineScenes(null); state.setIsOutlineStructured(false); } state.setError(null); onNext(); } else { throw new Error(typeof response.outline === 'string' ? response.outline : 'Failed to generate outline'); } } catch (err: any) { const status = err?.response?.status; if (status === 429 || status === 402) { const handled = await triggerSubscriptionError(err); if (handled) { setIsGeneratingOutline(false); return; } } const errorMessage = err.response?.data?.detail || err.message || 'Failed to generate outline'; setError(errorMessage); state.setError(errorMessage); } finally { setIsGeneratingOutline(false); } }; const customValuesSetters: CustomValuesSetters = { setCustomWritingStyles, setCustomStoryTones, setCustomNarrativePOVs, setCustomAudienceAgeGroups, setCustomContentRatings, setCustomEndingPreferences, }; // Get normalized audienceAgeGroup value (fallback to default if invalid, but preserve custom values) const allAudienceAgeGroups = [...AUDIENCE_AGE_GROUPS, ...customAudienceAgeGroups]; const normalizedAudienceAgeGroup = allAudienceAgeGroups.includes(state.audienceAgeGroup) ? state.audienceAgeGroup : state.audienceAgeGroup === 'Adults' ? 'Adults (18+)' : state.audienceAgeGroup === 'Children' ? 'Children (5-12)' : state.audienceAgeGroup === 'Young Adults' ? 'Young Adults (13-17)' : state.audienceAgeGroup || 'Adults (18+)'; // Preserve custom values instead of defaulting // Fix invalid audienceAgeGroup values on mount and when state changes (but preserve custom values) useEffect(() => { // Only normalize if it's an old format value, not a custom value if ( state.audienceAgeGroup && state.audienceAgeGroup !== normalizedAudienceAgeGroup && !allAudienceAgeGroups.includes(state.audienceAgeGroup) && (state.audienceAgeGroup === 'Adults' || state.audienceAgeGroup === 'Children' || state.audienceAgeGroup === 'Young Adults') ) { state.setAudienceAgeGroup(normalizedAudienceAgeGroup); } }, [state.audienceAgeGroup, normalizedAudienceAgeGroup, state.setAudienceAgeGroup, allAudienceAgeGroups]); const handleRegeneratePremise = async () => { // Validate required fields if (!state.persona || !state.storySetting || !state.characters || !state.plotElements) { setError('Please fill in all required fields (Persona, Setting, Characters, Plot Elements)'); return; } setIsRegeneratingPremise(true); setError(null); try { const request = state.getRequest(); const response = await storyWriterApi.generatePremise(request); if (response.success && response.premise) { state.setPremise(response.premise); state.setError(null); } else { throw new Error(response.premise || 'Failed to generate premise'); } } catch (err: any) { // Check if this is a subscription error (429/402) and trigger global subscription modal const status = err?.response?.status; if (status === 429 || status === 402) { console.log('StorySetup: Detected subscription error in regenerate premise, triggering global handler', { status, data: err?.response?.data, }); const handled = await triggerSubscriptionError(err); if (handled) { console.log('StorySetup: Global subscription error handler triggered successfully'); setIsRegeneratingPremise(false); return; } else { console.warn('StorySetup: Global subscription error handler did not handle the error'); } } // For non-subscription errors, show local error message const errorMessage = err.response?.data?.detail || err.message || 'Failed to generate premise'; setError(errorMessage); state.setError(errorMessage); } finally { setIsRegeneratingPremise(false); } }; return ( Story Setup Configure your story parameters and premise. Fill in the required fields and click "Next: Generate Outline" to continue. {error && ( setError(null)}> {error} )} {/* AI Story Setup Button */} {/* Story Parameters Section */} {/* Story Configuration Section */} {/* Feature Checkboxes Section */} {/* Generation Settings Section */} {/* Generate Button */} {/* AI Story Setup Modal */} setIsModalOpen(false)} state={state} customValuesSetters={customValuesSetters} /> ); }; export default StorySetup;