feat(podcast): add Get Trending Topics modal to podcast topic input
Frontend Changes: - Added TrendingTopicsModal with tabs (Interest Chart, Regions, Related Topics, Related Queries) - Reuses existing TrendsChart component from Research module - Clickable chips for related topics/queries that populate the topic input - Added 'Get Trending Topics' green button next to 'Enhance Topic With AI' - Responsive layout: buttons stack on mobile, side-by-side on desktop - Wired up modal state in CreateModal - Backend endpoint and podcastApi method committed in prior push
This commit is contained in:
@@ -15,6 +15,7 @@ import { PodcastConfiguration } from "./CreateStep/PodcastConfiguration";
|
||||
import { AvatarSelector } from "./CreateStep/AvatarSelector";
|
||||
import { CreateActions } from "./CreateStep/CreateActions";
|
||||
import { EnhancedTopicChoicesModal } from "./EnhancedTopicChoicesModal";
|
||||
import { TrendingTopicsModal } from "./CreateStep/TrendingTopicsModal";
|
||||
|
||||
const ENHANCE_TOPIC_PROGRESS_MESSAGES = [
|
||||
"Analyzing your topic idea...",
|
||||
@@ -61,6 +62,10 @@ export const CreateModal: React.FC<CreateModalProps> = ({ onCreate, open, defaul
|
||||
const [choicesModalOpen, setChoicesModalOpen] = useState(false);
|
||||
const [editedChoices, setEditedChoices] = useState<string[]>([]);
|
||||
|
||||
// Trending topics state
|
||||
const [trendingModalOpen, setTrendingModalOpen] = useState(false);
|
||||
const [trendingLoading, setTrendingLoading] = useState(false);
|
||||
|
||||
// Rotate placeholder every 3 seconds
|
||||
useEffect(() => {
|
||||
if (!topicInput) {
|
||||
@@ -582,9 +587,11 @@ export const CreateModal: React.FC<CreateModalProps> = ({ onCreate, open, defaul
|
||||
isUrl={isUrl}
|
||||
showAIDetailsButton={showAIDetailsButton}
|
||||
onAIDetailsClick={handleAIDetailsClick}
|
||||
onTrendingTopicsClick={() => setTrendingModalOpen(true)}
|
||||
placeholderIndex={placeholderIndex}
|
||||
loading={enhancingTopic}
|
||||
loadingMessage={enhanceTopicMessage}
|
||||
trendingLoading={trendingLoading}
|
||||
estimatedCost={null}
|
||||
duration={duration}
|
||||
speakers={speakers}
|
||||
@@ -651,6 +658,14 @@ export const CreateModal: React.FC<CreateModalProps> = ({ onCreate, open, defaul
|
||||
onSelectChoice={handleChoiceSelection}
|
||||
loading={enhancingTopic}
|
||||
/>
|
||||
|
||||
{/* Trending Topics Modal */}
|
||||
<TrendingTopicsModal
|
||||
open={trendingModalOpen}
|
||||
onClose={() => setTrendingModalOpen(false)}
|
||||
onSelectTopic={(topic) => setTopicInput(topic)}
|
||||
initialKeywords={topicInput}
|
||||
/>
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { Box, Typography, TextField, Tooltip, Button, CircularProgress, alpha, Stack, Chip } from "@mui/material";
|
||||
import { AutoAwesome as AutoAwesomeIcon, AttachMoney as AttachMoneyIcon } from "@mui/icons-material";
|
||||
import { AutoAwesome as AutoAwesomeIcon, AttachMoney as AttachMoneyIcon, TrendingUp as TrendingUpIcon } from "@mui/icons-material";
|
||||
import { Knobs } from "../types";
|
||||
|
||||
export const TOPIC_PLACEHOLDERS = [
|
||||
@@ -18,9 +18,11 @@ interface TopicUrlInputProps {
|
||||
isUrl: boolean;
|
||||
showAIDetailsButton: boolean;
|
||||
onAIDetailsClick?: () => void;
|
||||
onTrendingTopicsClick?: () => void;
|
||||
placeholderIndex: number;
|
||||
loading?: boolean;
|
||||
loadingMessage?: string;
|
||||
trendingLoading?: boolean;
|
||||
estimatedCost?: {
|
||||
ttsCost: number;
|
||||
avatarCost: number;
|
||||
@@ -39,9 +41,11 @@ export const TopicUrlInput: React.FC<TopicUrlInputProps> = ({
|
||||
isUrl,
|
||||
showAIDetailsButton,
|
||||
onAIDetailsClick,
|
||||
onTrendingTopicsClick,
|
||||
placeholderIndex,
|
||||
loading = false,
|
||||
loadingMessage,
|
||||
trendingLoading = false,
|
||||
estimatedCost,
|
||||
duration = 1,
|
||||
speakers = 1,
|
||||
@@ -249,9 +253,47 @@ export const TopicUrlInput: React.FC<TopicUrlInputProps> = ({
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* Enhance topic with AI button - appears when user types (and not a URL) */}
|
||||
{/* Enhance topic with AI button + Get Trending Topics - appears when user types (and not a URL) */}
|
||||
{showAIDetailsButton && !isUrl && (
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 1.5, flexDirection: "column", alignItems: "flex-end", gap: 0.6 }}>
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 1.5, flexDirection: { xs: "column", sm: "row" }, alignItems: { xs: "stretch", sm: "flex-end" }, gap: 1 }}>
|
||||
<Button
|
||||
size="small"
|
||||
variant="contained"
|
||||
startIcon={
|
||||
trendingLoading ? (
|
||||
<CircularProgress size={14} thickness={5} sx={{ color: "rgba(255,255,255,0.92)" }} />
|
||||
) : (
|
||||
<TrendingUpIcon />
|
||||
)
|
||||
}
|
||||
onClick={onTrendingTopicsClick}
|
||||
disabled={trendingLoading || loading}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: 600,
|
||||
borderRadius: 2.5,
|
||||
color: "#f8fbff",
|
||||
px: 2,
|
||||
py: 0.75,
|
||||
border: "1px solid rgba(16, 185, 129, 0.4)",
|
||||
background: "linear-gradient(120deg, #10b981 0%, #059669 55%, #047857 100%)",
|
||||
boxShadow: "0 8px 18px rgba(16, 185, 129, 0.28), inset 0 1px 0 rgba(255,255,255,0.22)",
|
||||
"&:hover": {
|
||||
background: "linear-gradient(120deg, #34d399 0%, #10b981 50%, #059669 100%)",
|
||||
boxShadow: "0 12px 24px rgba(16, 185, 129, 0.35), inset 0 1px 0 rgba(255,255,255,0.26)",
|
||||
transform: "translateY(-1px)",
|
||||
},
|
||||
"&.Mui-disabled": {
|
||||
color: "#e2e8f0",
|
||||
borderColor: "rgba(110, 231, 183, 0.7)",
|
||||
background: "linear-gradient(120deg, #10b981 0%, #059669 55%, #047857 100%)",
|
||||
opacity: 0.78,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{trendingLoading ? "Fetching Trends..." : "Get Trending Topics"}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
variant="contained"
|
||||
@@ -263,7 +305,7 @@ export const TopicUrlInput: React.FC<TopicUrlInputProps> = ({
|
||||
)
|
||||
}
|
||||
onClick={onAIDetailsClick}
|
||||
disabled={loading}
|
||||
disabled={loading || trendingLoading}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontSize: "0.875rem",
|
||||
@@ -290,13 +332,13 @@ export const TopicUrlInput: React.FC<TopicUrlInputProps> = ({
|
||||
>
|
||||
{loading ? "Enhancing Topic With AI..." : "Enhance Topic With AI"}
|
||||
</Button>
|
||||
{loading && (
|
||||
<Typography sx={{ fontSize: "0.75rem", color: "#1d4ed8", fontWeight: 600 }}>
|
||||
{loadingMessage || "Analyzing your topic and improving clarity..."}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{loading && (
|
||||
<Typography sx={{ fontSize: "0.75rem", color: "#1d4ed8", fontWeight: 600, mt: 0.5, textAlign: "right" }}>
|
||||
{loadingMessage || "Analyzing your topic and improving clarity..."}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Box,
|
||||
Typography,
|
||||
Tabs,
|
||||
Tab,
|
||||
Chip,
|
||||
Stack,
|
||||
CircularProgress,
|
||||
Alert,
|
||||
LinearProgress,
|
||||
IconButton,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
TrendingUp as TrendingUpIcon,
|
||||
Close as CloseIcon,
|
||||
Public as PublicIcon,
|
||||
Search as SearchIcon,
|
||||
AutoAwesome as AutoAwesomeIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { TrendsChart } from "../../Research/steps/components/TrendsChart";
|
||||
import { GoogleTrendsData } from "../../Research/types/intent.types";
|
||||
import { podcastApi } from "../../../services/podcastApi";
|
||||
|
||||
interface TrendingTopicsModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSelectTopic: (topic: string) => void;
|
||||
initialKeywords: string;
|
||||
}
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
index: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
const TabPanel: React.FC<TabPanelProps> = ({ children, value, index }) => (
|
||||
<Box role="tabpanel" hidden={value !== index} sx={{ pt: 2 }}>
|
||||
{value === index && children}
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const TrendingTopicsModal: React.FC<TrendingTopicsModalProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
onSelectTopic,
|
||||
initialKeywords,
|
||||
}) => {
|
||||
const [trendsData, setTrendsData] = useState<GoogleTrendsData | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [tabValue, setTabValue] = useState(0);
|
||||
|
||||
const fetchTrends = useCallback(async () => {
|
||||
if (!initialKeywords.trim()) return;
|
||||
|
||||
const keywords = initialKeywords
|
||||
.split(/[,;]+/)
|
||||
.map((k) => k.trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, 5);
|
||||
|
||||
if (keywords.length === 0) return;
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setTrendsData(null);
|
||||
|
||||
try {
|
||||
const result = await podcastApi.getTrendingTopics({
|
||||
keywords,
|
||||
timeframe: "today 12-m",
|
||||
geo: "US",
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
setTrendsData(result.data as GoogleTrendsData);
|
||||
} else {
|
||||
setError(result.error || "Failed to fetch trends data");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err?.response?.data?.detail || err?.message || "Failed to fetch trending topics");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [initialKeywords]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && initialKeywords.trim()) {
|
||||
fetchTrends();
|
||||
}
|
||||
}, [open, initialKeywords, fetchTrends]);
|
||||
|
||||
const handleSelectTopic = (topic: string) => {
|
||||
onSelectTopic(topic);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setTrendsData(null);
|
||||
setError(null);
|
||||
setTabValue(0);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const regions = trendsData?.interest_by_region || [];
|
||||
const relatedTopics = trendsData?.related_topics || { top: [], rising: [] };
|
||||
const relatedQueries = trendsData?.related_queries || { top: [], rising: [] };
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: 3,
|
||||
maxHeight: "90vh",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
pb: 1,
|
||||
background: "linear-gradient(135deg, rgba(102, 126, 234, 0.08) 0%, rgba(118, 75, 162, 0.08) 100%)",
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" alignItems="center" spacing={1.5}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 0.75,
|
||||
borderRadius: 1.5,
|
||||
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<TrendingUpIcon sx={{ color: "#fff", fontSize: "1.25rem" }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, color: "#0f172a", fontSize: "1.1rem" }}>
|
||||
Trending Topics
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: "#64748b" }}>
|
||||
Google Trends insights for “{initialKeywords}”
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
<IconButton onClick={handleClose} sx={{ color: "#64748b" }}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent sx={{ px: 3, py: 2 }}>
|
||||
{loading && (
|
||||
<Box sx={{ py: 4, textAlign: "center" }}>
|
||||
<CircularProgress size={40} sx={{ color: "#667eea", mb: 2 }} />
|
||||
<Typography variant="body2" sx={{ color: "#64748b" }}>
|
||||
Fetching trending topics from Google Trends...
|
||||
</Typography>
|
||||
<LinearProgress sx={{ mt: 2, borderRadius: 1 }} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<Alert severity="error" sx={{ mt: 2, borderRadius: 2 }}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{!loading && trendsData && (
|
||||
<>
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
onChange={(_, v) => setTabValue(v)}
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
sx={{
|
||||
borderBottom: "1px solid rgba(0,0,0,0.08)",
|
||||
"& .MuiTab-root": {
|
||||
textTransform: "none",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.875rem",
|
||||
},
|
||||
"& .Mui-selected": {
|
||||
color: "#667eea",
|
||||
},
|
||||
"& .MuiTabs-indicator": {
|
||||
background: "linear-gradient(90deg, #667eea 0%, #764ba2 100%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tab icon={<TrendingUpIcon sx={{ fontSize: "1rem" }} />} iconPosition="start" label="Interest Chart" />
|
||||
<Tab icon={<PublicIcon sx={{ fontSize: "1rem" }} />} iconPosition="start" label="Regions" />
|
||||
<Tab icon={<AutoAwesomeIcon sx={{ fontSize: "1rem" }} />} iconPosition="start" label="Related Topics" />
|
||||
<Tab icon={<SearchIcon sx={{ fontSize: "1rem" }} />} iconPosition="start" label="Related Queries" />
|
||||
</Tabs>
|
||||
|
||||
<TabPanel value={tabValue} index={0}>
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<TrendsChart data={trendsData} height={280} showAverage={true} />
|
||||
</Box>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={tabValue} index={1}>
|
||||
{regions.length === 0 ? (
|
||||
<Box sx={{ py: 3, textAlign: "center" }}>
|
||||
<PublicIcon sx={{ fontSize: 40, color: "#cbd5e1", mb: 1 }} />
|
||||
<Typography variant="body2" sx={{ color: "#64748b" }}>
|
||||
No regional data available for this topic.
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Stack spacing={1} sx={{ maxHeight: 350, overflow: "auto" }}>
|
||||
{regions.slice(0, 15).map((region: any, idx: number) => {
|
||||
const regionName = region.regionName || region.geoName || region.name || `Region ${idx + 1}`;
|
||||
const value = region.value || region.interest || 0;
|
||||
const maxVal = Math.max(...regions.slice(0, 15).map((r: any) => r.value || r.interest || 0));
|
||||
const pct = maxVal > 0 ? (value / maxVal) * 100 : 0;
|
||||
return (
|
||||
<Box
|
||||
key={idx}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 1.5,
|
||||
p: 1,
|
||||
borderRadius: 1,
|
||||
"&:hover": { background: "rgba(102, 126, 234, 0.04)" },
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" sx={{ minWidth: 30, fontWeight: 600, color: "#64748b" }}>
|
||||
{idx + 1}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ flex: 1, fontWeight: 500, color: "#0f172a" }}>
|
||||
{regionName}
|
||||
</Typography>
|
||||
<Box sx={{ flex: 1, maxWidth: 200 }}>
|
||||
<Box
|
||||
sx={{
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
background: "rgba(102, 126, 234, 0.1)",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: `${pct}%`,
|
||||
borderRadius: 4,
|
||||
background: "linear-gradient(90deg, #667eea 0%, #764ba2 100%)",
|
||||
transition: "width 0.3s ease",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600, color: "#667eea", minWidth: 30 }}>
|
||||
{value}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={tabValue} index={2}>
|
||||
{relatedTopics.top.length === 0 && relatedTopics.rising.length === 0 ? (
|
||||
<Box sx={{ py: 3, textAlign: "center" }}>
|
||||
<AutoAwesomeIcon sx={{ fontSize: 40, color: "#cbd5e1", mb: 1 }} />
|
||||
<Typography variant="body2" sx={{ color: "#64748b" }}>
|
||||
No related topics data available.
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Stack spacing={2}>
|
||||
{relatedTopics.rising.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, color: "#059669", fontWeight: 700 }}>
|
||||
Rising Topics
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
|
||||
{relatedTopics.rising.map((topic: any, idx: number) => {
|
||||
const label = topic.topic_title || topic.title || topic.query || String(topic);
|
||||
return (
|
||||
<Chip
|
||||
key={idx}
|
||||
label={label}
|
||||
size="small"
|
||||
onClick={() => handleSelectTopic(label)}
|
||||
sx={{
|
||||
background: "linear-gradient(135deg, rgba(16, 185, 129, 0.12) 0%, rgba(5, 150, 105, 0.12) 100%)",
|
||||
color: "#059669",
|
||||
border: "1px solid rgba(16, 185, 129, 0.3)",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.75rem",
|
||||
cursor: "pointer",
|
||||
mb: 0.5,
|
||||
"&:hover": {
|
||||
background: "linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(5, 150, 105, 0.2) 100%)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
{relatedTopics.top.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, color: "#667eea", fontWeight: 700 }}>
|
||||
Top Topics
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
|
||||
{relatedTopics.top.map((topic: any, idx: number) => {
|
||||
const label = topic.topic_title || topic.title || topic.query || String(topic);
|
||||
return (
|
||||
<Chip
|
||||
key={idx}
|
||||
label={label}
|
||||
size="small"
|
||||
onClick={() => handleSelectTopic(label)}
|
||||
sx={{
|
||||
background: "linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%)",
|
||||
color: "#667eea",
|
||||
border: "1px solid rgba(102, 126, 234, 0.25)",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.75rem",
|
||||
cursor: "pointer",
|
||||
mb: 0.5,
|
||||
"&:hover": {
|
||||
background: "linear-gradient(135deg, rgba(102, 126, 234, 0.18) 0%, rgba(118, 75, 162, 0.18) 100%)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={tabValue} index={3}>
|
||||
{relatedQueries.top.length === 0 && relatedQueries.rising.length === 0 ? (
|
||||
<Box sx={{ py: 3, textAlign: "center" }}>
|
||||
<SearchIcon sx={{ fontSize: 40, color: "#cbd5e1", mb: 1 }} />
|
||||
<Typography variant="body2" sx={{ color: "#64748b" }}>
|
||||
No related queries data available.
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Stack spacing={2}>
|
||||
{relatedQueries.rising.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, color: "#059669", fontWeight: 700 }}>
|
||||
Rising Queries
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
|
||||
{relatedQueries.rising.map((query: any, idx: number) => {
|
||||
const label = query.query || query.title || String(query);
|
||||
return (
|
||||
<Chip
|
||||
key={idx}
|
||||
label={label}
|
||||
size="small"
|
||||
onClick={() => handleSelectTopic(label)}
|
||||
sx={{
|
||||
background: "linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%)",
|
||||
color: "#d97706",
|
||||
border: "1px solid rgba(245, 158, 11, 0.25)",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.75rem",
|
||||
cursor: "pointer",
|
||||
mb: 0.5,
|
||||
"&:hover": {
|
||||
background: "linear-gradient(135deg, rgba(245, 158, 11, 0.18) 0%, rgba(217, 119, 6, 0.18) 100%)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
{relatedQueries.top.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, color: "#667eea", fontWeight: 700 }}>
|
||||
Top Queries
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
|
||||
{relatedQueries.top.map((query: any, idx: number) => {
|
||||
const label = query.query || query.title || String(query);
|
||||
return (
|
||||
<Chip
|
||||
key={idx}
|
||||
label={label}
|
||||
size="small"
|
||||
onClick={() => handleSelectTopic(label)}
|
||||
sx={{
|
||||
background: "linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%)",
|
||||
color: "#667eea",
|
||||
border: "1px solid rgba(102, 126, 234, 0.25)",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.75rem",
|
||||
cursor: "pointer",
|
||||
mb: 0.5,
|
||||
"&:hover": {
|
||||
background: "linear-gradient(135deg, rgba(102, 126, 234, 0.18) 0%, rgba(118, 75, 162, 0.18) 100%)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</TabPanel>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!loading && !error && !trendsData && (
|
||||
<Box sx={{ py: 4, textAlign: "center" }}>
|
||||
<TrendingUpIcon sx={{ fontSize: 48, color: "#cbd5e1", mb: 1 }} />
|
||||
<Typography variant="body2" sx={{ color: "#64748b" }}>
|
||||
Enter a topic and click “Get Trending Topics” to see Google Trends data.
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions
|
||||
sx={{
|
||||
px: 3,
|
||||
py: 2,
|
||||
borderTop: "1px solid rgba(0,0,0,0.08)",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" sx={{ color: "#94a3b8" }}>
|
||||
Data from Google Trends
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontWeight: 600,
|
||||
color: "#64748b",
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user