diff --git a/frontend/src/components/PodcastMaker/AnalysisPanel.tsx b/frontend/src/components/PodcastMaker/AnalysisPanel.tsx index b9ac2cd9..fd228e83 100644 --- a/frontend/src/components/PodcastMaker/AnalysisPanel.tsx +++ b/frontend/src/components/PodcastMaker/AnalysisPanel.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; -import { Stack, Box, Typography, Divider, Chip, alpha, Button } from "@mui/material"; -import { Psychology as PsychologyIcon, Person as PersonIcon, Edit as EditIcon, Save as SaveIcon, Close as CloseIcon, Input as InputIcon, Groups as GroupsIcon, ListAlt as ListAltIcon, Lightbulb as TipsIcon, Article as ArticleIcon } from "@mui/icons-material"; -import { PodcastAnalysis, PodcastEstimate } from "./types"; +import { Stack, Box, Typography, Divider, Chip, alpha, Button, IconButton, Popover, TextField, Tooltip } from "@mui/material"; +import { Psychology as PsychologyIcon, Person as PersonIcon, Edit as EditIcon, Save as SaveIcon, Close as CloseIcon, Input as InputIcon, Groups as GroupsIcon, ListAlt as ListAltIcon, Lightbulb as TipsIcon, Article as ArticleIcon, AutoFixHigh as BibleIcon } from "@mui/icons-material"; +import { PodcastAnalysis, PodcastEstimate, PodcastBible } from "./types"; import { GlassyCard, glassyCardSx, SecondaryButton } from "./ui"; import { Refresh as RefreshIcon } from "@mui/icons-material"; import { aiApiClient } from "../../api/client"; @@ -15,8 +15,10 @@ interface AnalysisPanelProps { speakers?: number; avatarUrl?: string | null; avatarPrompt?: string | null; + bible?: PodcastBible | null; onRegenerate?: () => void; onUpdateAnalysis?: (updatedAnalysis: PodcastAnalysis) => void; + onUpdateBible?: (updatedBible: PodcastBible) => void; } type TabId = 'inputs' | 'audience' | 'outline' | 'details' | 'takeaways' | 'guest'; @@ -62,17 +64,21 @@ export const AnalysisPanel: React.FC = ({ speakers, avatarUrl, avatarPrompt, + bible, onRegenerate, - onUpdateAnalysis + onUpdateAnalysis, + onUpdateBible }) => { const [activeTab, setActiveTab] = useState('inputs'); const [avatarBlobUrl, setAvatarBlobUrl] = useState(null); const [avatarLoading, setAvatarLoading] = useState(false); const [avatarError, setAvatarError] = useState(false); + const [bibleAnchorEl, setBibleAnchorEl] = useState(null); // Edit states const [isEditing, setIsEditing] = useState(false); const [editedAnalysis, setEditedAnalysis] = useState(null); + const [editedBible, setEditedBible] = useState(null); const tabs: TabConfig[] = [ { id: 'inputs', label: 'Your Inputs', icon: }, @@ -326,6 +332,29 @@ export const AnalysisPanel: React.FC = ({ + {/* Bible Button */} + {bible && ( + + setBibleAnchorEl(e.currentTarget)} + sx={{ + bgcolor: bibleAnchorEl ? "linear-gradient(135deg, #667eea 0%, #764ba2 100%)" : "rgba(102, 126, 234, 0.1)", + border: "1px solid", + borderColor: bibleAnchorEl ? "transparent" : "rgba(102, 126, 234, 0.3)", + borderRadius: 2, + p: 1, + transition: "all 0.2s ease", + "&:hover": { + background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)", + borderColor: "transparent", + }, + }} + > + + + + )} + {isEditing ? ( <> = ({ )} + + {/* Bible Popover */} + setBibleAnchorEl(null)} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'right', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + PaperProps={{ + sx: { + mt: 1, + maxWidth: 420, + borderRadius: 3, + background: "linear-gradient(135deg, #1e293b 0%, #0f172a 100%)", + border: "1px solid rgba(102, 126, 234, 0.3)", + boxShadow: "0 10px 40px rgba(102, 126, 234, 0.25)", + }, + }} + > + + + + + + Podcast Bible + + + + ℹ️ + + + + + {/* Host Persona */} + + + Host Persona + + + {bible?.host?.name || "Not set"} • {bible?.host?.background || "No background"} • {bible?.host?.vocal_style || "No style"} + + + + {/* Audience DNA */} + + + Audience DNA + + + {bible?.audience?.expertise_level || "General"} • {(bible?.audience?.interests || []).slice(0, 3).join(", ") || "Various interests"} + + + + {/* Brand DNA */} + + + Brand DNA + + + {bible?.brand?.industry || "No industry"} • {bible?.brand?.tone || "No tone"} • {bible?.brand?.communication_style || "No style"} + + + + + Podcast Bible personalizes all AI generation for your unique voice + + + + ); diff --git a/frontend/src/components/PodcastMaker/PodcastDashboard.tsx b/frontend/src/components/PodcastMaker/PodcastDashboard.tsx index 83bad5d5..e2eb8c88 100644 --- a/frontend/src/components/PodcastMaker/PodcastDashboard.tsx +++ b/frontend/src/components/PodcastMaker/PodcastDashboard.tsx @@ -9,7 +9,6 @@ import { RenderQueue } from "./RenderQueue"; import { RecentEpisodesPreview } from "./RecentEpisodesPreview"; import { ProjectList } from "./ProjectList"; import { PreflightBlockDialog } from "./PreflightBlockDialog"; -import { PodcastBiblePanel } from "./PodcastBiblePanel"; import { Header, ProgressStepper, @@ -199,14 +198,8 @@ const PodcastDashboard: React.FC = () => { )} - {/* Podcast Bible */} - {project && bible && (currentStep === 'analysis' || (currentStep === 'research' && !research)) && !showScriptEditor && !showRenderQueue && ( - setBible(updated)} - /> - )} - + {/* Podcast Bible - now in AnalysisPanel header */} + {(workflow.isAnalyzing || workflow.isResearching || workflow.isGeneratingScript) && ( @@ -241,8 +234,10 @@ const PodcastDashboard: React.FC = () => { speakers={project?.speakers} avatarUrl={project?.avatarUrl} avatarPrompt={project?.avatarPrompt} + bible={bible} onRegenerate={() => setShowRegenModal(true)} onUpdateAnalysis={(updated) => projectState.setAnalysis(updated)} + onUpdateBible={(updated) => setBible(updated)} /> )} diff --git a/frontend/src/components/PodcastMaker/PodcastDashboard/Header.tsx b/frontend/src/components/PodcastMaker/PodcastDashboard/Header.tsx index 5e5e234b..d469b2ca 100644 --- a/frontend/src/components/PodcastMaker/PodcastDashboard/Header.tsx +++ b/frontend/src/components/PodcastMaker/PodcastDashboard/Header.tsx @@ -1,13 +1,18 @@ -import React from "react"; -import { Stack, Typography, Box } from "@mui/material"; +import React, { useState } from "react"; +import { Stack, Typography, Box, IconButton, Menu, MenuItem, Divider, ListItemIcon, ListItemText } from "@mui/material"; import { Mic as MicIcon, - Info as InfoIcon, + Menu as MenuIcon, + Close as CloseIcon, AutoAwesome as AutoAwesomeIcon, LibraryMusic as LibraryMusicIcon, + Folder as FolderIcon, + Help as HelpIcon, + Add as AddIcon, + BarChart as BarChartIcon, } from "@mui/icons-material"; import { useNavigate } from "react-router-dom"; -import { PrimaryButton, SecondaryButton } from "../ui"; +import { PrimaryButton } from "../ui"; import HeaderControls from "../../shared/HeaderControls"; interface HeaderProps { @@ -17,6 +22,36 @@ interface HeaderProps { export const Header: React.FC = ({ onShowProjects, onNewEpisode }) => { const navigate = useNavigate(); + const [anchorEl, setAnchorEl] = useState(null); + const isMenuOpen = Boolean(anchorEl); + + const handleMenuOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + }; + + const handleHelp = () => { + handleMenuClose(); + window.open("/docs", "_blank"); + }; + + const handleMyEpisodes = () => { + handleMenuClose(); + navigate("/asset-library?source_module=podcast_maker&asset_type=audio"); + }; + + const handleMyProjects = () => { + handleMenuClose(); + onShowProjects(); + }; + + const handleNewEpisode = () => { + handleMenuClose(); + onNewEpisode(); + }; return ( = ({ onShowProjects, onNewEpisode }) minWidth: 0, background: "linear-gradient(135deg, rgba(102, 126, 234, 0.08) 0%, rgba(118, 75, 162, 0.08) 100%)", borderRadius: 3, - p: { xs: 2, md: 2.5 }, + p: { xs: 1.5, md: 2.5 }, border: "1px solid rgba(102, 126, 234, 0.15)", position: "relative", overflow: "hidden", @@ -45,13 +80,14 @@ export const Header: React.FC = ({ onShowProjects, onNewEpisode }) justifyContent="space-between" alignItems="center" flexWrap="wrap" - gap={2} + gap={1} > + {/* Logo and Title */} = ({ onShowProjects, onNewEpisode }) boxShadow: "0 4px 12px rgba(102, 126, 234, 0.3)", }} > - + = ({ onShowProjects, onNewEpisode }) WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent", fontWeight: 700, - fontSize: { xs: "1.25rem", md: "1.5rem" }, + fontSize: { xs: "1.1rem", sm: "1.25rem", md: "1.5rem" }, letterSpacing: "-0.02em", }} > ALwrity Podcast Maker - + + {/* Right side - Hamburger Menu + HeaderControls + Create */} + + {/* Header Controls (alerts + user) */} - window.open("/docs", "_blank")} - startIcon={} + + {/* Hamburger Menu Button */} + - Help - - navigate("/asset-library?source_module=podcast_maker&asset_type=audio")} - startIcon={} - tooltip="View all podcast episodes in Asset Library" - sx={{ - display: { xs: "none", xl: "flex" }, - borderColor: "rgba(102, 126, 234, 0.3) !important", - color: "#667eea !important", - "&:hover": { - borderColor: "rgba(102, 126, 234, 0.5) !important", - background: "rgba(102, 126, 234, 0.1) !important", + {isMenuOpen ? ( + + ) : ( + + )} + + + {/* Dropdown Menu */} + - My Episodes - - } - tooltip="View and resume saved projects" - sx={{ - flexShrink: 0, - display: "flex !important", - borderColor: "rgba(102, 126, 234, 0.3) !important", - color: "#667eea !important", - "&:hover": { - borderColor: "rgba(102, 126, 234, 0.5) !important", - background: "rgba(102, 126, 234, 0.1) !important", - }, - }} - > - My Projects - - } - sx={{ - flexShrink: 0, - display: "flex", - }} - > - New Episode - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); -}; - +}; \ No newline at end of file