ALwrity LinkedIn Writer: Billing Dashboard: Compact View, Billing Overview, System Health Indicator, Cost Breakdown, Usage Trends, Usage Alerts, Comprehensive API Breakdown

This commit is contained in:
ajaysi
2025-09-11 11:09:10 +05:30
parent b156298e82
commit 1b65a9487b
84 changed files with 10143 additions and 156 deletions

View File

@@ -32,6 +32,7 @@ import AnalyzePillarChips from './components/AnalyzePillarChips';
import EngagePillarChips from './components/EngagePillarChips';
import EnhancedTodayChip from './components/EnhancedTodayChip';
import OnboardingModal from './components/OnboardingModal';
import WorkflowHeroSection from './components/WorkflowHeroSection';
import { pillarData } from './components/PillarData';
import { useWorkflowStore } from '../../stores/workflowStore';
@@ -487,6 +488,14 @@ const ContentLifecyclePillars: React.FC = () => {
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [onboardingModalOpen, setOnboardingModalOpen] = useState(false);
// Workflow store hooks
const {
currentWorkflow,
workflowProgress,
isLoading: workflowLoading,
startWorkflow,
} = useWorkflowStore();
const handleOnboardingClick = () => {
setOnboardingModalOpen(true);
};
@@ -495,6 +504,20 @@ const ContentLifecyclePillars: React.FC = () => {
setOnboardingModalOpen(false);
};
const handleStartWorkflow = async () => {
try {
if (currentWorkflow) {
await startWorkflow(currentWorkflow.id);
}
} catch (error) {
console.error('Failed to start workflow:', error);
}
};
// Check if workflow is active (in progress or completed)
const isWorkflowActive = currentWorkflow?.workflowStatus === 'in_progress' ||
currentWorkflow?.workflowStatus === 'completed';
return (
<>
<Box
@@ -503,7 +526,8 @@ const ContentLifecyclePillars: React.FC = () => {
background: 'linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
backdropFilter: 'blur(8px)',
borderRadius: 2,
mb: 4
mb: 4,
position: 'relative', // For hero section positioning
}}
>
<Container maxWidth="xl">
@@ -530,6 +554,13 @@ const ContentLifecyclePillars: React.FC = () => {
))}
</Box>
</Container>
{/* Hero Section Overlay */}
<WorkflowHeroSection
onStartWorkflow={handleStartWorkflow}
isWorkflowActive={isWorkflowActive}
isLoading={workflowLoading}
/>
</Box>
{/* Onboarding Modal */}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
Box,
Container,
@@ -24,6 +24,8 @@ import EmptyState from '../shared/EmptyState';
import ContentLifecyclePillars from './ContentLifecyclePillars';
import AnalyticsInsights from './components/AnalyticsInsights';
import ToolsModal from './components/ToolsModal';
import EnhancedBillingDashboard from '../billing/EnhancedBillingDashboard';
import CompactSidebar from './components/CompactSidebar';
// Shared types and utilities
import { Tool } from '../shared/types';
@@ -41,6 +43,9 @@ const MainDashboard: React.FC = () => {
const theme = useTheme();
const navigate = useNavigate();
// Sidebar state
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
// Zustand store hooks
const {
loading,
@@ -272,7 +277,13 @@ const MainDashboard: React.FC = () => {
},
}}
>
<Container maxWidth="xl" sx={{ position: 'relative', zIndex: 1 }}>
<Container
maxWidth="xl"
sx={{
position: 'relative',
zIndex: 1,
}}
>
<AnimatePresence>
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -302,22 +313,39 @@ const MainDashboard: React.FC = () => {
{/* Content Lifecycle Pillars - First Panel */}
<ContentLifecyclePillars />
{/* Search and Filter */}
<SearchFilter
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
onClearSearch={() => setSearchQuery('')}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
selectedSubCategory={selectedSubCategory}
onSubCategoryChange={setSelectedSubCategory}
toolCategories={toolCategories}
theme={theme}
onCategoryClick={handleCategoryClick}
/>
{/* Side-by-side layout for Areas 2 and 3 */}
<Box sx={{ display: 'flex', gap: 3, mt: 3 }}>
{/* Area 2: Search Tools Sidebar */}
<Box sx={{
width: sidebarCollapsed ? 60 : 280,
transition: 'width 0.3s ease-in-out',
flexShrink: 0
}}>
<CompactSidebar
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
onClearSearch={() => setSearchQuery('')}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
selectedSubCategory={selectedSubCategory}
onSubCategoryChange={setSelectedSubCategory}
toolCategories={toolCategories}
onCategoryClick={handleCategoryClick}
collapsed={sidebarCollapsed}
onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)}
theme={theme}
/>
</Box>
{/* Analytics Insights - Good/Bad/Ugly */}
<AnalyticsInsights />
{/* Area 3: Analytics and Billing */}
<Box sx={{ flex: 1, minWidth: 0 }}>
{/* Analytics Insights - Good/Bad/Ugly */}
<AnalyticsInsights />
{/* Billing & Usage Dashboard */}
<EnhancedBillingDashboard />
</Box>
</Box>
{/* Tools Modal */}
<ToolsModal

View File

@@ -263,7 +263,7 @@ const AnalyticsInsights: React.FC<AnalyticsInsightsProps> = ({ data, onActionCli
};
return (
<Box sx={{ mt: 2, mb: 2.9 }}>
<Box sx={{ mt: 1, mb: 1.5 }}>
<Box sx={{ position: 'relative', overflow: 'hidden' }}>
<Typography
variant="h6"
@@ -277,7 +277,7 @@ const AnalyticsInsights: React.FC<AnalyticsInsightsProps> = ({ data, onActionCli
Today's Analytics Insights
</Typography>
</Box>
<Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5}>
<Stack direction={{ xs: 'column', md: 'row' }} spacing={1}>
{columns.map((col) => {
const isHovered = hovered === col.key;
const visibleItems = isHovered ? col.items : col.items.slice(0, 1);
@@ -291,16 +291,16 @@ const AnalyticsInsights: React.FC<AnalyticsInsightsProps> = ({ data, onActionCli
<Badge>{col.items.length}</Badge>
</GradientHeader>
<CardContent sx={{ p: 1.5 }}>
<Stack spacing={1}>
<CardContent sx={{ p: 1, '&:last-child': { pb: 1 } }}>
<Stack spacing={0.5}>
{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
p: 0.8
}}>
<Stack direction="row" spacing={0.5} alignItems="center" sx={{ mb: 0.25 }}>
<Stack direction="row" spacing={0.5} alignItems="center" sx={{ mb: 0.1 }}>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.95)', fontWeight: 700, fontSize: '0.8rem' }}>
{insight.title}
</Typography>

View File

@@ -0,0 +1,685 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
Box,
Paper,
Typography,
Chip,
IconButton,
Tooltip,
Divider,
} from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import {
Search,
Filter,
Settings,
ChevronLeft,
ChevronRight,
Activity,
Zap
} from 'lucide-react';
// Shared components
import SearchFilter from '../../shared/SearchFilter';
// Types
import { ToolCategories } from '../../shared/types';
interface CompactSidebarProps {
searchQuery: string;
onSearchChange: (query: string) => void;
onClearSearch: () => void;
selectedCategory: string | null;
onCategoryChange: (category: string | null) => void;
selectedSubCategory: string | null;
onSubCategoryChange: (subCategory: string | null) => void;
toolCategories: ToolCategories;
onCategoryClick: (categoryName: string | null, categoryData?: any) => void;
collapsed: boolean;
onToggleCollapse: () => void;
theme: any;
}
// Session control for animation
const SIDEBAR_ANIMATION_KEY = 'sidebar_animation_shown';
const ANIMATION_COOLDOWN = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const shouldShowAnimation = (): boolean => {
const lastShown = localStorage.getItem(SIDEBAR_ANIMATION_KEY);
if (!lastShown) return true;
const lastShownTime = parseInt(lastShown, 10);
const now = Date.now();
return (now - lastShownTime) > ANIMATION_COOLDOWN;
};
const markAnimationShown = (): void => {
localStorage.setItem(SIDEBAR_ANIMATION_KEY, Date.now().toString());
};
const CompactSidebar: React.FC<CompactSidebarProps> = ({
searchQuery,
onSearchChange,
onClearSearch,
selectedCategory,
onCategoryChange,
selectedSubCategory,
onSubCategoryChange,
toolCategories,
onCategoryClick,
collapsed,
onToggleCollapse,
theme
}) => {
const [isAnimating, setIsAnimating] = useState(false);
const [rippleIndex, setRippleIndex] = useState(-1);
const [shouldAutoExpand, setShouldAutoExpand] = useState(false);
const [userHasInteracted, setUserHasInteracted] = useState(false);
// Calculate total tools count
const totalTools = Object.values(toolCategories).reduce((sum, category) => {
if ('tools' in category) {
return sum + category.tools.length;
} else if ('subCategories' in category) {
return sum + Object.values(category.subCategories).reduce((subSum, subCat) => subSum + subCat.tools.length, 0);
}
return sum;
}, 0);
// Ripple effect for chips
const startRippleEffect = useCallback(() => {
const categoryEntries = Object.entries(toolCategories).slice(0, 5);
categoryEntries.forEach((_, index) => {
setTimeout(() => {
setRippleIndex(index);
// Reset ripple after animation
setTimeout(() => setRippleIndex(-1), 1000);
}, index * 200); // 200ms delay between each chip
});
}, [toolCategories]);
// Check if we should show the animation on mount (only once)
useEffect(() => {
if (shouldShowAnimation() && collapsed && !userHasInteracted) {
setShouldAutoExpand(true);
setIsAnimating(true);
markAnimationShown();
}
}, []); // Empty dependency array - only run once on mount
// Handle auto-expand animation
useEffect(() => {
if (shouldAutoExpand && collapsed && !userHasInteracted) {
// Auto-expand after a short delay
const expandTimer = setTimeout(() => {
onToggleCollapse(); // Expand the sidebar
}, 500);
// Start ripple effect after sidebar is expanded
const rippleTimer = setTimeout(() => {
startRippleEffect();
}, 1000);
// Auto-collapse after 2 seconds
const collapseTimer = setTimeout(() => {
onToggleCollapse(); // Collapse the sidebar
setIsAnimating(false);
setShouldAutoExpand(false);
}, 3000);
return () => {
clearTimeout(expandTimer);
clearTimeout(rippleTimer);
clearTimeout(collapseTimer);
};
}
}, [shouldAutoExpand, collapsed, onToggleCollapse, startRippleEffect, userHasInteracted]);
return (
<motion.div
initial={{ x: -300, opacity: 0 }}
animate={{
x: 0,
opacity: 1,
...(isAnimating && {
scale: [1, 1.02, 1],
boxShadow: [
'0 8px 32px rgba(0,0,0,0.1)',
'0 12px 40px rgba(74, 222, 128, 0.2)',
'0 8px 32px rgba(0,0,0,0.1)'
]
})
}}
transition={{
duration: 0.3,
...(isAnimating && {
scale: { duration: 2, ease: 'easeInOut' },
boxShadow: { duration: 2, ease: 'easeInOut' }
})
}}
>
<Paper
elevation={0}
sx={{
width: '100%',
height: 'fit-content',
minHeight: '400px',
background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 3,
overflow: 'hidden',
boxShadow: '0 8px 32px rgba(0,0,0,0.1)',
}}
>
{/* Header */}
<Box
sx={{
p: collapsed ? 1 : 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
borderBottom: '1px solid rgba(255,255,255,0.1)',
minHeight: 56,
background: 'linear-gradient(90deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
position: 'relative',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.2) 50%, transparent 100%)',
}
}}
>
{/* Animation indicator */}
{isAnimating && !collapsed && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
style={{
position: 'absolute',
top: -8,
right: -8,
width: 16,
height: 16,
borderRadius: '50%',
background: 'linear-gradient(45deg, #4ade80, #22c55e)',
boxShadow: '0 0 10px rgba(74, 222, 128, 0.6)',
zIndex: 10
}}
>
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 2, repeat: Infinity, ease: 'linear' }}
style={{
width: '100%',
height: '100%',
borderRadius: '50%',
border: '2px solid transparent',
borderTop: '2px solid rgba(255,255,255,0.8)',
}}
/>
</motion.div>
)}
{!collapsed && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Box sx={{
width: 8,
height: 8,
borderRadius: '50%',
background: 'linear-gradient(45deg, #4ade80, #22c55e)',
boxShadow: '0 0 8px rgba(74, 222, 128, 0.4)'
}} />
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
Tools
</Typography>
</Box>
)}
<Tooltip title={collapsed ? "Expand sidebar" : "Collapse sidebar"}>
<IconButton
size="small"
onClick={() => {
setUserHasInteracted(true);
onToggleCollapse();
}}
sx={{
color: 'rgba(255,255,255,0.7)',
backgroundColor: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.1)',
'&:hover': {
color: '#ffffff',
backgroundColor: 'rgba(255,255,255,0.1)',
transform: 'scale(1.05)'
},
transition: 'all 0.2s ease-in-out'
}}
>
{collapsed ? <ChevronRight size={16} /> : <ChevronLeft size={16} />}
</IconButton>
</Tooltip>
</Box>
{/* Content */}
<Box sx={{ p: collapsed ? 1 : 2, height: 'calc(100% - 56px)', overflow: 'auto' }}>
{!collapsed ? (
<>
{/* Search Section */}
<Box sx={{
mb: 2,
p: 2,
backgroundColor: 'rgba(255,255,255,0.03)',
borderRadius: 2,
border: '1px solid rgba(255,255,255,0.05)',
position: 'relative',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.1) 50%, transparent 100%)',
}
}}>
<Typography variant="subtitle2" sx={{
mb: 1,
color: 'rgba(255,255,255,0.9)',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<Search size={16} color="rgba(255,255,255,0.7)" />
Search Tools
</Typography>
<SearchFilter
searchQuery={searchQuery}
onSearchChange={onSearchChange}
onClearSearch={onClearSearch}
selectedCategory={selectedCategory}
onCategoryChange={onCategoryChange}
selectedSubCategory={selectedSubCategory}
onSubCategoryChange={onSubCategoryChange}
toolCategories={toolCategories}
theme={theme}
onCategoryClick={onCategoryClick}
compact={true}
/>
</Box>
<Divider sx={{ my: 2, borderColor: 'rgba(255,255,255,0.1)' }} />
{/* Quick Stats */}
<Box sx={{
mb: 2,
p: 2,
backgroundColor: 'rgba(255,255,255,0.03)',
borderRadius: 2,
border: '1px solid rgba(255,255,255,0.05)',
position: 'relative',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.1) 50%, transparent 100%)',
}
}}>
<Typography variant="subtitle2" sx={{
mb: 1,
color: 'rgba(255,255,255,0.9)',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<Activity size={16} color="rgba(255,255,255,0.7)" />
Quick Stats
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Total Tools
</Typography>
<Chip
label={totalTools}
size="small"
sx={{
backgroundColor: 'rgba(74, 222, 128, 0.2)',
color: '#4ade80',
border: '1px solid rgba(74, 222, 128, 0.3)'
}}
/>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Categories
</Typography>
<Chip
label={Object.keys(toolCategories).length}
size="small"
sx={{
backgroundColor: 'rgba(59, 130, 246, 0.2)',
color: '#3b82f6',
border: '1px solid rgba(59, 130, 246, 0.3)'
}}
/>
</Box>
</Box>
</Box>
<Divider sx={{ my: 2, borderColor: 'rgba(255,255,255,0.1)' }} />
{/* Category Quick Access */}
<Box sx={{
p: 2,
backgroundColor: 'rgba(255,255,255,0.03)',
borderRadius: 2,
border: '1px solid rgba(255,255,255,0.05)',
position: 'relative',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.1) 50%, transparent 100%)',
}
}}>
<Typography variant="subtitle2" sx={{
mb: 1,
color: 'rgba(255,255,255,0.9)',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<Zap size={16} color="rgba(255,255,255,0.7)" />
Quick Access
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{Object.entries(toolCategories).slice(0, 5).map(([categoryId, category], index) => {
const toolCount = 'tools' in category
? category.tools.length
: Object.values(category.subCategories).reduce((sum, subCat) => sum + subCat.tools.length, 0);
const isRippling = rippleIndex === index;
return (
<Tooltip key={categoryId} title={`${categoryId} (${toolCount} tools)`}>
<motion.div
animate={isRippling ? {
scale: [1, 1.05, 1],
boxShadow: [
'0 0 0 rgba(74, 222, 128, 0)',
'0 0 20px rgba(74, 222, 128, 0.6)',
'0 0 0 rgba(74, 222, 128, 0)'
]
} : {}}
transition={{ duration: 1, ease: 'easeInOut' }}
>
<Chip
label={`${categoryId} (${toolCount})`}
size="small"
onClick={() => {
setUserHasInteracted(true);
onCategoryClick(categoryId, category);
}}
sx={{
backgroundColor: selectedCategory === categoryId
? 'rgba(74, 222, 128, 0.2)'
: isRippling
? 'rgba(74, 222, 128, 0.15)'
: 'rgba(255,255,255,0.05)',
color: selectedCategory === categoryId || isRippling ? '#4ade80' : '#ffffff',
border: selectedCategory === categoryId
? '1px solid rgba(74, 222, 128, 0.3)'
: isRippling
? '1px solid rgba(74, 222, 128, 0.4)'
: '1px solid rgba(255,255,255,0.1)',
cursor: 'pointer',
transition: 'all 0.2s ease-in-out',
position: 'relative',
overflow: 'hidden',
'&::before': isRippling ? {
content: '""',
position: 'absolute',
top: 0,
left: '-100%',
width: '100%',
height: '100%',
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent)',
animation: 'shimmer 1s ease-in-out',
'@keyframes shimmer': {
'0%': { left: '-100%' },
'100%': { left: '100%' }
}
} : {},
'&:hover': {
backgroundColor: selectedCategory === categoryId
? 'rgba(74, 222, 128, 0.3)'
: 'rgba(255,255,255,0.15)',
transform: 'translateY(-1px)',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
}
}}
/>
</motion.div>
</Tooltip>
);
})}
</Box>
</Box>
</>
) : (
/* Collapsed State - Enhanced Icons with Depth */
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, pt: 2 }}>
<Tooltip title="Search Tools">
<motion.div
whileHover={{ scale: 1.1, y: -2 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<IconButton
size="small"
sx={{
color: searchQuery ? '#4ade80' : 'rgba(255,255,255,0.8)',
backgroundColor: searchQuery
? 'rgba(74, 222, 128, 0.15)'
: 'rgba(255,255,255,0.08)',
border: searchQuery
? '2px solid rgba(74, 222, 128, 0.4)'
: '2px solid rgba(255,255,255,0.15)',
borderRadius: '12px',
width: 40,
height: 40,
boxShadow: searchQuery
? '0 8px 25px rgba(74, 222, 128, 0.3), 0 0 20px rgba(74, 222, 128, 0.2), inset 0 1px 0 rgba(255,255,255,0.2)'
: '0 6px 20px rgba(0,0,0,0.15), 0 0 15px rgba(255,255,255,0.1), inset 0 1px 0 rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: searchQuery
? 'linear-gradient(135deg, rgba(74, 222, 128, 0.1) 0%, rgba(34, 197, 94, 0.1) 100%)'
: 'linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
borderRadius: '10px',
zIndex: -1
},
'&:hover': {
color: '#ffffff',
backgroundColor: searchQuery
? 'rgba(74, 222, 128, 0.25)'
: 'rgba(255,255,255,0.15)',
boxShadow: searchQuery
? '0 12px 35px rgba(74, 222, 128, 0.4), 0 0 30px rgba(74, 222, 128, 0.3), inset 0 1px 0 rgba(255,255,255,0.3)'
: '0 10px 30px rgba(0,0,0,0.2), 0 0 25px rgba(255,255,255,0.15), inset 0 1px 0 rgba(255,255,255,0.2)',
transform: 'translateY(-2px)'
}
}}
>
<Search size={18} />
</IconButton>
</motion.div>
</Tooltip>
<Tooltip title="Filter Categories">
<motion.div
whileHover={{ scale: 1.1, y: -2 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<IconButton
size="small"
sx={{
color: selectedCategory ? '#3b82f6' : 'rgba(255,255,255,0.8)',
backgroundColor: selectedCategory
? 'rgba(59, 130, 246, 0.15)'
: 'rgba(255,255,255,0.08)',
border: selectedCategory
? '2px solid rgba(59, 130, 246, 0.4)'
: '2px solid rgba(255,255,255,0.15)',
borderRadius: '12px',
width: 40,
height: 40,
boxShadow: selectedCategory
? '0 8px 25px rgba(59, 130, 246, 0.3), 0 0 20px rgba(59, 130, 246, 0.2), inset 0 1px 0 rgba(255,255,255,0.2)'
: '0 6px 20px rgba(0,0,0,0.15), 0 0 15px rgba(255,255,255,0.1), inset 0 1px 0 rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: selectedCategory
? 'linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.1) 100%)'
: 'linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
borderRadius: '10px',
zIndex: -1
},
'&:hover': {
color: '#ffffff',
backgroundColor: selectedCategory
? 'rgba(59, 130, 246, 0.25)'
: 'rgba(255,255,255,0.15)',
boxShadow: selectedCategory
? '0 12px 35px rgba(59, 130, 246, 0.4), 0 0 30px rgba(59, 130, 246, 0.3), inset 0 1px 0 rgba(255,255,255,0.3)'
: '0 10px 30px rgba(0,0,0,0.2), 0 0 25px rgba(255,255,255,0.15), inset 0 1px 0 rgba(255,255,255,0.2)',
transform: 'translateY(-2px)'
}
}}
>
<Filter size={18} />
</IconButton>
</motion.div>
</Tooltip>
<Divider sx={{ width: '100%', borderColor: 'rgba(255,255,255,0.1)' }} />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
{Object.entries(toolCategories).slice(0, 4).map(([categoryId, category]) => {
const toolCount = 'tools' in category
? category.tools.length
: Object.values(category.subCategories).reduce((sum, subCat) => sum + subCat.tools.length, 0);
const isSelected = selectedCategory === categoryId;
return (
<Tooltip key={categoryId} title={`${categoryId} (${toolCount})`}>
<motion.div
whileHover={{ scale: 1.15, y: -3 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<Box
sx={{
width: 36,
height: 36,
borderRadius: '50%',
backgroundColor: isSelected
? 'rgba(74, 222, 128, 0.2)'
: 'rgba(255,255,255,0.08)',
border: isSelected
? '2px solid rgba(74, 222, 128, 0.5)'
: '2px solid rgba(255,255,255,0.2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
boxShadow: isSelected
? '0 8px 25px rgba(74, 222, 128, 0.3), 0 0 20px rgba(74, 222, 128, 0.2), inset 0 1px 0 rgba(255,255,255,0.2)'
: '0 6px 20px rgba(0,0,0,0.15), 0 0 15px rgba(255,255,255,0.1), inset 0 1px 0 rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: isSelected
? 'linear-gradient(135deg, rgba(74, 222, 128, 0.1) 0%, rgba(34, 197, 94, 0.1) 100%)'
: 'linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
borderRadius: '50%',
zIndex: -1
},
'&:hover': {
backgroundColor: isSelected
? 'rgba(74, 222, 128, 0.3)'
: 'rgba(255,255,255,0.15)',
boxShadow: isSelected
? '0 12px 35px rgba(74, 222, 128, 0.4), 0 0 30px rgba(74, 222, 128, 0.3), inset 0 1px 0 rgba(255,255,255,0.3)'
: '0 10px 30px rgba(0,0,0,0.2), 0 0 25px rgba(255,255,255,0.15), inset 0 1px 0 rgba(255,255,255,0.2)',
transform: 'translateY(-3px)'
}
}}
onClick={() => {
setUserHasInteracted(true);
onCategoryClick(categoryId, category);
}}
>
<Typography
variant="caption"
sx={{
color: isSelected ? '#4ade80' : '#ffffff',
fontWeight: 'bold',
fontSize: '0.75rem',
textShadow: '0 1px 2px rgba(0,0,0,0.3)'
}}
>
{categoryId.charAt(0).toUpperCase()}
</Typography>
</Box>
</motion.div>
</Tooltip>
);
})}
</Box>
</Box>
)}
</Box>
</Paper>
</motion.div>
);
};
export default CompactSidebar;

View File

@@ -0,0 +1,279 @@
import React from 'react';
import {
Box,
Typography,
Button,
useTheme,
useMediaQuery
} from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import {
PlayArrow,
TrendingUp,
Rocket,
ArrowRight,
Star
} from '@mui/icons-material';
interface WorkflowHeroSectionProps {
onStartWorkflow: () => void;
isWorkflowActive: boolean;
isLoading: boolean;
}
const WorkflowHeroSection: React.FC<WorkflowHeroSectionProps> = ({
onStartWorkflow,
isWorkflowActive,
isLoading
}) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
// Show hero section only when workflow is not started, not in progress, and not completed
const shouldShowHero = !isWorkflowActive;
return (
<AnimatePresence>
{shouldShowHero && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.6, ease: "easeOut" }}
>
{/* Backdrop Overlay - Only over pillars section */}
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.6)',
backdropFilter: 'blur(6px)',
zIndex: 10,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 2, // Match the parent container's border radius
}}
>
{/* Hero Content */}
<Box
sx={{
textAlign: 'center',
maxWidth: isMobile ? '90%' : '500px',
px: 3,
py: 4,
background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)',
backdropFilter: 'blur(20px)',
borderRadius: 3,
border: '1px solid rgba(255,255,255,0.2)',
boxShadow: '0 15px 30px rgba(0,0,0,0.3), 0 0 0 1px rgba(255,255,255,0.1)',
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(45deg, rgba(255,107,53,0.1) 0%, rgba(255,140,66,0.1) 50%, rgba(255,107,53,0.1) 100%)',
backgroundSize: '200% 200%',
animation: 'gradientShift 6s ease-in-out infinite',
zIndex: -1,
},
'@keyframes gradientShift': {
'0%, 100%': { backgroundPosition: '0% 50%' },
'50%': { backgroundPosition: '100% 50%' },
},
}}
>
{/* Floating Sparkles */}
<Box
sx={{
position: 'absolute',
top: 20,
right: 20,
animation: 'float 3s ease-in-out infinite',
'@keyframes float': {
'0%, 100%': { transform: 'translateY(0px)' },
'50%': { transform: 'translateY(-10px)' },
},
}}
>
<Star sx={{ color: 'rgba(255,255,255,0.6)', fontSize: 24 }} />
</Box>
<Box
sx={{
position: 'absolute',
bottom: 20,
left: 20,
animation: 'float 3s ease-in-out infinite 1.5s',
'@keyframes float': {
'0%, 100%': { transform: 'translateY(0px)' },
'50%': { transform: 'translateY(-10px)' },
},
}}
>
<TrendingUp sx={{ color: 'rgba(255,255,255,0.6)', fontSize: 24 }} />
</Box>
{/* Main Content */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
>
{/* Icon */}
<Box sx={{ mb: 2 }}>
<motion.div
animate={{
rotate: [0, 5, -5, 0],
scale: [1, 1.1, 1]
}}
transition={{
duration: 2,
repeat: Infinity,
repeatType: "reverse"
}}
>
<Rocket
sx={{
fontSize: isMobile ? 40 : 48,
color: '#FF6B35',
filter: 'drop-shadow(0 4px 8px rgba(255,107,53,0.3))'
}}
/>
</motion.div>
</Box>
{/* Main Heading */}
<Typography
variant={isMobile ? "h5" : "h4"}
sx={{
fontWeight: 800,
color: '#ffffff',
mb: 1.5,
textShadow: '0 4px 8px rgba(0,0,0,0.3)',
background: 'linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%)',
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
Grow Your Business Now
</Typography>
{/* Supporting Text */}
<Typography
variant={isMobile ? "body2" : "body1"}
sx={{
color: 'rgba(255,255,255,0.9)',
mb: 3,
lineHeight: 1.5,
maxWidth: '400px',
mx: 'auto',
textShadow: '0 2px 4px rgba(0,0,0,0.3)',
}}
>
Start your personalized content workflow and watch your digital marketing transform.
Our AI-powered system will guide you through every step of your content journey.
</Typography>
{/* CTA Button */}
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Button
variant="contained"
size="large"
startIcon={<PlayArrow />}
endIcon={<ArrowRight />}
onClick={onStartWorkflow}
disabled={isLoading}
sx={{
background: 'linear-gradient(135deg, #FF6B35 0%, #E55A2B 100%)',
border: '2px solid transparent',
borderRadius: 2,
px: 4,
py: 1.5,
fontSize: isMobile ? '0.9rem' : '1rem',
fontWeight: 700,
textTransform: 'none',
boxShadow: '0 6px 24px rgba(255,107,53,0.4), 0 0 0 1px rgba(255,255,255,0.2)',
position: 'relative',
overflow: 'hidden',
'&:hover': {
background: 'linear-gradient(135deg, #E55A2B 0%, #D1491F 100%)',
boxShadow: '0 12px 40px rgba(255,107,53,0.6), 0 0 0 1px rgba(255,255,255,0.3)',
transform: 'translateY(-2px)',
},
'&:disabled': {
background: 'rgba(255,255,255,0.1)',
color: 'rgba(255,255,255,0.5)',
},
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: '-100%',
width: '100%',
height: '100%',
background: 'linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent)',
animation: 'shimmer 2.5s infinite',
zIndex: 1,
},
'&::after': {
content: '""',
position: 'absolute',
top: -4,
left: -4,
right: -4,
bottom: -4,
background: 'linear-gradient(45deg, #FF6B35, #FF8C42, #FF6B35, #FF8C42)',
backgroundSize: '400% 400%',
borderRadius: 'inherit',
zIndex: -1,
animation: 'borderGlow 3s ease-in-out infinite',
},
'@keyframes shimmer': {
'0%': { left: '-100%' },
'100%': { left: '100%' },
},
'@keyframes borderGlow': {
'0%, 100%': { backgroundPosition: '0% 50%' },
'50%': { backgroundPosition: '100% 50%' },
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
>
{isLoading ? 'Starting...' : '🚀 Start Your Journey'}
</Button>
</motion.div>
{/* Additional Info */}
<Typography
variant="caption"
sx={{
color: 'rgba(255,255,255,0.7)',
mt: 2,
display: 'block',
textShadow: '0 1px 2px rgba(0,0,0,0.3)',
}}
>
Personalized workflow 🎯 AI-powered guidance 📈 Business growth
</Typography>
</motion.div>
</Box>
</Box>
</motion.div>
)}
</AnimatePresence>
);
};
export default WorkflowHeroSection;