import React, { useState, useCallback, useEffect, useMemo } from "react"; import { shouldSkipOnboarding } from '../../utils/demoMode'; import { Box, Paper, Stack, Alert, Divider, CircularProgress, alpha, Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from "@mui/material"; import { usePodcastProjectState } from "../../hooks/usePodcastProjectState"; import { PodcastCostEst } from "./types"; import { CreateModal } from "./CreateModal"; import { AnalysisPanel } from "./AnalysisPanel"; import { ScriptEditor } from "./ScriptEditor"; import { RenderQueue } from "./RenderQueue"; import { RecentEpisodesPreview } from "./RecentEpisodesPreview"; import { ProjectList } from "./ProjectList"; import { PreflightBlockDialog } from "./PreflightBlockDialog"; import { Header, EstimateCard, QuerySelection, ResearchSummary, RegenerationFeedbackModal, usePodcastWorkflow, DEFAULT_KNOBS, getStepLabel, } from "./PodcastDashboard/index"; const PodcastDashboard: React.FC = () => { useEffect(() => { try { const skip = shouldSkipOnboarding(); // Skip onboarding in podcast-only mode } catch (e) { console.warn('PodcastDashboard entry: gating log error', e); } }, []); const projectState = usePodcastProjectState(); const [showProjectList, setShowProjectList] = useState(false); const { project, analysis, queries, selectedQueries, research, rawResearch, estimate, scriptData, renderJobs, knobs: knobsState, researchProvider, showScriptEditor, showRenderQueue, currentStep, bible, setScriptData, setBible, setShowScriptEditor, setShowRenderQueue, setResearchProvider, updateRenderJob, resetState, loadProjectFromDb, setCurrentStep, } = projectState; const workflow = usePodcastWorkflow({ projectState, onError: (msg: string) => { // Error handling is done through workflow's own announcement system console.error("Workflow error:", msg); }, }); const [showRegenModal, setShowRegenModal] = useState(false); const headerCostEst = useMemo(() => { const defaultBreakdown: PodcastCostEst["breakdown"] = [ { phase: "Analyze", cost: 0 }, { phase: "Gather", cost: 0 }, { phase: "Write", cost: 0 }, { phase: "Produce", cost: 0 }, ]; if (!estimate && !research?.costEst) { return null; } const breakdownMap = new Map(defaultBreakdown.map((item) => [item.phase, item.cost])); if (research?.costEst?.breakdown?.length) { research.costEst.breakdown.forEach((item) => { breakdownMap.set(item.phase, Number(item.cost) || 0); }); } if (estimate) { const gatherCost = breakdownMap.get("Gather") || 0; const produceCost = breakdownMap.get("Produce") || 0; if (gatherCost === 0 && estimate.researchCost > 0) { breakdownMap.set("Gather", estimate.researchCost); } if (produceCost === 0) { breakdownMap.set("Produce", estimate.ttsCost + estimate.avatarCost + estimate.videoCost); } } const breakdown: PodcastCostEst["breakdown"] = defaultBreakdown.map((item) => ({ phase: item.phase, cost: breakdownMap.get(item.phase) || 0, })); const total = breakdown.reduce((sum, item) => sum + item.cost, 0); return { total, breakdown, currency: "USD", last_updated: research?.costEst?.last_updated || new Date().toISOString(), }; }, [estimate, research?.costEst]); const handleSelectProject = useCallback(async (projectId: string) => { try { await loadProjectFromDb(projectId); setShowProjectList(false); } catch (error) { const errorMsg = `Failed to load project: ${error instanceof Error ? error.message : "Unknown error"}`; // Use workflow's setAnnouncement - workflow is stable from hook workflow.setAnnouncement(errorMsg); } }, [loadProjectFromDb, workflow]); const handleNewEpisode = useCallback(() => { resetState(); setShowProjectList(false); }, [resetState]); if (showProjectList) { return setShowProjectList(false)} />; } return ( {/* Header */}
setShowProjectList(true)} onNewEpisode={handleNewEpisode} activeStep={workflow.activeStep} completedSteps={[ ...(analysis ? [0] : []), ...(research ? [1] : []), ...(scriptData ? [2] : []), ...(renderJobs.some(j => j.status === "completed") ? [3] : []), ]} costEst={headerCostEst} onStepClick={(step) => { // Handle step clicks - could navigate to different views }} /> {/* Progress stepper is in Header - keeping UI clean */} {/* Resume Alert */} {workflow.showResumeAlert && project && ( workflow.setShowResumeAlert(false)} sx={{ background: "#d1fae5", border: "1px solid #a7f3d0", "& .MuiAlert-icon": { color: "#10b981" }, }} > Project Restored: Resuming from {getStepLabel(currentStep)} step. Your progress has been saved. )} {/* Announcements */} {workflow.announcement && ( workflow.setAnnouncement("")} sx={{ background: workflow.announcementSeverity === "error" ? "#fef2f2" : workflow.announcementSeverity === "success" ? "#f0fdf4" : "#dbeafe", border: `1px solid ${workflow.announcementSeverity === "error" ? "#fecaca" : workflow.announcementSeverity === "success" ? "#bbf7d0" : "#bfdbfe"}`, "& .MuiAlert-icon": { color: workflow.announcementSeverity === "error" ? "#ef4444" : workflow.announcementSeverity === "success" ? "#22c55e" : "#3b82f6" }, }} > {workflow.announcement} )} {/* Podcast Bible - now in AnalysisPanel header */} {(workflow.isAnalyzing || workflow.isResearching || workflow.isGeneratingScript) && ( {workflow.isAnalyzing ? "Analyzing your idea with AI..." : workflow.isGeneratingScript ? "Generating script with AI..." : "Running research... This may take a moment."} )} {/* Create Modal */} {!project && ( <> {}} /> )} {/* Main Content */} {analysis && (currentStep === 'analysis' || (currentStep === 'research' && !research)) && !showScriptEditor && !showRenderQueue && ( setShowRegenModal(true)} onUpdateAnalysis={(updated) => projectState.setAnalysis(updated)} onUpdateBible={(updated) => setBible(updated)} /> )} {/* Main content area */} {queries.length > 0 && currentStep === 'research' && !research && !showScriptEditor && !showRenderQueue && ( )} {research && (currentStep === 'research' || currentStep === 'script') && !showScriptEditor && !showRenderQueue && ( )} {showScriptEditor && project && research && rawResearch && ( setScriptData(s)} onBackToResearch={() => setShowScriptEditor(false)} onProceedToRendering={(s) => workflow.handleProceedToRendering(s)} onError={(msg) => workflow.setAnnouncement(msg)} avatarUrl={project?.avatarUrl} /> )} {showScriptEditor && (!research || !rawResearch) && ( Complete a research run before opening the script editor. )} {showRenderQueue && project && scriptData && ( setScriptData(updatedScript)} onBack={() => { setShowRenderQueue(false); setShowScriptEditor(true); }} onError={(msg) => workflow.setAnnouncement(msg)} /> )} {/* Preflight Block Dialog */} { workflow.setShowPreflightDialog(false); workflow.setPreflightResponse(null); }} response={workflow.preflightResponse} operationName={workflow.preflightOperationName} /> {/* Regeneration Feedback Modal */} setShowRegenModal(false)} onConfirm={async (feedback) => { setShowRegenModal(false); await workflow.handleRegenerate(feedback); }} isSubmitting={workflow.isAnalyzing} /> {/* Duplicate Project Dialog */} workflow.setShowDuplicateDialog(false)} maxWidth="sm" fullWidth PaperProps={{ sx: { background: "linear-gradient(135deg, #1e293b 0%, #0f172a 100%)", border: "1px solid rgba(167, 139, 250, 0.3)", borderRadius: 3, }, }} > Duplicate Project Found A project with a similar idea already exists. You can edit the existing project or create a new one (which will overwrite the previous). Existing project idea:

{workflow.duplicateProjectInfo.idea}

); }; export default PodcastDashboard;