From edf3f32b3caf5f8d8f780b81e6717617e66a3008 Mon Sep 17 00:00:00 2001 From: ajaysi Date: Tue, 7 Apr 2026 17:45:43 +0530 Subject: [PATCH] feat: Add hamburger menu to Podcast Maker header and move Bible to AnalysisPanel - Add hamburger menu to Header with gradient styling - Move Help, My Episodes, My Projects, New Episode into dropdown menu - Move PodcastBiblePanel into AnalysisPanel header as icon button - Display Bible details in a styled Popover - Improve overall header UX and mobile responsiveness --- .../components/PodcastMaker/AnalysisPanel.tsx | 112 ++++++++- .../PodcastMaker/PodcastDashboard.tsx | 13 +- .../PodcastMaker/PodcastDashboard/Header.tsx | 228 ++++++++++++------ 3 files changed, 266 insertions(+), 87 deletions(-) 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