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
This commit is contained in:
ajaysi
2026-04-07 17:45:43 +05:30
parent e59c77b221
commit edf3f32b3c
3 changed files with 266 additions and 87 deletions

View File

@@ -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<AnalysisPanelProps> = ({
speakers,
avatarUrl,
avatarPrompt,
bible,
onRegenerate,
onUpdateAnalysis
onUpdateAnalysis,
onUpdateBible
}) => {
const [activeTab, setActiveTab] = useState<TabId>('inputs');
const [avatarBlobUrl, setAvatarBlobUrl] = useState<string | null>(null);
const [avatarLoading, setAvatarLoading] = useState(false);
const [avatarError, setAvatarError] = useState(false);
const [bibleAnchorEl, setBibleAnchorEl] = useState<HTMLElement | null>(null);
// Edit states
const [isEditing, setIsEditing] = useState(false);
const [editedAnalysis, setEditedAnalysis] = useState<PodcastAnalysis | null>(null);
const [editedBible, setEditedBible] = useState<PodcastBible | null>(null);
const tabs: TabConfig[] = [
{ id: 'inputs', label: 'Your Inputs', icon: <InputIcon /> },
@@ -326,6 +332,29 @@ export const AnalysisPanel: React.FC<AnalysisPanelProps> = ({
</Stack>
<Stack direction="row" spacing={1}>
{/* Bible Button */}
{bible && (
<Tooltip title="Podcast Bible - Hyper-personalized context">
<IconButton
onClick={(e) => 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",
},
}}
>
<BibleIcon sx={{ color: bibleAnchorEl ? "#fff" : "#667eea", fontSize: 20 }} />
</IconButton>
</Tooltip>
)}
{isEditing ? (
<>
<SecondaryButton
@@ -442,6 +471,81 @@ export const AnalysisPanel: React.FC<AnalysisPanelProps> = ({
<GuestTab analysis={currentAnalysis} />
)}
</Box>
{/* Bible Popover */}
<Popover
open={Boolean(bibleAnchorEl)}
anchorEl={bibleAnchorEl}
onClose={() => 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)",
},
}}
>
<Box sx={{ p: 2.5 }}>
<Stack spacing={2}>
<Stack direction="row" alignItems="center" spacing={1}>
<BibleIcon sx={{ color: "#a78bfa", fontSize: 24 }} />
<Typography variant="h6" sx={{ color: "#fff", fontWeight: 700 }}>
Podcast Bible
</Typography>
<Tooltip title="Hyper-personalized context derived from your onboarding data. This grounds all research and script generation.">
<IconButton size="small" sx={{ ml: 'auto' }}>
<Typography variant="caption" sx={{ color: "#94a3b8" }}></Typography>
</IconButton>
</Tooltip>
</Stack>
{/* Host Persona */}
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: "rgba(99, 102, 241, 0.1)", border: "1px solid rgba(99, 102, 241, 0.2)" }}>
<Typography variant="caption" sx={{ color: "#a78bfa", fontWeight: 600, mb: 0.5, display: "block" }}>
Host Persona
</Typography>
<Typography variant="body2" sx={{ color: "rgba(255,255,255,0.8)", fontSize: "0.8rem" }}>
{bible?.host?.name || "Not set"} {bible?.host?.background || "No background"} {bible?.host?.vocal_style || "No style"}
</Typography>
</Box>
{/* Audience DNA */}
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: "rgba(34, 197, 94, 0.1)", border: "1px solid rgba(34, 197, 94, 0.2)" }}>
<Typography variant="caption" sx={{ color: "#22c55e", fontWeight: 600, mb: 0.5, display: "block" }}>
Audience DNA
</Typography>
<Typography variant="body2" sx={{ color: "rgba(255,255,255,0.8)", fontSize: "0.8rem" }}>
{bible?.audience?.expertise_level || "General"} {(bible?.audience?.interests || []).slice(0, 3).join(", ") || "Various interests"}
</Typography>
</Box>
{/* Brand DNA */}
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: "rgba(249, 115, 22, 0.1)", border: "1px solid rgba(249, 115, 22, 0.2)" }}>
<Typography variant="caption" sx={{ color: "#f97316", fontWeight: 600, mb: 0.5, display: "block" }}>
Brand DNA
</Typography>
<Typography variant="body2" sx={{ color: "rgba(255,255,255,0.8)", fontSize: "0.8rem" }}>
{bible?.brand?.industry || "No industry"} {bible?.brand?.tone || "No tone"} {bible?.brand?.communication_style || "No style"}
</Typography>
</Box>
<Typography variant="caption" sx={{ color: "rgba(255,255,255,0.5)", textAlign: "center", fontSize: "0.7rem" }}>
Podcast Bible personalizes all AI generation for your unique voice
</Typography>
</Stack>
</Box>
</Popover>
</Stack>
</GlassyCard>
);

View File

@@ -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 = () => {
</Alert>
)}
{/* Podcast Bible */}
{project && bible && (currentStep === 'analysis' || (currentStep === 'research' && !research)) && !showScriptEditor && !showRenderQueue && (
<PodcastBiblePanel
bible={bible}
onUpdate={(updated) => setBible(updated)}
/>
)}
{/* Podcast Bible - now in AnalysisPanel header */}
{(workflow.isAnalyzing || workflow.isResearching || workflow.isGeneratingScript) && (
<Stack direction="row" spacing={2} alignItems="center" sx={{ py: 1.5 }}>
<CircularProgress size={20} sx={{ color: "#667eea" }} />
@@ -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)}
/>
)}

View File

@@ -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<HeaderProps> = ({ onShowProjects, onNewEpisode }) => {
const navigate = useNavigate();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const isMenuOpen = Boolean(anchorEl);
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
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 (
<Box
@@ -25,7 +60,7 @@ export const Header: React.FC<HeaderProps> = ({ 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<HeaderProps> = ({ onShowProjects, onNewEpisode })
justifyContent="space-between"
alignItems="center"
flexWrap="wrap"
gap={2}
gap={1}
>
{/* Logo and Title */}
<Stack direction="row" alignItems="center" gap={1.5}>
<Box
sx={{
width: 44,
height: 44,
width: { xs: 36, md: 44 },
height: { xs: 36, md: 44 },
borderRadius: 2,
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
display: "flex",
@@ -60,7 +96,7 @@ export const Header: React.FC<HeaderProps> = ({ onShowProjects, onNewEpisode })
boxShadow: "0 4px 12px rgba(102, 126, 234, 0.3)",
}}
>
<MicIcon sx={{ color: "#fff", fontSize: 24 }} />
<MicIcon sx={{ color: "#fff", fontSize: { xs: 20, md: 24 } }} />
</Box>
<Typography
variant="h5"
@@ -69,87 +105,131 @@ export const Header: React.FC<HeaderProps> = ({ 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
</Typography>
</Stack>
<Stack
direction="row"
spacing={1}
alignItems="center"
flexWrap="wrap"
useFlexGap
sx={{
justifyContent: { xs: "flex-start", md: "flex-end" },
gap: { xs: 0.5, md: 1 },
minWidth: 0,
}}
>
{/* Right side - Hamburger Menu + HeaderControls + Create */}
<Stack direction="row" spacing={1} alignItems="center">
{/* Header Controls (alerts + user) */}
<HeaderControls colorMode="light" showAlerts={true} showUser={true} />
<SecondaryButton
onClick={() => window.open("/docs", "_blank")}
startIcon={<InfoIcon />}
{/* Hamburger Menu Button */}
<IconButton
onClick={handleMenuOpen}
sx={{
display: { xs: "none", lg: "flex" },
borderColor: "rgba(102, 126, 234, 0.3) !important",
color: "#667eea !important",
background: isMenuOpen
? "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
: "rgba(102, 126, 234, 0.1)",
border: "1px solid",
borderColor: isMenuOpen ? "transparent" : "rgba(102, 126, 234, 0.3)",
borderRadius: 2,
p: 1,
transition: "all 0.2s ease",
"&:hover": {
borderColor: "rgba(102, 126, 234, 0.5) !important",
background: "rgba(102, 126, 234, 0.1) !important",
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
borderColor: "transparent",
transform: "scale(1.05)",
},
}}
>
Help
</SecondaryButton>
<SecondaryButton
onClick={() => navigate("/asset-library?source_module=podcast_maker&asset_type=audio")}
startIcon={<LibraryMusicIcon />}
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 ? (
<CloseIcon sx={{ color: "#fff", fontSize: 20 }} />
) : (
<MenuIcon sx={{ color: "#667eea", fontSize: 20 }} />
)}
</IconButton>
{/* Dropdown Menu */}
<Menu
anchorEl={anchorEl}
open={isMenuOpen}
onClose={handleMenuClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
PaperProps={{
sx: {
mt: 1,
minWidth: 220,
borderRadius: 2,
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)",
"& .MuiMenuItem-root": {
color: "rgba(255, 255, 255, 0.85)",
px: 2,
py: 1.5,
transition: "all 0.15s ease",
"&:hover": {
background: "linear-gradient(135deg, rgba(102, 126, 234, 0.2) 0%, rgba(118, 75, 162, 0.2) 100%)",
color: "#fff",
},
},
"& .MuiListItemIcon-root": {
color: "#a78bfa",
minWidth: 36,
},
"& .MuiDivider-root": {
borderColor: "rgba(102, 126, 234, 0.2)",
my: 0.5,
},
},
}}
>
My Episodes
</SecondaryButton>
<SecondaryButton
onClick={onShowProjects}
startIcon={<MicIcon />}
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
</SecondaryButton>
<PrimaryButton
onClick={onNewEpisode}
startIcon={<AutoAwesomeIcon />}
sx={{
flexShrink: 0,
display: "flex",
}}
>
New Episode
</PrimaryButton>
<MenuItem onClick={handleNewEpisode}>
<ListItemIcon>
<AddIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary="New Episode"
primaryTypographyProps={{ fontWeight: 600 }}
/>
</MenuItem>
<MenuItem onClick={handleMyProjects}>
<ListItemIcon>
<FolderIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary="My Projects"
primaryTypographyProps={{ fontWeight: 500 }}
/>
</MenuItem>
<MenuItem onClick={handleMyEpisodes}>
<ListItemIcon>
<LibraryMusicIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary="My Episodes"
primaryTypographyProps={{ fontWeight: 500 }}
/>
</MenuItem>
<Divider />
<MenuItem onClick={handleHelp}>
<ListItemIcon>
<HelpIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary="Help & Docs"
primaryTypographyProps={{ fontWeight: 500 }}
/>
</MenuItem>
</Menu>
</Stack>
</Stack>
</Box>
);
};
};