Fixes to Generate Pillar Chips
This commit is contained in:
@@ -22,6 +22,7 @@ import LoadingSkeleton from '../shared/LoadingSkeleton';
|
||||
import ErrorDisplay from '../shared/ErrorDisplay';
|
||||
import EmptyState from '../shared/EmptyState';
|
||||
import ContentLifecyclePillars from './ContentLifecyclePillars';
|
||||
import AnalyticsInsights from './components/AnalyticsInsights';
|
||||
|
||||
// Shared types and utilities
|
||||
import { Tool } from '../shared/types';
|
||||
@@ -244,6 +245,9 @@ const MainDashboard: React.FC = () => {
|
||||
{/* Content Lifecycle Pillars - First Panel */}
|
||||
<ContentLifecyclePillars />
|
||||
|
||||
{/* Analytics Insights - Good/Bad/Ugly */}
|
||||
<AnalyticsInsights />
|
||||
|
||||
{/* Search and Filter */}
|
||||
<SearchFilter
|
||||
searchQuery={searchQuery}
|
||||
@@ -267,8 +271,8 @@ const MainDashboard: React.FC = () => {
|
||||
transition={{ duration: 0.5, delay: categoryIndex * 0.1 }}
|
||||
>
|
||||
<Box sx={{ mb: 5 }}>
|
||||
{/* Only show Category Header when no specific category is selected (showing all tools) */}
|
||||
{selectedCategory === null && (
|
||||
{/* Show Category Header when no specific category is selected OR when searching across all categories */}
|
||||
{(selectedCategory === null || searchQuery) && (
|
||||
<CategoryHeader
|
||||
categoryName={categoryName}
|
||||
category={category}
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
CardContent,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip,
|
||||
Modal,
|
||||
IconButton,
|
||||
Chip,
|
||||
Stack,
|
||||
Divider
|
||||
} from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { keyframes } from '@mui/system';
|
||||
import {
|
||||
CheckCircle as CheckIcon,
|
||||
WarningAmber as WarningIcon,
|
||||
Error as ErrorIcon,
|
||||
Close as CloseIcon,
|
||||
TrendingUp as TrendingUpIcon,
|
||||
TrendingDown as TrendingDownIcon,
|
||||
Info as InfoIcon
|
||||
} from '@mui/icons-material';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface Insight {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
metric: string;
|
||||
value: string;
|
||||
trend: 'up' | 'down' | 'stable';
|
||||
priority: 'low' | 'medium' | 'high' | 'critical';
|
||||
category: 'engagement' | 'reach' | 'conversion' | 'seo' | 'content';
|
||||
platform: 'facebook' | 'linkedin' | 'twitter' | 'instagram' | 'website';
|
||||
detailedAnalysis: string;
|
||||
recommendations: string[];
|
||||
impact: string;
|
||||
timeframe: string;
|
||||
}
|
||||
|
||||
interface AnalyticsData {
|
||||
theGood: Insight[];
|
||||
theBad: Insight[];
|
||||
theUgly: Insight[];
|
||||
}
|
||||
|
||||
interface AnalyticsInsightsProps {
|
||||
data?: AnalyticsData; // optional - falls back to mock
|
||||
onActionClick?: (action: 'alwrity' | 'ignore', insight: Insight) => void;
|
||||
}
|
||||
|
||||
const ColumnCard = styled(Card)(({ theme }) => ({
|
||||
background: 'linear-gradient(180deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.08) 100%)',
|
||||
border: '1px solid rgba(255,255,255,0.16)',
|
||||
backdropFilter: 'blur(18px)',
|
||||
WebkitBackdropFilter: 'blur(18px)',
|
||||
borderRadius: theme.spacing(2),
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 8px 20px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.22)',
|
||||
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-3px)',
|
||||
boxShadow: '0 12px 28px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.28)'
|
||||
}
|
||||
}));
|
||||
|
||||
const Pill = styled('div')<{ color: string }>(() => ({
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: 6,
|
||||
}));
|
||||
|
||||
const GradientHeader = styled(Box)<{ gradient: string }>(({ gradient }) => ({
|
||||
background: gradient,
|
||||
padding: '8px 12px',
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
}));
|
||||
|
||||
const Badge = styled('span')(({ theme }) => ({
|
||||
background: 'rgba(255,255,255,0.15)',
|
||||
border: '1px solid rgba(255,255,255,0.35)',
|
||||
color: 'white',
|
||||
borderRadius: 999,
|
||||
padding: '1px 6px',
|
||||
fontWeight: 700,
|
||||
fontSize: '0.65rem'
|
||||
}));
|
||||
|
||||
// Subtle shimmer animation for the title text
|
||||
const shimmerText = keyframes`
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
`;
|
||||
|
||||
const mockData: AnalyticsData = {
|
||||
theGood: [
|
||||
{
|
||||
id: 'good-1',
|
||||
title: 'LinkedIn Engagement Surge',
|
||||
description: 'LinkedIn engagement is up significantly this week.',
|
||||
metric: 'Engagement Rate',
|
||||
value: '+45%',
|
||||
trend: 'up',
|
||||
priority: 'high',
|
||||
category: 'engagement',
|
||||
platform: 'linkedin',
|
||||
detailedAnalysis: 'Recent posts on AI topics resonated strongly with your B2B audience.',
|
||||
recommendations: ['Post 3x/week on AI trends', 'Engage with comments within 2 hours'],
|
||||
impact: 'High lead-gen potential',
|
||||
timeframe: 'Last 7 days'
|
||||
},
|
||||
{
|
||||
id: 'good-2',
|
||||
title: 'Website Traffic Growth',
|
||||
description: 'Organic traffic increased due to improved SEO.',
|
||||
metric: 'Organic Traffic',
|
||||
value: '+23%',
|
||||
trend: 'up',
|
||||
priority: 'medium',
|
||||
category: 'seo',
|
||||
platform: 'website',
|
||||
detailedAnalysis: 'Technical fixes and content refresh improved rankings.',
|
||||
recommendations: ['Create 2 pillar pages', 'Refresh 5 top posts'],
|
||||
impact: 'Improved visibility',
|
||||
timeframe: 'Last 30 days'
|
||||
},
|
||||
{
|
||||
id: 'good-3',
|
||||
title: 'Top-Performing Post',
|
||||
description: 'A recent LinkedIn post outperformed baseline by 2.1x',
|
||||
metric: 'Engagement Index',
|
||||
value: '2.1x',
|
||||
trend: 'up',
|
||||
priority: 'medium',
|
||||
category: 'engagement',
|
||||
platform: 'linkedin',
|
||||
detailedAnalysis: 'Carousel format and thought leadership angle worked well.',
|
||||
recommendations: ['Use carousel weekly', 'Add CTA to subscribe'],
|
||||
impact: 'Audience growth',
|
||||
timeframe: 'This week'
|
||||
}
|
||||
],
|
||||
theBad: [
|
||||
{
|
||||
id: 'bad-1',
|
||||
title: 'Facebook Reach Decline',
|
||||
description: 'Facebook post reach dropped this month.',
|
||||
metric: 'Reach',
|
||||
value: '-18%',
|
||||
trend: 'down',
|
||||
priority: 'medium',
|
||||
category: 'reach',
|
||||
platform: 'facebook',
|
||||
detailedAnalysis: 'Algorithm change likely impacting page distribution.',
|
||||
recommendations: ['Test short video posts', 'Boost first-hour engagement'],
|
||||
impact: 'Lower awareness',
|
||||
timeframe: 'Last 30 days'
|
||||
},
|
||||
{
|
||||
id: 'bad-2',
|
||||
title: 'Email CTR Stagnant',
|
||||
description: 'Content CTR plateaued across campaigns.',
|
||||
metric: 'CTR',
|
||||
value: '0.9%',
|
||||
trend: 'stable',
|
||||
priority: 'low',
|
||||
category: 'content',
|
||||
platform: 'website',
|
||||
detailedAnalysis: 'Subject lines lack urgency; preview text uninspiring.',
|
||||
recommendations: ['A/B test subject lines', 'Add curiosity hook'],
|
||||
impact: 'Reduced visits',
|
||||
timeframe: 'Last 14 days'
|
||||
}
|
||||
],
|
||||
theUgly: [
|
||||
{
|
||||
id: 'ugly-1',
|
||||
title: 'Critical SEO Issues',
|
||||
description: '15 pages have broken internal links.',
|
||||
metric: 'Broken Links',
|
||||
value: '15 pages',
|
||||
trend: 'down',
|
||||
priority: 'critical',
|
||||
category: 'seo',
|
||||
platform: 'website',
|
||||
detailedAnalysis: 'Broken links hurt crawlability and user experience.',
|
||||
recommendations: ['Fix links immediately', 'Add automated link checks'],
|
||||
impact: 'Severe ranking risk',
|
||||
timeframe: 'Ongoing'
|
||||
},
|
||||
{
|
||||
id: 'ugly-2',
|
||||
title: 'Declining Conversions',
|
||||
description: 'Checkout conversion dropped vs prior month.',
|
||||
metric: 'CVR',
|
||||
value: '-12%',
|
||||
trend: 'down',
|
||||
priority: 'high',
|
||||
category: 'conversion',
|
||||
platform: 'website',
|
||||
detailedAnalysis: 'Funnel analysis shows friction on payment step.',
|
||||
recommendations: ['Simplify checkout', 'Add alternate payment'],
|
||||
impact: 'Direct revenue impact',
|
||||
timeframe: 'Last 30 days'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const getGradient = (type: 'good' | 'bad' | 'ugly') => {
|
||||
switch (type) {
|
||||
case 'good':
|
||||
return 'linear-gradient(135deg, rgba(76,175,80,0.55) 0%, rgba(139,195,74,0.55) 100%)';
|
||||
case 'bad':
|
||||
return 'linear-gradient(135deg, rgba(255,152,0,0.55) 0%, rgba(245,124,0,0.55) 100%)';
|
||||
default:
|
||||
return 'linear-gradient(135deg, rgba(244,67,54,0.55) 0%, rgba(233,30,99,0.55) 100%)';
|
||||
}
|
||||
};
|
||||
|
||||
const getIcon = (type: 'good' | 'bad' | 'ugly') => {
|
||||
switch (type) {
|
||||
case 'good':
|
||||
return <CheckIcon />;
|
||||
case 'bad':
|
||||
return <WarningIcon />;
|
||||
default:
|
||||
return <ErrorIcon />;
|
||||
}
|
||||
};
|
||||
|
||||
const TrendChip: React.FC<{ trend: Insight['trend'] }> = ({ trend }) => {
|
||||
if (trend === 'up') return <Chip size="small" icon={<TrendingUpIcon />} label="Up" sx={{ color: '#4CAF50', background: '#4CAF5022', border: '1px solid #4CAF5044', fontWeight: 700, fontSize: '0.6rem', height: 18 }} />;
|
||||
if (trend === 'down') return <Chip size="small" icon={<TrendingDownIcon />} label="Down" sx={{ color: '#F44336', background: '#F4433622', border: '1px solid #F4433644', fontWeight: 700, fontSize: '0.6rem', height: 18 }} />;
|
||||
return <Chip size="small" icon={<InfoIcon />} label="Stable" sx={{ color: '#90CAF9', background: '#90CAF922', border: '1px solid #90CAF944', fontWeight: 700, fontSize: '0.6rem', height: 18 }} />;
|
||||
};
|
||||
|
||||
const AnalyticsInsights: React.FC<AnalyticsInsightsProps> = ({ data, onActionClick }) => {
|
||||
const [hovered, setHovered] = React.useState<'good' | 'bad' | 'ugly' | null>(null);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [selected, setSelected] = React.useState<Insight | null>(null);
|
||||
|
||||
const insights = data || mockData;
|
||||
|
||||
const columns: Array<{ key: 'good' | 'bad' | 'ugly'; title: string; items: Insight[] }> = [
|
||||
{ key: 'good', title: 'The Good', items: insights.theGood },
|
||||
{ key: 'bad', title: 'The Bad', items: insights.theBad },
|
||||
{ key: 'ugly', title: 'The Ugly', items: insights.theUgly },
|
||||
];
|
||||
|
||||
const handleKnowMore = (insight: Insight) => {
|
||||
setSelected(insight);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => setOpen(false);
|
||||
|
||||
const handleAction = (action: 'alwrity' | 'ignore') => {
|
||||
if (selected && onActionClick) {
|
||||
onActionClick(action, selected);
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 2, mb: 2.5 }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
fontWeight: 800,
|
||||
mb: 1.5,
|
||||
fontSize: '1.1rem',
|
||||
background: 'linear-gradient(90deg, rgba(255,255,255,0.35), rgba(255,255,255,0.9) 50%, rgba(255,255,255,0.35))',
|
||||
WebkitBackgroundClip: 'text',
|
||||
backgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
backgroundSize: '200% 100%',
|
||||
animation: `${shimmerText} 3.2s linear infinite`,
|
||||
}}
|
||||
>
|
||||
Analytics Insights
|
||||
</Typography>
|
||||
<Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5}>
|
||||
{columns.map((col) => {
|
||||
const isHovered = hovered === col.key;
|
||||
const visibleItems = isHovered ? col.items : col.items.slice(0, 1);
|
||||
const gradient = getGradient(col.key);
|
||||
return (
|
||||
<motion.div key={col.key} style={{ flex: 1 }} onMouseEnter={() => setHovered(col.key)} onMouseLeave={() => setHovered(null)}>
|
||||
<ColumnCard>
|
||||
<GradientHeader gradient={gradient}>
|
||||
{getIcon(col.key)}
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800, fontSize: '0.9rem' }}>{col.title}</Typography>
|
||||
<Badge>{col.items.length}</Badge>
|
||||
</GradientHeader>
|
||||
|
||||
<CardContent sx={{ p: 1.5 }}>
|
||||
<Stack spacing={1}>
|
||||
{visibleItems.map((insight) => (
|
||||
<Box key={insight.id} sx={{
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
border: '1px solid rgba(255,255,255,0.18)',
|
||||
borderRadius: 1.5,
|
||||
p: 1
|
||||
}}>
|
||||
<Stack direction="row" spacing={0.5} alignItems="center" sx={{ mb: 0.25 }}>
|
||||
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.95)', fontWeight: 700, fontSize: '0.8rem' }}>
|
||||
{insight.title}
|
||||
</Typography>
|
||||
<TrendChip trend={insight.trend} />
|
||||
</Stack>
|
||||
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.8)', fontSize: '0.7rem', lineHeight: 1.2 }}>
|
||||
{insight.description}
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={0.5} sx={{ mt: 0.5 }}>
|
||||
<Chip size="small" label={`${insight.metric}: ${insight.value}`} sx={{ color: 'rgba(255,255,255,0.95)', background: 'rgba(255,255,255,0.12)', border: '1px solid rgba(255,255,255,0.24)', fontWeight: 700, fontSize: '0.65rem', height: 20 }} />
|
||||
<Chip size="small" label={insight.platform} sx={{ color: 'rgba(255,255,255,0.85)', background: 'rgba(255,255,255,0.08)', fontSize: '0.65rem', height: 20 }} />
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
{isHovered && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Tooltip title={`Open detailed insights for ${col.title.toLowerCase()}.`}>
|
||||
<span>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => handleKnowMore(col.items[0])}
|
||||
size="small"
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontWeight: 800,
|
||||
background: gradient,
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
||||
fontSize: '0.75rem',
|
||||
px: 2,
|
||||
py: 0.5
|
||||
}}
|
||||
>
|
||||
Know More
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</ColumnCard>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
|
||||
<Modal open={open} onClose={handleClose}>
|
||||
<Box sx={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: { xs: '92%', md: 900 },
|
||||
maxHeight: '80vh',
|
||||
overflowY: 'auto',
|
||||
background: 'linear-gradient(180deg, rgba(16,24,39,0.92) 0%, rgba(26,33,56,0.92) 100%)',
|
||||
border: '1px solid rgba(255,255,255,0.18)',
|
||||
borderRadius: 3,
|
||||
boxShadow: '0 26px 80px rgba(0,0,0,0.5)'
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', p: 2.5, borderBottom: '1px solid rgba(255,255,255,0.15)' }}>
|
||||
<Typography variant="h6" sx={{ color: 'rgba(255,255,255,0.95)', fontWeight: 800 }}>
|
||||
{selected?.title}
|
||||
</Typography>
|
||||
<IconButton onClick={handleClose} sx={{ color: 'rgba(255,255,255,0.85)' }}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<CardContent>
|
||||
<Stack spacing={1.5}>
|
||||
<Typography variant="body1" sx={{ color: 'rgba(255,255,255,0.9)' }}>
|
||||
{selected?.detailedAnalysis}
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Chip size="small" label={`${selected?.metric}: ${selected?.value}`} sx={{ color: 'rgba(255,255,255,0.95)', background: 'rgba(255,255,255,0.12)', border: '1px solid rgba(255,255,255,0.24)', fontWeight: 700 }} />
|
||||
{selected?.platform && (
|
||||
<Chip size="small" label={selected.platform} sx={{ color: 'rgba(255,255,255,0.85)', background: 'rgba(255,255,255,0.08)' }} />
|
||||
)}
|
||||
{selected?.impact && (
|
||||
<Chip size="small" label={`Impact: ${selected.impact}`} sx={{ color: 'rgba(255,255,255,0.85)', background: 'rgba(255,255,255,0.08)' }} />
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<Divider sx={{ my: 1.5, borderColor: 'rgba(255,255,255,0.15)' }} />
|
||||
<Typography variant="subtitle2" sx={{ color: 'rgba(255,255,255,0.9)', fontWeight: 800 }}>
|
||||
Recommendations
|
||||
</Typography>
|
||||
<Stack spacing={0.75}>
|
||||
{selected?.recommendations.map((rec, idx) => (
|
||||
<Typography key={idx} variant="body2" sx={{ color: 'rgba(255,255,255,0.8)' }}>• {rec}</Typography>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1.5, mt: 2 }}>
|
||||
<Tooltip title="Save this as a memory for ALwrity AI to take action automatically.">
|
||||
<span>
|
||||
<Button variant="contained" color="success" onClick={() => handleAction('alwrity')} sx={{ textTransform: 'none', fontWeight: 800 }}>
|
||||
ALwrity it
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip title="Dismiss for now. You can revisit later in analytics.">
|
||||
<span>
|
||||
<Button variant="outlined" color="inherit" onClick={() => handleAction('ignore')} sx={{ textTransform: 'none', fontWeight: 800 }}>
|
||||
Ignore it
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Box>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnalyticsInsights;
|
||||
|
||||
|
||||
@@ -35,77 +35,107 @@ const SearchFilter: React.FC<SearchFilterProps> = ({
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Descriptions for category tooltips
|
||||
const categoryDescriptions: Record<string, string> = {
|
||||
'Generate Content': 'AI multimodal generators: Blog, Image, Audio, Video.',
|
||||
'SEO Tools': 'Enterprise SEO analysis, technical tools, and optimization utilities.',
|
||||
'Social Media': 'Platform writers for Facebook, LinkedIn, Twitter, Instagram, YouTube.',
|
||||
'Dashboards': 'Analytics dashboards: SEO, Social, Website, Strategy, and Calendar.'
|
||||
};
|
||||
return (
|
||||
<SearchContainer>
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mb: 3 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
placeholder="Search tools..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon sx={{ color: 'rgba(255, 255, 255, 0.7)' }} />
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: searchQuery && (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={onClearSearch} size="small">
|
||||
<ClearIcon sx={{ color: 'rgba(255, 255, 255, 0.7)' }} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
color: 'white',
|
||||
'& fieldset': {
|
||||
borderColor: 'rgba(255, 255, 255, 0.3)',
|
||||
{/* Single Row Layout: Search Input + Category Filters */}
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
{/* Search Input - Takes available space */}
|
||||
<Box sx={{ flex: '1 1 300px', minWidth: '250px' }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
placeholder="Search tools..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon sx={{ color: 'rgba(255, 255, 255, 0.85)' }} />
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: searchQuery && (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={onClearSearch} size="small">
|
||||
<ClearIcon sx={{ color: 'rgba(255, 255, 255, 0.85)' }} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
color: 'white',
|
||||
background: 'linear-gradient(135deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.08) 100%)',
|
||||
borderRadius: 2.5,
|
||||
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.2), 0 6px 18px rgba(0,0,0,0.25)',
|
||||
'& fieldset': {
|
||||
borderColor: 'rgba(255, 255, 255, 0.28)',
|
||||
},
|
||||
'&:hover fieldset': {
|
||||
borderColor: 'rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
'&.Mui-focused fieldset': {
|
||||
borderColor: 'rgba(255, 255, 255, 0.85)',
|
||||
},
|
||||
'& input::placeholder': {
|
||||
color: 'rgba(255, 255, 255, 0.85)',
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
'&:hover fieldset': {
|
||||
borderColor: 'rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
'&.Mui-focused fieldset': {
|
||||
borderColor: 'rgba(255, 255, 255, 0.8)',
|
||||
},
|
||||
'& input::placeholder': {
|
||||
color: 'rgba(255, 255, 255, 0.6)',
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Filter Icon */}
|
||||
<Tooltip title="Filter by category">
|
||||
<IconButton sx={{ color: 'rgba(255, 255, 255, 0.7)' }}>
|
||||
<FilterIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
{/* Enhanced Category Filter with Tool Count Badges */}
|
||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||
<CategoryChip
|
||||
label="All Tools"
|
||||
onClick={() => onCategoryChange(null)}
|
||||
active={selectedCategory === null}
|
||||
theme={theme}
|
||||
toolCount={Object.values(toolCategories).reduce((total, category) => total + getToolCount(category), 0)}
|
||||
/>
|
||||
{Object.keys(toolCategories).map((category) => (
|
||||
{/* Category Filter Chips - Inline with search */}
|
||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<CategoryChip
|
||||
key={category}
|
||||
label={category}
|
||||
onClick={() => onCategoryChange(category)}
|
||||
active={selectedCategory === category}
|
||||
label="All Tools"
|
||||
onClick={() => onCategoryChange(null)}
|
||||
active={selectedCategory === null}
|
||||
theme={theme}
|
||||
toolCount={getToolCount(toolCategories[category])}
|
||||
toolCount={Object.values(toolCategories).reduce((total, category) => total + getToolCount(category), 0)}
|
||||
/>
|
||||
))}
|
||||
{Object.keys(toolCategories).map((category) => {
|
||||
const cat = toolCategories[category] as any;
|
||||
const gradient = (cat && cat.gradient) || undefined;
|
||||
const desc = categoryDescriptions[category] || `Filter tools by ${category}.`;
|
||||
return (
|
||||
<Tooltip
|
||||
key={category}
|
||||
title={`${desc} Total tools: ${getToolCount(cat)}.`}
|
||||
placement="top"
|
||||
arrow
|
||||
enterDelay={300}
|
||||
>
|
||||
<CategoryChip
|
||||
label={category}
|
||||
onClick={() => onCategoryChange(category)}
|
||||
active={selectedCategory === category}
|
||||
theme={theme}
|
||||
toolCount={getToolCount(cat)}
|
||||
gradient={gradient}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Sub-category Filter for SEO & Analytics */}
|
||||
{selectedCategory === 'SEO & Analytics' && 'subCategories' in toolCategories['SEO & Analytics'] && (
|
||||
{/* Sub-category Filter for SEO Tools */}
|
||||
{selectedCategory === 'SEO Tools' && 'subCategories' in toolCategories['SEO Tools'] && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: 'rgba(255, 255, 255, 0.8)', mb: 1, fontWeight: 600 }}>
|
||||
Filter by sub-category:
|
||||
@@ -117,7 +147,7 @@ const SearchFilter: React.FC<SearchFilterProps> = ({
|
||||
active={selectedSubCategory === null}
|
||||
theme={theme}
|
||||
/>
|
||||
{Object.keys(toolCategories['SEO & Analytics'].subCategories).map((subCategory) => (
|
||||
{Object.keys(toolCategories['SEO Tools'].subCategories).map((subCategory) => (
|
||||
<CategoryChip
|
||||
key={subCategory}
|
||||
label={subCategory}
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Star as StarIcon,
|
||||
StarBorder as StarBorderIcon
|
||||
StarBorder as StarBorderIcon,
|
||||
LockOutlined as LockIcon
|
||||
} from '@mui/icons-material';
|
||||
import { ToolCardProps } from './types';
|
||||
import { getStatusConfig } from './utils';
|
||||
@@ -22,6 +23,7 @@ const ToolCard: React.FC<ToolCardProps> = ({
|
||||
onToggleFavorite
|
||||
}) => {
|
||||
const config = getStatusConfig(tool.status);
|
||||
const isLocked = tool.status === 'premium' || tool.status === 'pro';
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -30,17 +32,17 @@ const ToolCard: React.FC<ToolCardProps> = ({
|
||||
backdropFilter: 'blur(24px)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
||||
borderRadius: 3,
|
||||
cursor: 'pointer',
|
||||
cursor: isLocked ? 'not-allowed' : 'pointer',
|
||||
transition: 'all 0.3s ease',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-8px) scale(1.02)',
|
||||
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.3)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
transform: isLocked ? 'none' : 'translateY(-8px) scale(1.02)',
|
||||
boxShadow: isLocked ? 'none' : '0 20px 40px rgba(0, 0, 0, 0.3)',
|
||||
border: isLocked ? '1px solid rgba(255, 255, 255, 0.12)' : '1px solid rgba(255, 255, 255, 0.2)',
|
||||
},
|
||||
}}
|
||||
onClick={() => onToolClick(tool)}
|
||||
onClick={() => { if (!isLocked) onToolClick(tool); }}
|
||||
>
|
||||
<CardContent sx={{ p: 3 }}>
|
||||
{/* Header with Icon and Status */}
|
||||
@@ -59,8 +61,9 @@ const ToolCard: React.FC<ToolCardProps> = ({
|
||||
background: `${config.color}20`,
|
||||
color: config.color,
|
||||
border: `1px solid ${config.color}40`,
|
||||
fontWeight: 600,
|
||||
fontWeight: 700,
|
||||
fontSize: '0.75rem',
|
||||
textTransform: 'capitalize',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -102,28 +105,48 @@ const ToolCard: React.FC<ToolCardProps> = ({
|
||||
Features:
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{tool.features.slice(0, 3).map((feature, index) => (
|
||||
<Chip
|
||||
key={index}
|
||||
label={feature}
|
||||
size="small"
|
||||
sx={{
|
||||
background: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.8)',
|
||||
fontSize: '0.7rem',
|
||||
height: '20px',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{tool.features.slice(0, 3).map((feature, index) => {
|
||||
const isDashboard = tool.name.toLowerCase().includes('dashboard');
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
label={feature}
|
||||
size="small"
|
||||
sx={{
|
||||
background: isDashboard
|
||||
? 'linear-gradient(135deg, rgba(156, 39, 176, 0.3) 0%, rgba(123, 31, 162, 0.2) 100%)'
|
||||
: 'rgba(255, 255, 255, 0.1)',
|
||||
color: isDashboard ? 'rgba(255, 255, 255, 0.95)' : 'rgba(255, 255, 255, 0.8)',
|
||||
fontSize: '0.7rem',
|
||||
height: '22px',
|
||||
border: isDashboard ? '1px solid rgba(156, 39, 176, 0.4)' : 'none',
|
||||
fontWeight: isDashboard ? 600 : 400,
|
||||
boxShadow: isDashboard ? '0 2px 8px rgba(156, 39, 176, 0.2)' : 'none',
|
||||
transition: 'all 0.2s ease',
|
||||
'&:hover': isDashboard ? {
|
||||
background: 'linear-gradient(135deg, rgba(156, 39, 176, 0.4) 0%, rgba(123, 31, 162, 0.3) 100%)',
|
||||
transform: 'translateY(-1px)',
|
||||
boxShadow: '0 4px 12px rgba(156, 39, 176, 0.3)',
|
||||
} : {},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{tool.features.length > 3 && (
|
||||
<Chip
|
||||
label={`+${tool.features.length - 3} more`}
|
||||
size="small"
|
||||
sx={{
|
||||
background: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.6)',
|
||||
background: tool.name.toLowerCase().includes('dashboard')
|
||||
? 'linear-gradient(135deg, rgba(156, 39, 176, 0.2) 0%, rgba(123, 31, 162, 0.1) 100%)'
|
||||
: 'rgba(255, 255, 255, 0.1)',
|
||||
color: tool.name.toLowerCase().includes('dashboard')
|
||||
? 'rgba(255, 255, 255, 0.8)'
|
||||
: 'rgba(255, 255, 255, 0.6)',
|
||||
fontSize: '0.7rem',
|
||||
height: '20px',
|
||||
height: '22px',
|
||||
border: tool.name.toLowerCase().includes('dashboard') ? '1px solid rgba(156, 39, 176, 0.3)' : 'none',
|
||||
fontWeight: tool.name.toLowerCase().includes('dashboard') ? 600 : 400,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -131,6 +154,40 @@ const ToolCard: React.FC<ToolCardProps> = ({
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
|
||||
{/* Locked overlay for Premium/Pro */}
|
||||
{isLocked && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background: 'linear-gradient(180deg, rgba(0,0,0,0.45) 0%, rgba(0,0,0,0.65) 100%)',
|
||||
backdropFilter: 'blur(2px)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
color: 'rgba(255,255,255,0.95)',
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
border: '1px solid rgba(255,255,255,0.25)',
|
||||
px: 1.5,
|
||||
py: 0.75,
|
||||
borderRadius: 2,
|
||||
boxShadow: '0 8px 24px rgba(0,0,0,0.35)'
|
||||
}}>
|
||||
<LockIcon fontSize="small" />
|
||||
<Typography variant="body2" sx={{ fontWeight: 700 }}>
|
||||
{(config.label || 'Pro') + ' • Locked'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -96,28 +96,35 @@ export const SearchContainer = styled(Box)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const CategoryChip = styled(Chip, {
|
||||
shouldForwardProp: (prop) => prop !== 'active' && prop !== 'toolCount',
|
||||
})<{ active?: boolean; toolCount?: number }>(({ theme, active, toolCount }) => ({
|
||||
shouldForwardProp: (prop) => prop !== 'active' && prop !== 'toolCount' && prop !== 'gradient',
|
||||
})<{ active?: boolean; toolCount?: number; gradient?: string }>(({ theme, active, toolCount, gradient }) => ({
|
||||
background: active
|
||||
? 'linear-gradient(135deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.2) 100%)'
|
||||
: 'rgba(255, 255, 255, 0.1)',
|
||||
? (gradient || 'linear-gradient(135deg, rgba(76, 175, 80, 0.4) 0%, rgba(139, 195, 74, 0.3) 50%, rgba(255, 255, 255, 0.2) 100%)')
|
||||
: 'linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.05) 100%)',
|
||||
color: 'white',
|
||||
fontWeight: active ? 700 : 600,
|
||||
fontSize: '0.9rem',
|
||||
padding: theme.spacing(1, 2),
|
||||
border: active
|
||||
? '2px solid rgba(255, 255, 255, 0.6)'
|
||||
: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
? '2px solid rgba(255, 255, 255, 0.6)'
|
||||
: '1px solid rgba(255, 255, 255, 0.25)',
|
||||
boxShadow: active
|
||||
? '0 6px 20px rgba(255, 255, 255, 0.2), 0 0 0 1px rgba(255,255,255,0.1)'
|
||||
: 'none',
|
||||
? '0 6px 20px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.2)'
|
||||
: '0 2px 8px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
|
||||
transform: active ? 'translateY(-2px) scale(1.05)' : 'none',
|
||||
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
position: 'relative',
|
||||
'&:hover': {
|
||||
background: 'rgba(255, 255, 255, 0.25)',
|
||||
background: active
|
||||
? (gradient || 'linear-gradient(135deg, rgba(76, 175, 80, 0.5) 0%, rgba(139, 195, 74, 0.4) 50%, rgba(255, 255, 255, 0.25) 100%)')
|
||||
: 'linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.1) 100%)',
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
||||
boxShadow: active
|
||||
? '0 8px 25px rgba(76, 175, 80, 0.4), 0 0 0 1px rgba(76, 175, 80, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.3)'
|
||||
: '0 4px 15px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.2)',
|
||||
border: active
|
||||
? '2px solid rgba(76, 175, 80, 0.8)'
|
||||
: '1px solid rgba(255, 255, 255, 0.4)',
|
||||
},
|
||||
'& .MuiChip-label': {
|
||||
padding: theme.spacing(0.5, 1),
|
||||
|
||||
@@ -6,7 +6,12 @@ export const getToolsForCategory = (category: Category, selectedSubCategory: str
|
||||
if (selectedSubCategory && category.subCategories[selectedSubCategory]) {
|
||||
return category.subCategories[selectedSubCategory].tools;
|
||||
}
|
||||
return [];
|
||||
// When no subcategory is selected, return all tools from all subcategories
|
||||
const allTools: Tool[] = [];
|
||||
Object.values(category.subCategories).forEach(subCategory => {
|
||||
allTools.push(...subCategory.tools);
|
||||
});
|
||||
return allTools;
|
||||
}
|
||||
return category.tools;
|
||||
};
|
||||
@@ -19,7 +24,9 @@ export const getFilteredCategories = (
|
||||
const filtered: ToolCategories = {};
|
||||
|
||||
Object.entries(toolCategories).forEach(([categoryName, category]) => {
|
||||
if (selectedCategory && categoryName !== selectedCategory) {
|
||||
// If there's a search query, search across ALL categories regardless of selected category
|
||||
// If no search query, respect the selected category filter
|
||||
if (!searchQuery && selectedCategory && categoryName !== selectedCategory) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,6 +67,14 @@ export const getStatusConfig = (status: string) => {
|
||||
return { color: '#FF9800', icon: '⚠', label: 'Good' };
|
||||
case 'needs_action':
|
||||
return { color: '#F44336', icon: '✗', label: 'Needs Action' };
|
||||
case 'premium':
|
||||
return { color: '#9C27B0', icon: '⭐', label: 'Premium' };
|
||||
case 'beta':
|
||||
return { color: '#FF9800', icon: '🧪', label: 'Beta' };
|
||||
case 'pro':
|
||||
return { color: '#2196F3', icon: '💎', label: 'Pro' };
|
||||
case 'active':
|
||||
return { color: '#4CAF50', icon: '✓', label: 'Active' };
|
||||
default:
|
||||
return { color: '#9E9E9E', icon: 'ℹ', label: 'Unknown' };
|
||||
}
|
||||
@@ -74,6 +89,14 @@ export const getStatusColor = (status: string) => {
|
||||
return '#FF9800';
|
||||
case 'needs_action':
|
||||
return '#F44336';
|
||||
case 'premium':
|
||||
return '#9C27B0';
|
||||
case 'beta':
|
||||
return '#FF9800';
|
||||
case 'pro':
|
||||
return '#2196F3';
|
||||
case 'active':
|
||||
return '#4CAF50';
|
||||
default:
|
||||
return '#9E9E9E';
|
||||
}
|
||||
@@ -88,6 +111,14 @@ export const getStatusIcon = (status: string) => {
|
||||
return '⚠';
|
||||
case 'needs_action':
|
||||
return '✗';
|
||||
case 'premium':
|
||||
return '⭐';
|
||||
case 'beta':
|
||||
return '🧪';
|
||||
case 'pro':
|
||||
return '💎';
|
||||
case 'active':
|
||||
return '✓';
|
||||
default:
|
||||
return 'ℹ';
|
||||
}
|
||||
|
||||
@@ -9,67 +9,66 @@ import {
|
||||
Speed as SpeedIcon,
|
||||
Business as BusinessIcon,
|
||||
SocialDistance as SocialIcon,
|
||||
Create as CreateIcon
|
||||
Create as CreateIcon,
|
||||
Dashboard as DashboardIcon,
|
||||
Facebook as FacebookIcon,
|
||||
LinkedIn as LinkedInIcon,
|
||||
Twitter as TwitterIcon,
|
||||
Instagram as InstagramIcon,
|
||||
Web as WebIcon,
|
||||
Timeline as StrategyIcon,
|
||||
CalendarMonth as CalendarIcon,
|
||||
Image as ImageIcon,
|
||||
Audiotrack as AudioIcon,
|
||||
VideoLibrary as VideoIcon
|
||||
} from '@mui/icons-material';
|
||||
import { ToolCategories } from '../components/shared/types';
|
||||
|
||||
export const toolCategories: ToolCategories = {
|
||||
'AI Content Writers': {
|
||||
icon: React.createElement(ArticleIcon),
|
||||
'Generate Content': {
|
||||
icon: React.createElement(AutoAwesomeIcon),
|
||||
color: '#4CAF50',
|
||||
gradient: 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)',
|
||||
tools: [
|
||||
{
|
||||
name: 'AI Blog Writer',
|
||||
description: 'Generate engaging blog posts with AI',
|
||||
name: 'Blog Writer',
|
||||
description: 'AI-powered blog post generation with SEO optimization',
|
||||
icon: React.createElement(ArticleIcon),
|
||||
status: 'active',
|
||||
path: '/ai-blog-writer',
|
||||
features: ['SEO Optimized', 'Multiple Formats', 'Custom Tone']
|
||||
status: 'beta',
|
||||
path: '/blog-writer',
|
||||
features: ['SEO Optimized', 'Multiple Formats', 'Custom Tone', 'Research Integration', 'Plagiarism Free'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'AI Essay Writer',
|
||||
description: 'Academic and professional essay writing',
|
||||
icon: React.createElement(CreateIcon),
|
||||
status: 'active',
|
||||
path: '/ai-essay-writer',
|
||||
features: ['Academic Style', 'Citation Support', 'Plagiarism Free']
|
||||
name: 'Image Generator',
|
||||
description: 'AI image creation and visual content generation',
|
||||
icon: React.createElement(ImageIcon),
|
||||
status: 'beta',
|
||||
path: '/image-generator',
|
||||
features: ['AI Art Generation', 'Style Customization', 'High Resolution', 'Brand Consistency', 'Multiple Formats'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'AI News Article Writer',
|
||||
description: 'Professional news and article writing',
|
||||
icon: React.createElement(ArticleIcon),
|
||||
status: 'active',
|
||||
path: '/ai-news-writer',
|
||||
features: ['Fact-Checked', 'Journalistic Style', 'Breaking News']
|
||||
name: 'Audio Generator',
|
||||
description: 'AI voice synthesis and audio content creation',
|
||||
icon: React.createElement(AudioIcon),
|
||||
status: 'premium',
|
||||
path: '/audio-generator',
|
||||
features: ['Voice Synthesis', 'Multiple Languages', 'Custom Voices', 'Audio Editing', 'Export Options'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'AI Story Writer',
|
||||
description: 'Creative storytelling and fiction writing',
|
||||
icon: React.createElement(CreateIcon),
|
||||
status: 'active',
|
||||
path: '/ai-story-writer',
|
||||
features: ['Creative Writing', 'Character Development', 'Plot Generation']
|
||||
},
|
||||
{
|
||||
name: 'AI Copywriter',
|
||||
description: 'Marketing copy and advertising content',
|
||||
icon: React.createElement(CampaignIcon),
|
||||
status: 'active',
|
||||
path: '/ai-copywriter',
|
||||
features: ['Persuasive Writing', 'Brand Voice', 'Call-to-Action']
|
||||
},
|
||||
{
|
||||
name: 'AI Product Description Writer',
|
||||
description: 'Compelling product descriptions',
|
||||
icon: React.createElement(BusinessIcon),
|
||||
status: 'active',
|
||||
path: '/ai-product-writer',
|
||||
features: ['E-commerce Optimized', 'Feature Highlighting', 'Conversion Focused']
|
||||
name: 'Video Generator',
|
||||
description: 'AI video creation and multimedia content generation',
|
||||
icon: React.createElement(VideoIcon),
|
||||
status: 'premium',
|
||||
path: '/video-generator',
|
||||
features: ['AI Video Creation', 'Scene Generation', 'Voice Integration', 'Custom Branding', 'Export Formats'],
|
||||
isHighlighted: true
|
||||
}
|
||||
]
|
||||
},
|
||||
'SEO & Analytics': {
|
||||
'SEO Tools': {
|
||||
icon: React.createElement(SearchIcon),
|
||||
color: '#2196F3',
|
||||
gradient: 'linear-gradient(135deg, #2196F3 0%, #1976D2 100%)',
|
||||
@@ -249,7 +248,7 @@ export const toolCategories: ToolCategories = {
|
||||
name: 'Facebook Content Writer',
|
||||
description: 'Engaging Facebook posts and ads with AI persona optimization',
|
||||
icon: React.createElement(SocialIcon),
|
||||
status: 'premium',
|
||||
status: 'beta',
|
||||
path: '/facebook-writer',
|
||||
features: ['Persona-Aware AI', 'Engagement Focused', 'Ad Copy', 'Post Scheduling', 'Platform Optimization'],
|
||||
isHighlighted: true
|
||||
@@ -258,7 +257,7 @@ export const toolCategories: ToolCategories = {
|
||||
name: 'LinkedIn Content Writer',
|
||||
description: 'Professional LinkedIn content with AI persona optimization',
|
||||
icon: React.createElement(BusinessIcon),
|
||||
status: 'premium',
|
||||
status: 'beta',
|
||||
path: '/linkedin-writer',
|
||||
features: ['Persona-Aware AI', 'Professional Tone', 'Thought Leadership', 'B2B Focus', 'Platform Optimization'],
|
||||
isHighlighted: true
|
||||
@@ -267,7 +266,7 @@ export const toolCategories: ToolCategories = {
|
||||
name: 'Twitter Content Writer',
|
||||
description: 'Viral Twitter threads and tweets',
|
||||
icon: React.createElement(SocialIcon),
|
||||
status: 'active',
|
||||
status: 'premium',
|
||||
path: '/twitter-writer',
|
||||
features: ['Viral Potential', 'Thread Creation', 'Hashtag Optimization']
|
||||
},
|
||||
@@ -275,7 +274,7 @@ export const toolCategories: ToolCategories = {
|
||||
name: 'Instagram Content Writer',
|
||||
description: 'Visual and engaging Instagram content',
|
||||
icon: React.createElement(SocialIcon),
|
||||
status: 'active',
|
||||
status: 'premium',
|
||||
path: '/instagram-writer',
|
||||
features: ['Visual Descriptions', 'Hashtag Strategy', 'Story Content']
|
||||
},
|
||||
@@ -283,95 +282,84 @@ export const toolCategories: ToolCategories = {
|
||||
name: 'YouTube Content Writer',
|
||||
description: 'Video scripts and descriptions',
|
||||
icon: React.createElement(SocialIcon),
|
||||
status: 'active',
|
||||
status: 'premium',
|
||||
path: '/youtube-writer',
|
||||
features: ['Video Scripts', 'SEO Descriptions', 'Engagement Hooks']
|
||||
}
|
||||
]
|
||||
},
|
||||
'Business & Marketing': {
|
||||
icon: React.createElement(BusinessIcon),
|
||||
'Dashboards': {
|
||||
icon: React.createElement(DashboardIcon),
|
||||
color: '#9C27B0',
|
||||
gradient: 'linear-gradient(135deg, #9C27B0 0%, #7B1FA2 100%)',
|
||||
tools: [
|
||||
{
|
||||
name: 'Financial Report Generator',
|
||||
description: 'Professional financial analysis and reports',
|
||||
icon: React.createElement(AnalyticsIcon),
|
||||
status: 'active',
|
||||
path: '/financial-reports',
|
||||
features: ['Data Analysis', 'Professional Reports', 'Insights Generation']
|
||||
},
|
||||
{
|
||||
name: 'Email Templates',
|
||||
description: 'Professional email templates and campaigns',
|
||||
icon: React.createElement(CampaignIcon),
|
||||
status: 'active',
|
||||
path: '/email-templates',
|
||||
features: ['Professional Templates', 'A/B Testing', 'Automation']
|
||||
},
|
||||
{
|
||||
name: 'Press Release Writer',
|
||||
description: 'Newsworthy press releases',
|
||||
icon: React.createElement(ArticleIcon),
|
||||
status: 'active',
|
||||
path: '/press-releases',
|
||||
features: ['Newsworthy Content', 'Media Ready', 'Distribution Ready']
|
||||
},
|
||||
{
|
||||
name: 'Landing Page Copy',
|
||||
description: 'High-converting landing page content',
|
||||
icon: React.createElement(BusinessIcon),
|
||||
status: 'active',
|
||||
path: '/landing-page-copy',
|
||||
features: ['Conversion Focused', 'A/B Testing', 'UX Optimized']
|
||||
},
|
||||
{
|
||||
name: 'Competitive Intelligence',
|
||||
description: 'Analyze competitors and market trends',
|
||||
icon: React.createElement(PsychologyIcon),
|
||||
status: 'premium',
|
||||
path: '/competitive-intelligence',
|
||||
features: ['Market Analysis', 'Competitor Tracking', 'Strategy Insights']
|
||||
}
|
||||
]
|
||||
},
|
||||
'Creative & Advanced': {
|
||||
icon: React.createElement(AutoAwesomeIcon),
|
||||
color: '#E91E63',
|
||||
gradient: 'linear-gradient(135deg, #E91E63 0%, #C2185B 100%)',
|
||||
tools: [
|
||||
{
|
||||
name: 'AI Agents Crew',
|
||||
description: 'Multi-agent AI content creation team',
|
||||
icon: React.createElement(AutoAwesomeIcon),
|
||||
status: 'premium',
|
||||
path: '/ai-agents-crew',
|
||||
features: ['Multi-Agent System', 'Collaborative Writing', 'Advanced AI']
|
||||
},
|
||||
{
|
||||
name: 'Content Performance Predictor',
|
||||
description: 'Predict content performance and engagement',
|
||||
icon: React.createElement(AnalyticsIcon),
|
||||
status: 'premium',
|
||||
path: '/content-predictor',
|
||||
features: ['Performance Prediction', 'Engagement Analysis', 'ROI Forecasting']
|
||||
},
|
||||
{
|
||||
name: 'Web Researcher',
|
||||
description: 'AI-powered web research and analysis',
|
||||
name: 'SEO Dashboard',
|
||||
description: 'Comprehensive SEO analytics and performance tracking',
|
||||
icon: React.createElement(SearchIcon),
|
||||
status: 'active',
|
||||
path: '/web-researcher',
|
||||
features: ['Real-time Research', 'Data Analysis', 'Insight Generation']
|
||||
status: 'beta',
|
||||
path: '/seo-dashboard',
|
||||
features: ['Keyword Rankings', 'Traffic Analytics', 'Backlink Monitoring', 'Site Health', 'Competitor Analysis'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'Content Scheduler',
|
||||
description: 'Intelligent content scheduling and planning',
|
||||
icon: React.createElement(CampaignIcon),
|
||||
status: 'active',
|
||||
path: '/content-scheduler',
|
||||
features: ['Smart Scheduling', 'Calendar Integration', 'Performance Tracking']
|
||||
name: 'Facebook Dashboard',
|
||||
description: 'Facebook page insights and content performance analytics',
|
||||
icon: React.createElement(FacebookIcon),
|
||||
status: 'beta',
|
||||
path: '/facebook-dashboard',
|
||||
features: ['Page Insights', 'Post Performance', 'Audience Analytics', 'Engagement Metrics', 'Ad Performance'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'LinkedIn Dashboard',
|
||||
description: 'LinkedIn company page and content analytics',
|
||||
icon: React.createElement(LinkedInIcon),
|
||||
status: 'beta',
|
||||
path: '/linkedin-dashboard',
|
||||
features: ['Company Analytics', 'Content Performance', 'Lead Generation', 'B2B Insights', 'Network Growth'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'Twitter Dashboard',
|
||||
description: 'Twitter analytics and engagement tracking',
|
||||
icon: React.createElement(TwitterIcon),
|
||||
status: 'pro',
|
||||
path: '/twitter-dashboard',
|
||||
features: ['Tweet Analytics', 'Follower Growth', 'Engagement Rates', 'Hashtag Performance', 'Mention Tracking']
|
||||
},
|
||||
{
|
||||
name: 'Instagram Dashboard',
|
||||
description: 'Instagram insights and visual content analytics',
|
||||
icon: React.createElement(InstagramIcon),
|
||||
status: 'pro',
|
||||
path: '/instagram-dashboard',
|
||||
features: ['Story Analytics', 'Post Performance', 'Reach & Impressions', 'Hashtag Insights', 'Audience Demographics']
|
||||
},
|
||||
{
|
||||
name: 'Website Dashboard',
|
||||
description: 'Website performance and visitor analytics',
|
||||
icon: React.createElement(WebIcon),
|
||||
status: 'pro',
|
||||
path: '/website-dashboard',
|
||||
features: ['Traffic Analysis', 'Page Performance', 'User Behavior', 'Conversion Tracking', 'Site Speed']
|
||||
},
|
||||
{
|
||||
name: 'Strategy Dashboard',
|
||||
description: 'Content strategy planning and performance overview',
|
||||
icon: React.createElement(StrategyIcon),
|
||||
status: 'beta',
|
||||
path: '/strategy-dashboard',
|
||||
features: ['Content Planning', 'Performance Overview', 'Goal Tracking', 'ROI Analysis', 'Strategic Insights'],
|
||||
isHighlighted: true
|
||||
},
|
||||
{
|
||||
name: 'Calendar Dashboard',
|
||||
description: 'Content calendar management and scheduling analytics',
|
||||
icon: React.createElement(CalendarIcon),
|
||||
status: 'beta',
|
||||
path: '/calendar-dashboard',
|
||||
features: ['Content Scheduling', 'Publishing Calendar', 'Performance Tracking', 'Team Collaboration', 'Content Planning']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user