Alwrity today's tasks workflow implementation plan.

This commit is contained in:
ajaysi
2025-09-06 15:28:05 +05:30
parent f82ada0361
commit ae42720c2a
25 changed files with 7836 additions and 46 deletions

View File

@@ -0,0 +1,517 @@
import React, { useState, useEffect } from 'react';
import {
Box,
Container,
Typography,
Card,
CardContent,
useTheme,
useMediaQuery,
Chip,
Tooltip,
Paper,
Modal,
Button,
IconButton,
Divider,
LinearProgress,
Avatar,
Stack
} from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import {
Close as CloseIcon,
Settings as SettingsIcon,
CheckCircle as CheckIcon,
RadioButtonUnchecked as UncheckedIcon,
TrendingUp as TrendingUpIcon
} from '@mui/icons-material';
import GeneratePillarChips from './components/GeneratePillarChips';
import PublishPillarChips from './components/PublishPillarChips';
import AnalyzePillarChips from './components/AnalyzePillarChips';
import EngagePillarChips from './components/EngagePillarChips';
import EnhancedTodayChip from './components/EnhancedTodayChip';
import OnboardingModal from './components/OnboardingModal';
import { pillarData } from './components/PillarData';
import { useWorkflowStore } from '../../stores/workflowStore';
// Enhanced Glassomorphic Chip Component with Popping Effects
const ChipWithTooltip: React.FC<{
chip: any;
delay?: number;
onOnboardingClick?: () => void;
}> = ({ chip, delay = 0, onOnboardingClick }) => {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % chip.bubbles.length);
}, 2000 + delay * 300);
return () => clearInterval(interval);
}, [chip.bubbles.length, delay]);
const IconComponent = chip.icon;
const handleClick = () => {
if (chip.label === 'On-Boarding' && onOnboardingClick) {
onOnboardingClick();
}
};
return (
<Tooltip
title={
<Box>
<Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
{chip.label}
</Typography>
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<Typography variant="caption" sx={{ color: 'white' }}>
{chip.bubbles[currentIndex]}
</Typography>
</motion.div>
</AnimatePresence>
</Box>
}
arrow
placement="top"
>
<Box
sx={{
position: 'relative',
cursor: 'pointer',
transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
'&:hover': {
transform: 'translateY(-4px) scale(1.05)',
'& .chip-glow': {
opacity: 1,
transform: 'scale(1.2)'
},
'& .chip-shadow': {
opacity: 0.6,
transform: 'translateY(8px) scale(1.1)'
}
}
}}
onClick={handleClick}
>
{/* Glow Effect */}
<Box
className="chip-glow"
sx={{
position: 'absolute',
top: -8,
left: -8,
right: -8,
bottom: -8,
background: chip.gradient || chip.color,
borderRadius: '20px',
opacity: 0,
transition: 'all 0.4s ease',
filter: 'blur(12px)',
zIndex: -2
}}
/>
{/* Shadow Effect */}
<Box
className="chip-shadow"
sx={{
position: 'absolute',
top: 4,
left: 2,
right: -2,
bottom: -4,
background: 'rgba(0,0,0,0.3)',
borderRadius: '16px',
opacity: 0.3,
transition: 'all 0.4s ease',
filter: 'blur(8px)',
zIndex: -1
}}
/>
{/* Main Chip */}
<Chip
icon={<IconComponent sx={{ fontSize: 14 }} />}
label={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<Typography variant="caption" sx={{ fontWeight: 600, fontSize: '0.7rem' }}>
{chip.label}
</Typography>
{chip.value && (
<Box
sx={{
backgroundColor: 'rgba(255,255,255,0.9)',
color: chip.color,
borderRadius: '50%',
width: 16,
height: 16,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '0.6rem',
fontWeight: 700,
boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
}}
>
{chip.value}
</Box>
)}
</Box>
}
size="small"
sx={{
background: `linear-gradient(135deg,
rgba(255,255,255,0.25) 0%,
rgba(255,255,255,0.1) 50%,
rgba(255,255,255,0.05) 100%)`,
backdropFilter: 'blur(20px)',
border: '1px solid rgba(255,255,255,0.3)',
color: 'white',
fontSize: '0.7rem',
fontWeight: 600,
height: 28,
minWidth: 100,
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: '-100%',
width: '100%',
height: '100%',
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent)',
transition: 'left 0.6s ease',
zIndex: 1
},
'&:hover::before': {
left: '100%'
},
'& .MuiChip-label': {
px: 1,
zIndex: 2,
position: 'relative'
},
'& .MuiChip-icon': {
zIndex: 2,
position: 'relative'
},
'&:hover': {
background: `linear-gradient(135deg,
rgba(255,255,255,0.35) 0%,
rgba(255,255,255,0.2) 50%,
rgba(255,255,255,0.1) 100%)`,
border: '1px solid rgba(255,255,255,0.5)',
boxShadow: `0 8px 32px ${chip.color}40,
0 4px 16px rgba(0,0,0,0.1),
inset 0 1px 0 rgba(255,255,255,0.3)`
}
}}
/>
</Box>
</Tooltip>
);
};
// Enhanced Pillar Component with Progressive Disclosure
const PillarCard: React.FC<{
pillar: typeof pillarData[0];
index: number;
onOnboardingClick?: () => void;
}> = ({ pillar, index, onOnboardingClick }) => {
const IconComponent = pillar.icon;
const [isHovered, setIsHovered] = useState(false);
const { currentWorkflow } = useWorkflowStore();
// Use live workflow tasks if available
const liveTasksForPillar = (currentWorkflow?.tasks && currentWorkflow.tasks.length > 0
? currentWorkflow.tasks
: pillar.todayTasks || []).filter((t: any) => t.pillarId === pillar.id);
const totalForPillar = liveTasksForPillar.length;
const doneForPillar = liveTasksForPillar.filter((t: any) => t.status === 'completed' || t.status === 'skipped').length;
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: index * 0.1 }}
whileHover={{ y: -5, scale: 1.02 }}
>
<Paper
elevation={8}
sx={{
height: isHovered ? 280 : 120, // Dynamic height based on hover state
background: pillar.gradient,
color: 'white',
cursor: 'pointer',
transition: 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)',
position: 'relative',
overflow: 'hidden',
// Large tick when pillar tasks complete (uses live store counts)
'&::after': {
content: doneForPillar > 0 && doneForPillar === totalForPillar ? '"✓"' : '""',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
fontSize: '64px',
color: 'rgba(255,255,255,0.9)',
textShadow: '0 4px 12px rgba(0,0,0,0.5)',
pointerEvents: 'none',
zIndex: 10, // Ensure tick is above all content
fontWeight: 'bold'
},
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(45deg, rgba(255,255,255,0.1) 0%, transparent 50%)',
opacity: isHovered ? 1 : 0,
transition: 'opacity 0.3s ease'
},
'&:hover': {
boxShadow: `0 12px 24px ${pillar.color}40`
}
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<CardContent sx={{ p: 2, height: '100%', display: 'flex', flexDirection: 'column' }}>
{/* Header */}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1.5, position: 'relative' }}>
<Box
sx={{
p: 0.8,
borderRadius: '50%',
backgroundColor: 'rgba(255,255,255,0.2)',
mr: 1.2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<IconComponent sx={{ fontSize: 18, color: 'white' }} />
</Box>
<Typography variant="h6" sx={{ fontWeight: 700, fontSize: '1rem' }}>
{pillar.title}
</Typography>
{/* Pillar task count badge */}
<Box sx={{ ml: 1, position: 'relative' }}>
<Box
sx={{
backgroundColor: 'rgba(255,255,255,0.9)',
color: pillar.color,
borderRadius: '12px',
px: 0.75,
py: 0.1,
fontSize: '0.65rem',
fontWeight: 800,
boxShadow: '0 2px 6px rgba(0,0,0,0.2)'
}}
>
{totalForPillar}
</Box>
</Box>
{/* More Options Indicator */}
{!isHovered && (
<motion.div
animate={{ opacity: [0.5, 1, 0.5] }}
transition={{ duration: 2, repeat: Infinity, ease: 'easeInOut' }}
style={{ marginLeft: 'auto' }}
>
<Typography variant="caption" sx={{ fontSize: '0.6rem', opacity: 0.7 }}>
</Typography>
</motion.div>
)}
</Box>
{/* Chips Layout with Progressive Disclosure */}
{pillar.id === 'generate' ? (
<GeneratePillarChips index={index} isHovered={isHovered} />
) : pillar.id === 'publish' ? (
<PublishPillarChips isHovered={isHovered} pillarColor={pillar.color} />
) : pillar.id === 'analyze' ? (
<AnalyzePillarChips isHovered={isHovered} pillarColor={pillar.color} />
) : pillar.id === 'engage' ? (
<EngagePillarChips isHovered={isHovered} pillarColor={pillar.color} />
) : (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: 1,
flexGrow: 1,
justifyContent: isHovered ? 'center' : 'flex-start'
}}
>
{/* Today Chip - Always Visible */}
<EnhancedTodayChip
pillarId={pillar.id}
pillarTitle={pillar.title}
pillarColor={pillar.color}
tasks={pillar.todayTasks}
delay={index * 5}
/>
{/* Additional Chips - Progressive Disclosure */}
<AnimatePresence>
{isHovered && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3, ease: 'easeInOut' }}
style={{ overflow: 'hidden' }}
>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 1 }}>
{pillar.id === 'plan' ? (
<>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<ChipWithTooltip chip={pillar.chips.onboarding} delay={index * 5 + 1} onOnboardingClick={onOnboardingClick} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<ChipWithTooltip chip={pillar.chips.strategy} delay={index * 5 + 2} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.3 }}
>
<ChipWithTooltip chip={pillar.chips.calendar} delay={index * 5 + 3} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<ChipWithTooltip chip={pillar.chips.review} delay={index * 5 + 4} />
</motion.div>
</>
) : pillar.id === 'remarket' ? (
<>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<ChipWithTooltip chip={pillar.chips.good} delay={index * 5 + 1} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<ChipWithTooltip chip={pillar.chips.bad} delay={index * 5 + 2} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.3 }}
>
<ChipWithTooltip chip={pillar.chips.ugly} delay={index * 5 + 3} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<ChipWithTooltip chip={pillar.chips.review} delay={index * 5 + 4} />
</motion.div>
</>
) : null}
</Box>
</motion.div>
)}
</AnimatePresence>
</Box>
)}
</CardContent>
</Paper>
</motion.div>
);
};
// Main Content Lifecycle Pillars Component
const ContentLifecyclePillars: React.FC = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [onboardingModalOpen, setOnboardingModalOpen] = useState(false);
const handleOnboardingClick = () => {
setOnboardingModalOpen(true);
};
const handleCloseModal = () => {
setOnboardingModalOpen(false);
};
return (
<>
<Box
sx={{
py: 3,
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
}}
>
<Container maxWidth="xl">
{/* Pillars Grid */}
<Box
sx={{
display: 'grid',
gridTemplateColumns: {
xs: 'repeat(2, 1fr)',
sm: 'repeat(3, 1fr)',
md: 'repeat(6, 1fr)'
},
gap: 2,
overflow: 'visible'
}}
>
{pillarData.map((pillar, index) => (
<PillarCard
key={pillar.id}
pillar={pillar}
index={index}
onOnboardingClick={handleOnboardingClick}
/>
))}
</Box>
</Container>
</Box>
{/* Onboarding Modal */}
<OnboardingModal
open={onboardingModalOpen}
onClose={handleCloseModal}
/>
</>
);
};
export default ContentLifecyclePillars;

View File

@@ -10,6 +10,7 @@ import {
} from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import AskAlwrityIcon from '../../assets/images/AskAlwrity-min.ico';
// Shared components
import DashboardHeader from '../shared/DashboardHeader';
@@ -20,13 +21,15 @@ import CategoryHeader from '../shared/CategoryHeader';
import LoadingSkeleton from '../shared/LoadingSkeleton';
import ErrorDisplay from '../shared/ErrorDisplay';
import EmptyState from '../shared/EmptyState';
import ContentLifecyclePillars from './ContentLifecyclePillars';
// Shared types and utilities
import { Tool, Category } from '../shared/types';
import { Tool } from '../shared/types';
import { getFilteredCategories, getToolsForCategory } from '../shared/utils';
// Zustand store
// Zustand stores
import { useDashboardStore } from '../../stores/dashboardStore';
import { useWorkflowStore } from '../../stores/workflowStore';
// Data
import { toolCategories } from '../../data/toolCategories';
@@ -34,7 +37,6 @@ import { toolCategories } from '../../data/toolCategories';
// Main dashboard component
const MainDashboard: React.FC = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const navigate = useNavigate();
// Zustand store hooks
@@ -50,13 +52,114 @@ const MainDashboard: React.FC = () => {
setSearchQuery,
setSelectedCategory,
setSelectedSubCategory,
setError,
setLoading,
showSnackbar,
hideSnackbar,
clearFilters,
} = useDashboardStore();
// Workflow store hooks
const {
currentWorkflow,
workflowProgress,
isLoading: workflowLoading,
generateDailyWorkflow,
startWorkflow,
pauseWorkflow,
stopWorkflow
} = useWorkflowStore();
// Initialize workflow on component mount
React.useEffect(() => {
const initializeWorkflow = async () => {
try {
// Generate daily workflow for current user
// In a real app, you'd get the actual user ID from auth context
const userId = 'demo-user'; // Replace with actual user ID
await generateDailyWorkflow(userId);
} catch (error) {
console.warn('Failed to initialize workflow:', error);
}
};
initializeWorkflow();
}, [generateDailyWorkflow]);
// Debug logging for workflow state
React.useEffect(() => {
console.log('Workflow Debug:', {
currentWorkflow,
workflowProgress,
isWorkflowActive: currentWorkflow?.workflowStatus === 'in_progress',
workflowStatus: currentWorkflow?.workflowStatus,
hasWorkflow: !!currentWorkflow
});
}, [currentWorkflow, workflowProgress]);
// State to track if we need to start a newly generated workflow
const [shouldStartWorkflow, setShouldStartWorkflow] = React.useState(false);
// Handle workflow start
const handleStartWorkflow = async () => {
try {
if (currentWorkflow) {
await startWorkflow(currentWorkflow.id);
} else {
// Generate workflow first, then mark that we should start it
await generateDailyWorkflow('demo-user');
setShouldStartWorkflow(true);
}
} catch (error) {
console.error('Failed to start workflow:', error);
}
};
// Auto-start workflow after generation
React.useEffect(() => {
if (shouldStartWorkflow && currentWorkflow && currentWorkflow.workflowStatus === 'not_started') {
const startGeneratedWorkflow = async () => {
try {
await startWorkflow(currentWorkflow.id);
setShouldStartWorkflow(false);
} catch (error) {
console.error('Failed to start generated workflow:', error);
setShouldStartWorkflow(false);
}
};
startGeneratedWorkflow();
}
}, [shouldStartWorkflow, currentWorkflow, startWorkflow]);
// Handle workflow pause
const handlePauseWorkflow = async () => {
if (currentWorkflow) {
try {
await pauseWorkflow(currentWorkflow.id);
} catch (error) {
console.error('Failed to pause workflow:', error);
}
}
};
// Handle workflow stop
const handleStopWorkflow = async () => {
if (currentWorkflow) {
try {
await stopWorkflow(currentWorkflow.id);
} catch (error) {
console.error('Failed to stop workflow:', error);
}
}
};
// Resume Plan modal from header In-Progress button
const handleResumePlanModal = () => {
// Programmatically click the Plan pillar Today chip
const planChip = document.querySelector('[data-pillar-id="plan"]');
if (planChip) {
(planChip as HTMLElement).click();
}
};
const handleToolClick = (tool: Tool) => {
console.log('Navigating to tool:', tool.path);
if (tool.path) {
@@ -120,12 +223,27 @@ const MainDashboard: React.FC = () => {
>
{/* Dashboard Header */}
<DashboardHeader
title="🚀 Alwrity Content Hub"
subtitle="Your AI-powered content creation suite"
title="Alwrity Content Hub"
subtitle=""
statusChips={[]}
rightContent={<SystemStatusIndicator />}
customIcon={AskAlwrityIcon}
workflowControls={{
onStartWorkflow: handleStartWorkflow,
onPauseWorkflow: handlePauseWorkflow,
onStopWorkflow: handleStopWorkflow,
onResumePlanModal: handleResumePlanModal,
isWorkflowActive: currentWorkflow?.workflowStatus === 'in_progress',
completedTasks: workflowProgress?.completedTasks || 0,
totalTasks: workflowProgress?.totalTasks || 0,
isLoading: workflowLoading
}}
/>
{/* Content Lifecycle Pillars - First Panel */}
<ContentLifecyclePillars />
{/* Search and Filter */}
<SearchFilter
searchQuery={searchQuery}
@@ -149,12 +267,14 @@ const MainDashboard: React.FC = () => {
transition={{ duration: 0.5, delay: categoryIndex * 0.1 }}
>
<Box sx={{ mb: 5 }}>
{/* Category Header */}
<CategoryHeader
categoryName={categoryName}
category={category}
theme={theme}
/>
{/* Only show Category Header when no specific category is selected (showing all tools) */}
{selectedCategory === null && (
<CategoryHeader
categoryName={categoryName}
category={category}
theme={theme}
/>
)}
<Grid container spacing={3}>
{getToolsForCategory(category, selectedSubCategory).map((tool: Tool, toolIndex: number) => (

View File

@@ -0,0 +1,246 @@
import React from 'react';
import { Box, Chip, useTheme } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import {
Facebook,
LinkedIn,
Twitter,
Web,
Analytics,
Dashboard
} from '@mui/icons-material';
import EnhancedTodayChip from './EnhancedTodayChip';
import { TodayTask } from '../../../types/workflow';
interface AnalyzePillarChipsProps {
isHovered: boolean;
pillarColor: string;
}
const AnalyzePillarChips: React.FC<AnalyzePillarChipsProps> = ({
isHovered,
pillarColor
}) => {
const theme = useTheme();
const navigate = useNavigate();
// Today's tasks for Analyze pillar
const todayTasks: TodayTask[] = [
{
id: "analyze-content-performance",
pillarId: "analyze",
title: "Review content performance",
description: "Analyze last week's content engagement metrics",
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/content-planning-dashboard',
icon: Analytics,
color: "#9C27B0",
enabled: true,
action: () => navigate('/content-planning-dashboard')
},
{
id: "analyze-strategy-alignment",
pillarId: "analyze",
title: "Check strategy alignment",
description: "Review content strategy against performance data",
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/content-planning-dashboard',
icon: Dashboard,
color: "#673AB7",
enabled: true,
action: () => navigate('/content-planning-dashboard')
},
{
id: "analyze-update-dashboard",
pillarId: "analyze",
title: "Update analytics dashboard",
description: "Refresh analytics data for all platforms",
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 30,
actionType: 'navigate' as const,
actionUrl: '/analytics-dashboard',
icon: Analytics,
color: "#3F51B5",
enabled: false,
action: () => {}
}
];
const handlePlanDashboardClick = () => {
navigate('/content-planning-dashboard');
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, width: '100%' }}>
{/* Today Chip - Always visible */}
<EnhancedTodayChip
pillarId="analyze"
pillarTitle="Analyze"
pillarColor={pillarColor}
tasks={todayTasks}
delay={0}
/>
{/* Progressive disclosure chips */}
<AnimatePresence>
{isHovered && (
<>
{/* Plan Dashboard Chip */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<Chip
icon={<Dashboard sx={{ fontSize: 16 }} />}
label="Plan Dashboard"
onClick={handlePlanDashboardClick}
sx={{
height: 28,
minWidth: 120,
background: 'linear-gradient(135deg, #9C27B0 0%, #7B1FA2 100%)',
color: 'white',
fontWeight: 600,
fontSize: '0.75rem',
border: '2px solid #9C27B0',
boxShadow: '0 4px 12px rgba(156, 39, 176, 0.3), 0 0 0 1px rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
cursor: 'pointer',
'&:hover': {
transform: 'translateY(-2px) scale(1.05)',
boxShadow: '0 6px 20px rgba(156, 39, 176, 0.4), 0 0 0 1px rgba(255,255,255,0.2)',
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
/>
</motion.div>
{/* Disabled Analytics Chips */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<Chip
icon={<LinkedIn sx={{ fontSize: 16 }} />}
label="LinkedIn Analytics"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(0, 119, 181, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(0, 119, 181, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.3 }}
>
<Chip
icon={<Facebook sx={{ fontSize: 16 }} />}
label="Facebook Analytics"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(24, 119, 242, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(24, 119, 242, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<Chip
icon={<Twitter sx={{ fontSize: 16 }} />}
label="Twitter Analytics"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(29, 161, 242, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(29, 161, 242, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.5 }}
>
<Chip
icon={<Web sx={{ fontSize: 16 }} />}
label="Website Analytics"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(255, 107, 53, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(255, 107, 53, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
</>
)}
</AnimatePresence>
{/* Ellipsis indicator when not hovered */}
{!isHovered && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'rgba(255, 255, 255, 0.6)',
fontSize: '1.2rem',
animation: 'pulse 2s infinite',
'@keyframes pulse': {
'0%, 100%': { opacity: 0.6 },
'50%': { opacity: 1 },
},
}}
>
</Box>
)}
</Box>
);
};
export default AnalyzePillarChips;

View File

@@ -0,0 +1,235 @@
import React from 'react';
import { Box, Chip, useTheme } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import {
Facebook,
LinkedIn,
Twitter,
Forum,
Comment,
Chat,
Groups
} from '@mui/icons-material';
import EnhancedTodayChip from './EnhancedTodayChip';
import { TodayTask } from '../../../types/workflow';
interface EngagePillarChipsProps {
isHovered: boolean;
pillarColor: string;
}
const EngagePillarChips: React.FC<EngagePillarChipsProps> = ({
isHovered,
pillarColor
}) => {
const theme = useTheme();
const navigate = useNavigate();
// Today's tasks for Engage pillar
const todayTasks: TodayTask[] = [
{
id: "engage-blog-comment",
pillarId: "engage",
title: "Reply to blog comment",
description: "Received comment on blog 'AI Persona for Content writing'",
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 10,
actionType: 'navigate' as const,
actionUrl: '/content-planning-dashboard',
icon: Comment,
color: "#E91E63",
enabled: true,
action: () => navigate('/content-planning-dashboard')
},
{
id: "engage-twitter-mention",
pillarId: "engage",
title: "Respond to Twitter mention",
description: "Reply to Twitter comment from @username",
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 5,
actionType: 'navigate' as const,
actionUrl: '/content-planning-dashboard',
icon: Twitter,
color: "#1DA1F2",
enabled: true,
action: () => navigate('/content-planning-dashboard')
},
{
id: "engage-linkedin-post",
pillarId: "engage",
title: "Engage with LinkedIn post",
description: "Respond to comments on latest LinkedIn post",
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/linkedin-engagement',
icon: LinkedIn,
color: "#0077B5",
enabled: false,
action: () => {}
}
];
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, width: '100%' }}>
{/* Today Chip - Always visible */}
<EnhancedTodayChip
pillarId="engage"
pillarTitle="Engage"
pillarColor={pillarColor}
tasks={todayTasks}
delay={0}
/>
{/* Progressive disclosure chips */}
<AnimatePresence>
{isHovered && (
<>
{/* Disabled Engagement Chips */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<Chip
icon={<LinkedIn sx={{ fontSize: 16 }} />}
label="LinkedIn Comments"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(0, 119, 181, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(0, 119, 181, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<Chip
icon={<Facebook sx={{ fontSize: 16 }} />}
label="Facebook Comments"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(24, 119, 242, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(24, 119, 242, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.3 }}
>
<Chip
icon={<Groups sx={{ fontSize: 16 }} />}
label="Community Engagement"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(233, 30, 99, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(233, 30, 99, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<Chip
icon={<Chat sx={{ fontSize: 16 }} />}
label="Live Chat Support"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(76, 175, 80, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(76, 175, 80, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.5 }}
>
<Chip
icon={<Forum sx={{ fontSize: 16 }} />}
label="Forum Discussions"
disabled
sx={{
height: 28,
minWidth: 120,
background: 'rgba(255, 152, 0, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(255, 152, 0, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
</>
)}
</AnimatePresence>
{/* Ellipsis indicator when not hovered */}
{!isHovered && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'rgba(255, 255, 255, 0.6)',
fontSize: '1.2rem',
animation: 'pulse 2s infinite',
'@keyframes pulse': {
'0%, 100%': { opacity: 0.6 },
'50%': { opacity: 1 },
},
}}
>
</Box>
)}
</Box>
);
};
export default EngagePillarChips;

View File

@@ -0,0 +1,233 @@
import React, { useState, useEffect } from 'react';
import {
Box,
Typography,
Chip,
Tooltip
} from '@mui/material';
import { motion } from 'framer-motion';
import {
Today as TodayIcon
} from '@mui/icons-material';
import { useWorkflowStore } from '../../../stores/workflowStore';
import { TodayTask } from '../../../types/workflow';
import EnhancedTodayModal from './EnhancedTodayModal';
interface EnhancedTodayChipProps {
pillarId: string;
pillarTitle: string;
pillarColor: string;
tasks: TodayTask[];
delay?: number;
}
// Enhanced Today Chip Component
const EnhancedTodayChip: React.FC<EnhancedTodayChipProps> = ({
pillarId,
pillarTitle,
pillarColor,
tasks,
delay = 0
}) => {
const [modalOpen, setModalOpen] = useState(false);
const [shouldShake, setShouldShake] = useState(false);
const [userManuallyClosed, setUserManuallyClosed] = useState(false);
const { workflowProgress, navigationState, currentWorkflow } = useWorkflowStore();
// Prefer live workflow tasks (to reflect updated statuses), fallback to props
const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0
? currentWorkflow.tasks
: tasks;
// Get pillar-specific progress
const pillarTasks = liveTasks.filter(task => task.pillarId === pillarId);
const completedPillarTasks = pillarTasks.filter(task => task.status === 'completed' || task.status === 'skipped').length;
const pillarProgress = pillarTasks.length > 0 ? (completedPillarTasks / pillarTasks.length) * 100 : 0;
const isPillarComplete = pillarTasks.length > 0 && completedPillarTasks === pillarTasks.length;
// Auto-shake animation (only when pillar is not complete)
useEffect(() => {
if (isPillarComplete) {
setShouldShake(false); // Stop any ongoing animation
return; // Don't animate if pillar is complete
}
const interval = setInterval(() => {
setShouldShake(true);
setTimeout(() => setShouldShake(false), 600);
}, 8000 + delay * 1000);
return () => clearInterval(interval);
}, [delay, isPillarComplete, liveTasks]);
// Auto-open Plan pillar modal when workflow starts (only if user hasn't manually closed it AND tasks are incomplete)
useEffect(() => {
if (pillarId === 'plan' &&
currentWorkflow?.workflowStatus === 'in_progress' &&
!modalOpen &&
!userManuallyClosed &&
!isPillarComplete) { // Only auto-open if Plan pillar tasks are not complete
// Small delay to ensure smooth transition
const timer = setTimeout(() => {
setModalOpen(true);
}, 500);
return () => clearTimeout(timer);
}
}, [currentWorkflow?.workflowStatus, pillarId, modalOpen, userManuallyClosed, isPillarComplete]);
const handleClick = () => {
setModalOpen(true);
setUserManuallyClosed(false); // Reset the flag when user manually opens
};
const handleCloseModal = () => {
setModalOpen(false);
if (pillarId === 'plan') {
setUserManuallyClosed(true); // Mark that user manually closed the Plan modal
}
};
return (
<>
<motion.div
animate={shouldShake && !isPillarComplete ? { x: [-2, 2, -2, 2, 0] } : {}}
transition={{ duration: 0.6 }}
>
<Tooltip title={`🎯 Today's ${pillarTitle} Tasks - Click to View!`} arrow>
<Box
onClick={handleClick}
data-pillar-id={pillarId}
sx={{
position: 'relative',
cursor: 'pointer',
'&:hover': {
transform: 'translateY(-2px) scale(1.05)',
'&::before': {
opacity: 1,
transform: 'translateX(0)'
}
},
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: `linear-gradient(45deg, transparent 30%, ${pillarColor}20 50%, transparent 70%)`,
opacity: 0,
transform: 'translateX(-100%)',
transition: 'all 0.6s ease',
borderRadius: 'inherit',
zIndex: 1
}
}}
>
<Chip
icon={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<TodayIcon sx={{
fontSize: 16,
'@keyframes rotate': {
from: { transform: 'rotate(0deg)' },
to: { transform: 'rotate(360deg)' }
},
animation: 'rotate 3s linear infinite'
}} />
<motion.span
animate={{ scale: [1, 1.2, 1] }}
transition={{ duration: 1, repeat: Infinity }}
>
</motion.span>
</Box>
}
label="Today"
sx={{
height: 32,
minWidth: 110,
background: `linear-gradient(135deg, ${pillarColor} 0%, ${pillarColor}CC 100%)`,
color: 'white',
fontWeight: 700,
fontSize: '0.75rem',
border: `2px solid ${pillarColor}`,
boxShadow: `
0 4px 12px ${pillarColor}40,
0 0 0 1px rgba(255,255,255,0.1),
inset 0 1px 0 rgba(255,255,255,0.2)
`,
backdropFilter: 'blur(25px)',
position: 'relative',
zIndex: 1, // Lower z-index to not cover the large tick
'&:hover': {
boxShadow: `
0 6px 20px ${pillarColor}60,
0 0 0 1px rgba(255,255,255,0.2),
inset 0 1px 0 rgba(255,255,255,0.3)
`,
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&::after': {
content: '""',
position: 'absolute',
top: -2,
left: -2,
right: -2,
bottom: -2,
background: `linear-gradient(45deg, ${pillarColor}, transparent, ${pillarColor})`,
borderRadius: 'inherit',
zIndex: -1,
'@keyframes attention-ring': {
'0%, 100%': { opacity: 0, transform: 'scale(1)' },
'50%': { opacity: 0.3, transform: 'scale(1.1)' }
},
animation: 'attention-ring 2s ease-in-out infinite'
}
}}
/>
{/* Progress indicator */}
{pillarProgress > 0 && (
<Box
sx={{
position: 'absolute',
top: -4,
right: -4,
width: 16,
height: 16,
borderRadius: '50%',
background: pillarProgress === 100 ? '#4CAF50' : pillarColor,
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '0.6rem',
fontWeight: 700,
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
border: '2px solid white'
}}
>
{pillarProgress === 100 ? '✓' : Math.round(pillarProgress)}
</Box>
)}
</Box>
</Tooltip>
</motion.div>
{/* Enhanced Modal */}
<EnhancedTodayModal
open={modalOpen}
onClose={handleCloseModal}
pillarId={pillarId}
pillarTitle={pillarTitle}
pillarColor={pillarColor}
tasks={liveTasks}
onPreventAutoReopen={() => setUserManuallyClosed(true)}
/>
</>
);
};
export default EnhancedTodayChip;

View File

@@ -0,0 +1,498 @@
import React, { useState } from 'react';
import {
Box,
Typography,
Chip,
Tooltip,
Modal,
Paper,
Button,
IconButton,
Avatar,
Stack,
LinearProgress,
CircularProgress,
Card,
CardContent
} from '@mui/material';
import { motion } from 'framer-motion';
import {
Today as TodayIcon,
Close as CloseIcon,
AutoAwesome as AlwrityIcon,
CheckCircle as CheckIcon,
PlayArrow as PlayIcon,
SkipNext as SkipIcon,
NavigateNext
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
import { useWorkflowStore } from '../../../stores/workflowStore';
import { TodayTask } from '../../../types/workflow';
interface EnhancedTodayModalProps {
open: boolean;
onClose: () => void;
pillarId: string;
pillarTitle: string;
pillarColor: string;
tasks: TodayTask[];
// When navigating away (Next), prevent the previous pillar modal from auto-reopening
onPreventAutoReopen?: () => void;
}
// Enhanced Today Modal with Workflow Integration
const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
open,
onClose,
pillarId,
pillarTitle,
pillarColor,
tasks,
onPreventAutoReopen
}) => {
const navigate = useNavigate();
const {
currentWorkflow,
workflowProgress,
navigationState,
completeTask,
skipTask,
moveToNextTask,
isLoading,
isWorkflowComplete
} = useWorkflowStore();
const [selectedTask, setSelectedTask] = useState<TodayTask | null>(null);
// Prefer live workflow tasks (to reflect updated statuses), fallback to props
const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0
? currentWorkflow.tasks
: tasks;
// Filter tasks for this pillar
const pillarTasks = liveTasks.filter(task => task.pillarId === pillarId);
const currentTask = navigationState?.currentTask;
const isComplete = isWorkflowComplete();
const handleTaskAction = async (task: TodayTask) => {
if (!task.enabled) return;
try {
// Execute the task action
if (task.action) {
task.action();
} else if (task.actionUrl) {
navigate(task.actionUrl);
}
// Mark task as completed in workflow
if (currentWorkflow) {
await completeTask(task.id);
}
} catch (error) {
console.error('Error executing task:', error);
}
};
const handleSkipTask = async (task: TodayTask) => {
if (currentWorkflow) {
await skipTask(task.id);
}
};
const handleStartWorkflow = async () => {
if (currentWorkflow) {
await moveToNextTask();
}
};
const handleNextPillar = async () => {
// Close current modal
onClose();
// Prevent auto-reopen of current modal during navigation
if (onPreventAutoReopen) {
onPreventAutoReopen();
}
// Navigate to next pillar
if (nextPillarId) {
setTimeout(() => {
// Trigger next pillar modal opening
const nextChip = document.querySelector(`[data-pillar-id="${nextPillarId}"]`);
if (nextChip) {
(nextChip as HTMLElement).click();
}
}, 300);
}
};
const handleWorkflowComplete = async () => {
// Mark all remaining tasks in this pillar as completed
const incompleteTasks = pillarTasks.filter(task =>
task.status !== 'completed' && task.status !== 'skipped'
);
for (const task of incompleteTasks) {
try {
await completeTask(task.id);
} catch (error) {
console.error(`Failed to complete task ${task.id}:`, error);
}
}
// Close the modal
onClose();
};
// Check if all tasks in this pillar are completed or skipped
const areAllTasksCompleted = pillarTasks.every(task =>
task.status === 'completed' || task.status === 'skipped'
);
// Check if this is the Plan pillar
const isPlanPillar = pillarId === 'plan';
// Define pillar order for navigation
const pillarOrder = ['plan', 'generate', 'publish', 'analyze', 'engage', 'remarket'];
const currentPillarIndex = pillarOrder.indexOf(pillarId);
const isLastPillar = currentPillarIndex === pillarOrder.length - 1;
const nextPillarId = !isLastPillar ? pillarOrder[currentPillarIndex + 1] : null;
const getTaskStatus = (task: TodayTask) => {
if (task.status === 'completed') return 'completed';
if (task.status === 'in_progress') return 'active';
if (task.status === 'skipped') return 'skipped';
return 'pending';
};
const getTaskStatusColor = (status: string) => {
switch (status) {
case 'completed': return '#4CAF50';
case 'active': return '#2196F3';
case 'skipped': return '#FF9800';
default: return '#9E9E9E';
}
};
return (
<Modal
open={open}
onClose={onClose}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
p: { xs: 1.5, md: 3 }
}}
>
<Paper
sx={{
width: { xs: '96vw', sm: '94vw', md: '90vw' },
maxWidth: 1200,
maxHeight: '92vh',
overflow: 'auto',
background: 'linear-gradient(135deg, rgba(255,255,255,0.96) 0%, rgba(250,250,252,0.92) 100%)',
backdropFilter: 'blur(24px)',
borderRadius: 4,
boxShadow: '0 30px 60px rgba(0,0,0,0.35)',
border: '1px solid rgba(0,0,0,0.06)'
}}
>
{/* Header */}
<Box sx={{ p: { xs: 2, md: 3 }, borderBottom: '1px solid rgba(0,0,0,0.08)' }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Avatar
sx={{
background: pillarColor,
width: 48,
height: 48
}}
>
<TodayIcon sx={{ fontSize: 24, color: 'white' }} />
</Avatar>
<Box>
<Typography variant="h5" sx={{ fontWeight: 800, color: '#23252F', letterSpacing: 0.2 }}>
Today's {pillarTitle} Tasks
</Typography>
<Typography variant="body2" sx={{ color: '#5A5F6A' }}>
Complete your daily marketing workflow
</Typography>
</Box>
</Box>
<IconButton onClick={onClose} sx={{ color: '#6B7280' }}>
<CloseIcon />
</IconButton>
</Box>
</Box>
{/* Workflow Progress - Circular in Header */}
{workflowProgress && (
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
p: { xs: 2, md: 3 },
borderBottom: '1px solid rgba(0,0,0,0.08)'
}}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Typography variant="body2" sx={{ color: '#5A5F6A', fontWeight: 600 }}>
Overall Progress
</Typography>
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress
variant="determinate"
value={workflowProgress.completionPercentage}
size={40}
thickness={4}
sx={{
color: pillarColor,
'& .MuiCircularProgress-circle': {
strokeLinecap: 'round',
}
}}
/>
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Typography
variant="caption"
component="div"
sx={{
color: '#5A5F6A',
fontWeight: 700,
fontSize: '0.7rem'
}}
>
{`${Math.round(workflowProgress.completionPercentage)}%`}
</Typography>
</Box>
</Box>
</Box>
<Typography variant="body2" sx={{ color: '#5A5F6A', fontWeight: 600 }}>
{workflowProgress.completedTasks} of {workflowProgress.totalTasks} tasks
</Typography>
</Box>
)}
{/* Tasks List */}
<Box sx={{ p: { xs: 2, md: 3 } }}>
<Typography variant="h6" sx={{ mb: 2, color: '#23252F', fontWeight: 800 }}>
{pillarTitle} Tasks
</Typography>
<Stack spacing={2}>
{pillarTasks.map((task, index) => {
const status = getTaskStatus(task);
const statusColor = getTaskStatusColor(status);
const isCurrentTask = currentTask?.id === task.id;
const IconComponent = (typeof task.icon === 'function' ? task.icon : undefined) as any;
return (
<motion.div
key={task.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: index * 0.1 }}
>
<Card
sx={{
border: isCurrentTask ? `2px solid ${pillarColor}` : '1px solid rgba(0,0,0,0.08)',
background: isCurrentTask ? `${pillarColor}12` : 'white',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: '0 8px 20px rgba(0,0,0,0.08)'
}
}}
>
<CardContent sx={{ p: { xs: 2, md: 2.5 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 1 }}>
{IconComponent && (
<Avatar
sx={{
background: statusColor,
width: 36,
height: 36
}}
>
<IconComponent sx={{ fontSize: 18, color: 'white' }} />
</Avatar>
)}
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#23252F' }}>
{task.title}
</Typography>
<Typography variant="body2" sx={{ color: '#5A5F6A' }}>
{task.description}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Chip
label={status}
size="small"
sx={{
background: `${statusColor}18`,
color: statusColor,
border: `1px solid ${statusColor}40`,
textTransform: 'capitalize'
}}
/>
<Typography variant="caption" sx={{ color: '#999' }}>
{task.estimatedTime} min
</Typography>
</Box>
</Box>
{/* Task Actions */}
<Box sx={{ display: 'flex', gap: 1.25, mt: 2 }}>
{status === 'pending' && task.enabled && (
<Button
variant="contained"
size="small"
startIcon={<AlwrityIcon />}
onClick={() => handleTaskAction(task)}
disabled={isLoading}
sx={{
background: pillarColor,
'&:hover': {
background: pillarColor,
opacity: 0.9
}
}}
>
ALwrity it
</Button>
)}
{status === 'active' && (
<Button
variant="outlined"
size="small"
startIcon={<PlayIcon />}
onClick={() => handleTaskAction(task)}
disabled={isLoading}
sx={{ borderColor: pillarColor, color: pillarColor }}
>
Continue
</Button>
)}
{status === 'completed' && (
<Button
variant="outlined"
size="small"
startIcon={<CheckIcon />}
disabled
sx={{ borderColor: '#4CAF50', color: '#4CAF50' }}
>
Completed
</Button>
)}
{status === 'pending' && (
<Button
variant="text"
size="small"
startIcon={<SkipIcon />}
onClick={() => handleSkipTask(task)}
disabled={isLoading}
sx={{ color: '#FF9800' }}
>
Skip
</Button>
)}
</Box>
</CardContent>
</Card>
</motion.div>
);
})}
</Stack>
</Box>
{/* Footer Actions */}
<Box sx={{ p: { xs: 2, md: 3 }, borderTop: '1px solid rgba(0,0,0,0.08)' }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="body2" sx={{ color: '#5A5F6A' }}>
{isComplete ? '🎉 All tasks completed!' : `${pillarTasks.length} tasks in this pillar`}
</Typography>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
{/* Next button for all pillars except the last one */}
{!isLastPillar && (
<>
<Button variant="outlined" onClick={onClose}>
Close
</Button>
<Tooltip
title={areAllTasksCompleted
? `All tasks completed! Click to proceed to ${nextPillarId ? nextPillarId.charAt(0).toUpperCase() + nextPillarId.slice(1) : 'next'} pillar`
: "Complete or skip all tasks in this pillar to proceed"
}
arrow
>
<span>
<Button
variant="contained"
startIcon={<NavigateNext />}
onClick={handleNextPillar}
disabled={!areAllTasksCompleted || isLoading}
sx={{
background: pillarColor,
'&:hover': {
background: pillarColor,
opacity: 0.9
},
'&:disabled': {
background: '#ccc',
color: '#666'
}
}}
>
Next
</Button>
</span>
</Tooltip>
</>
)}
{/* Last pillar (Remarket) - Workflow Complete button acts as close */}
{isLastPillar && (
<Button
variant="contained"
startIcon={<CheckIcon />}
onClick={handleWorkflowComplete}
sx={{
background: '#4CAF50',
'&:hover': {
background: '#45a049',
opacity: 0.9
}
}}
>
Workflow Complete!
</Button>
)}
</Box>
</Box>
</Box>
</Paper>
</Modal>
);
};
export default EnhancedTodayModal;

View File

@@ -0,0 +1,609 @@
import React, { useState, useEffect } from 'react';
import {
Box,
Typography,
Chip,
Tooltip,
Modal,
Paper,
Button,
IconButton,
Divider,
Avatar,
Stack
} from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import {
Today as TodayIcon,
TextFields as TextIcon,
Image as ImageIcon,
AudioFile as AudioIcon,
VideoFile as VideoIcon,
Close as CloseIcon,
Facebook as FacebookIcon,
LinkedIn as LinkedInIcon,
Language as WebsiteIcon,
AutoAwesome as AlwrityIcon
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
import EnhancedTodayChip from './EnhancedTodayChip';
import { TodayTask } from '../../../types/workflow';
// Today Modal Component
const TodayModal: React.FC<{
open: boolean;
onClose: () => void;
}> = ({ open, onClose }) => {
const navigate = useNavigate();
const tasks = [
{
id: 'facebook',
title: "Post 'ALwrity AI Content Generation' on Facebook",
platform: 'Facebook',
icon: FacebookIcon,
color: '#1877F2',
enabled: true,
action: () => navigate('/facebook-writer')
},
{
id: 'website',
title: 'Write a Blog on "AI Image generation prompts" for wix website',
platform: 'Website',
icon: WebsiteIcon,
color: '#FF6B35',
enabled: false,
action: () => {}
},
{
id: 'linkedin',
title: "Write & Post on LinkedIn on 'AI Agents frameworks latest news'",
platform: 'LinkedIn',
icon: LinkedInIcon,
color: '#0077B5',
enabled: true,
action: () => navigate('/linkedin-writer')
}
];
return (
<Modal
open={open}
onClose={onClose}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
p: 2
}}
>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={{ duration: 0.3 }}
>
<Paper
elevation={24}
sx={{
width: { xs: '95%', sm: '90%', md: '600px' },
maxHeight: '80vh',
overflow: 'auto',
borderRadius: 3,
background: 'linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)',
position: 'relative'
}}
>
{/* Header */}
<Box
sx={{
p: 3,
background: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
color: 'white',
borderRadius: '12px 12px 0 0',
position: 'relative'
}}
>
<IconButton
onClick={onClose}
sx={{
position: 'absolute',
top: 16,
right: 16,
color: 'white',
backgroundColor: 'rgba(255,255,255,0.1)',
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.2)'
}
}}
>
<CloseIcon />
</IconButton>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Avatar
sx={{
backgroundColor: 'rgba(255,255,255,0.2)',
mr: 2,
width: 48,
height: 48
}}
>
<TodayIcon sx={{ fontSize: 24 }} />
</Avatar>
<Box>
<Typography variant="h5" sx={{ fontWeight: 700, mb: 0.5 }}>
Today's Tasks
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9 }}>
AI-powered content generation for today
</Typography>
</Box>
</Box>
</Box>
{/* Content */}
<Box sx={{ p: 3 }}>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 3, color: '#1565C0' }}>
🚀 Ready to Generate Content
</Typography>
<Stack spacing={2}>
{tasks.map((task, index) => {
const IconComponent = task.icon;
return (
<motion.div
key={task.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: index * 0.1 }}
>
<Paper
elevation={2}
sx={{
p: 2.5,
borderRadius: 2,
border: `2px solid ${task.enabled ? task.color : '#E0E0E0'}`,
backgroundColor: task.enabled ? 'white' : '#F5F5F5',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: `0 8px 24px ${task.color}20`
}
}}
>
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
<Avatar
sx={{
backgroundColor: task.enabled ? task.color : '#BDBDBD',
width: 40,
height: 40
}}
>
<IconComponent sx={{ fontSize: 20, color: 'white' }} />
</Avatar>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 1 }}>
{task.title}
</Typography>
<Chip
label={task.platform}
size="small"
sx={{
backgroundColor: task.enabled ? `${task.color}20` : '#E0E0E0',
color: task.enabled ? task.color : '#757575',
fontWeight: 500
}}
/>
</Box>
<Button
variant="contained"
startIcon={<AlwrityIcon />}
onClick={task.action}
disabled={!task.enabled}
sx={{
background: task.enabled
? `linear-gradient(135deg, ${task.color} 0%, ${task.color}CC 100%)`
: '#E0E0E0',
color: 'white',
px: 3,
py: 1,
borderRadius: 2,
fontWeight: 600,
textTransform: 'none',
boxShadow: task.enabled
? `0 4px 12px ${task.color}40`
: 'none',
'&:hover': task.enabled ? {
background: `linear-gradient(135deg, ${task.color}CC 0%, ${task.color} 100%)`,
boxShadow: `0 6px 16px ${task.color}50`
} : {},
'&:disabled': {
backgroundColor: '#E0E0E0',
color: '#9E9E9E'
}
}}
>
ALwrity it
</Button>
</Box>
</Paper>
</motion.div>
);
})}
</Stack>
<Divider sx={{ my: 3 }} />
<Box sx={{ textAlign: 'center' }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 2 }}>
💡 Tip: Use ALwrity's AI to generate engaging content tailored to each platform
</Typography>
</Box>
</Box>
</Paper>
</motion.div>
</Modal>
);
};
// Enhanced Chip Component for Generate Pillar
const GenerateChip: React.FC<{
chip: any;
delay?: number;
onTodayClick?: () => void;
}> = ({ chip, delay = 0, onTodayClick }) => {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
if (chip.bubbles && chip.bubbles.length > 0) {
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % chip.bubbles.length);
}, 2000 + delay * 300);
return () => clearInterval(interval);
}
}, [chip.bubbles?.length, delay]);
const IconComponent = chip.icon;
const handleClick = () => {
if (chip.label === 'Today' && onTodayClick) {
onTodayClick();
}
};
return (
<Tooltip
title={
chip.bubbles && chip.bubbles.length > 0 ? (
<Box>
<Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
{chip.label}
</Typography>
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<Typography variant="caption" sx={{ color: 'white' }}>
{chip.bubbles[currentIndex]}
</Typography>
</motion.div>
</AnimatePresence>
</Box>
) : chip.label
}
arrow
placement="top"
>
<Box
sx={{
position: 'relative',
cursor: 'pointer',
transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
'&:hover': {
transform: 'translateY(-4px) scale(1.05)',
'& .chip-glow': {
opacity: 1,
transform: 'scale(1.2)'
},
'& .chip-shadow': {
opacity: 0.6,
transform: 'translateY(8px) scale(1.1)'
}
}
}}
onClick={handleClick}
>
{/* Glow Effect */}
<Box
className="chip-glow"
sx={{
position: 'absolute',
top: -8,
left: -8,
right: -8,
bottom: -8,
background: chip.gradient || chip.color,
borderRadius: '20px',
opacity: 0,
transition: 'all 0.4s ease',
filter: 'blur(12px)',
zIndex: -2
}}
/>
{/* Shadow Effect */}
<Box
className="chip-shadow"
sx={{
position: 'absolute',
top: 4,
left: 2,
right: -2,
bottom: -4,
background: 'rgba(0,0,0,0.3)',
borderRadius: '16px',
opacity: 0.3,
transition: 'all 0.4s ease',
filter: 'blur(8px)',
zIndex: -1
}}
/>
{/* Main Chip */}
<Chip
icon={<IconComponent sx={{ fontSize: 14 }} />}
label={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<Typography variant="caption" sx={{ fontWeight: 600, fontSize: '0.7rem' }}>
{chip.label}
</Typography>
{chip.value && (
<Box
sx={{
backgroundColor: 'rgba(255,255,255,0.9)',
color: chip.color,
borderRadius: '50%',
width: 16,
height: 16,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '0.6rem',
fontWeight: 700,
boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
}}
>
{chip.value}
</Box>
)}
</Box>
}
size="small"
sx={{
background: `linear-gradient(135deg,
rgba(255,255,255,0.25) 0%,
rgba(255,255,255,0.1) 50%,
rgba(255,255,255,0.05) 100%)`,
backdropFilter: 'blur(20px)',
border: '1px solid rgba(255,255,255,0.3)',
color: 'white',
fontSize: '0.7rem',
fontWeight: 600,
height: 28,
minWidth: 100,
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: '-100%',
width: '100%',
height: '100%',
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent)',
transition: 'left 0.6s ease',
zIndex: 1
},
'&:hover::before': {
left: '100%'
},
'& .MuiChip-label': {
px: 1,
zIndex: 2,
position: 'relative'
},
'& .MuiChip-icon': {
zIndex: 2,
position: 'relative'
},
'&:hover': {
background: `linear-gradient(135deg,
rgba(255,255,255,0.35) 0%,
rgba(255,255,255,0.2) 50%,
rgba(255,255,255,0.1) 100%)`,
border: '1px solid rgba(255,255,255,0.5)',
boxShadow: `0 8px 32px ${chip.color}40,
0 4px 16px rgba(0,0,0,0.1),
inset 0 1px 0 rgba(255,255,255,0.3)`
}
}}
/>
</Box>
</Tooltip>
);
};
// Generate Pillar Chips Component
const GeneratePillarChips: React.FC<{
index: number;
isHovered?: boolean;
}> = ({ index, isHovered = false }) => {
// Generate pillar Today tasks
const generateTodayTasks: TodayTask[] = [
{
id: 'facebook-post',
pillarId: 'generate',
title: "Post 'ALwrity AI Content Generation' on Facebook",
description: 'Create and publish engaging Facebook content',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/facebook-writer',
icon: FacebookIcon,
color: '#1877F2',
enabled: true,
action: () => console.log('Navigate to Facebook writer')
},
{
id: 'blog-post',
pillarId: 'generate',
title: 'Write Blog on "AI Image Generation Prompts"',
description: 'Create comprehensive blog post for website',
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 30,
actionType: 'navigate' as const,
actionUrl: '/blog-writer',
icon: WebsiteIcon,
color: '#FF6B35',
enabled: false,
action: () => {}
},
{
id: 'linkedin-post',
pillarId: 'generate',
title: "Write & Post on LinkedIn 'AI Agents Frameworks'",
description: 'Create professional LinkedIn content',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/linkedin-writer',
icon: LinkedInIcon,
color: '#0077B5',
enabled: true,
action: () => console.log('Navigate to LinkedIn writer')
}
];
// Generate pillar chips data
const generateChips = {
text: {
label: 'Text',
icon: TextIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['Blog posts', 'Social media', 'Email content']
},
image: {
label: 'Image',
icon: ImageIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['Visual content', 'Infographics', 'Social images']
},
audio: {
label: 'Audio',
icon: AudioIcon,
color: '#9C27B0',
gradient: 'linear-gradient(135deg, #9C27B0 0%, #6A1B9A 100%)',
bubbles: ['Podcast scripts', 'Voice content', 'Audio ads']
},
video: {
label: 'Video',
icon: VideoIcon,
color: '#E91E63',
gradient: 'linear-gradient(135deg, #E91E63 0%, #C2185B 100%)',
bubbles: ['Video scripts', 'YouTube content', 'Social videos']
}
};
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: 1,
flexGrow: 1,
justifyContent: isHovered ? 'center' : 'flex-start'
}}
>
{/* Today Chip - Always Visible */}
<EnhancedTodayChip
pillarId="generate"
pillarTitle="Generate"
pillarColor="#1565C0"
tasks={generateTodayTasks}
delay={index * 5}
/>
{/* More Options Indicator */}
{!isHovered && (
<motion.div
animate={{ opacity: [0.5, 1, 0.5] }}
transition={{ duration: 2, repeat: Infinity, ease: 'easeInOut' }}
style={{ alignSelf: 'center', marginTop: '8px' }}
>
<Typography variant="caption" sx={{ fontSize: '0.6rem', opacity: 0.7, color: 'white' }}>
</Typography>
</motion.div>
)}
{/* Content Type Chips - Progressive Disclosure */}
<AnimatePresence>
{isHovered && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3, ease: 'easeInOut' }}
style={{ overflow: 'hidden' }}
>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 1 }}>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<GenerateChip chip={generateChips.text} delay={index * 5 + 1} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<GenerateChip chip={generateChips.image} delay={index * 5 + 2} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.3 }}
>
<GenerateChip chip={generateChips.audio} delay={index * 5 + 3} />
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<GenerateChip chip={generateChips.video} delay={index * 5 + 4} />
</motion.div>
</Box>
</motion.div>
)}
</AnimatePresence>
</Box>
);
};
export default GeneratePillarChips;

View File

@@ -0,0 +1,324 @@
import React from 'react';
import {
Box,
Typography,
Modal,
Paper,
Button,
IconButton,
Divider,
LinearProgress,
Avatar,
Stack,
Chip
} from '@mui/material';
import { motion } from 'framer-motion';
import {
Close as CloseIcon,
Settings as SettingsIcon,
PersonAdd as OnboardingIcon,
CheckCircle as CheckIcon,
TrendingUp as TrendingUpIcon,
Psychology as PsychologyIcon
} from '@mui/icons-material';
// Onboarding Modal Component
const OnboardingModal: React.FC<{
open: boolean;
onClose: () => void;
}> = ({ open, onClose }) => {
// Mock onboarding data - in real app, this would come from database
const onboardingData = {
userProfile: {
name: 'John Doe',
company: 'TechCorp Inc.',
role: 'Marketing Manager',
completion: 85
},
preferences: {
contentTypes: ['Blog Posts', 'Social Media', 'Email Campaigns'],
platforms: ['LinkedIn', 'Facebook', 'Twitter'],
tone: 'Professional',
frequency: 'Daily'
},
goals: {
primary: 'Increase brand awareness',
secondary: 'Generate leads',
metrics: ['Engagement Rate', 'Click-through Rate', 'Conversion Rate']
},
aiAnalysis: {
score: 8.5,
insights: [
'Strong foundation with clear goals and preferences',
'Content strategy well-aligned with target audience',
'Consider expanding to Instagram for better reach',
'Email campaigns could benefit from A/B testing'
],
recommendations: [
'Set up automated content scheduling',
'Implement advanced analytics tracking',
'Create content templates for consistency',
'Establish brand voice guidelines'
]
}
};
return (
<Modal
open={open}
onClose={onClose}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
p: 2
}}
>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={{ duration: 0.3 }}
>
<Paper
elevation={24}
sx={{
width: { xs: '95%', sm: '90%', md: '80%', lg: '70%' },
maxWidth: 800,
maxHeight: '90vh',
overflow: 'auto',
borderRadius: 3,
background: 'linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)',
position: 'relative'
}}
>
{/* Header */}
<Box
sx={{
p: 3,
background: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
color: 'white',
borderRadius: '12px 12px 0 0',
position: 'relative'
}}
>
<IconButton
onClick={onClose}
sx={{
position: 'absolute',
top: 16,
right: 16,
color: 'white',
backgroundColor: 'rgba(255,255,255,0.1)',
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.2)'
}
}}
>
<CloseIcon />
</IconButton>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Avatar
sx={{
backgroundColor: 'rgba(255,255,255,0.2)',
mr: 2,
width: 48,
height: 48
}}
>
<OnboardingIcon sx={{ fontSize: 24 }} />
</Avatar>
<Box>
<Typography variant="h5" sx={{ fontWeight: 700, mb: 0.5 }}>
Onboarding Status
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9 }}>
Complete your setup to unlock full potential
</Typography>
</Box>
</Box>
<Box sx={{ mb: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2" sx={{ fontWeight: 600 }}>
Overall Progress
</Typography>
<Typography variant="body2" sx={{ fontWeight: 600 }}>
{onboardingData.userProfile.completion}%
</Typography>
</Box>
<LinearProgress
variant="determinate"
value={onboardingData.userProfile.completion}
sx={{
height: 8,
borderRadius: 4,
backgroundColor: 'rgba(255,255,255,0.2)',
'& .MuiLinearProgress-bar': {
backgroundColor: 'white',
borderRadius: 4
}
}}
/>
</Box>
</Box>
{/* Content */}
<Box sx={{ p: 3 }}>
{/* User Profile Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2, color: '#2E7D32' }}>
👤 User Profile
</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: 'repeat(2, 1fr)' }, gap: 2 }}>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Name</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.userProfile.name}</Typography>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Company</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.userProfile.company}</Typography>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Role</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.userProfile.role}</Typography>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Completion</Typography>
<Typography variant="body1" sx={{ fontWeight: 600, color: '#4CAF50' }}>{onboardingData.userProfile.completion}%</Typography>
</Box>
</Box>
</Box>
{/* Preferences Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2, color: '#2E7D32' }}>
Preferences
</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: 'repeat(2, 1fr)' }, gap: 2 }}>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 1 }}>Content Types</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap">
{onboardingData.preferences.contentTypes.map((type, idx) => (
<Chip key={idx} label={type} size="small" sx={{ backgroundColor: '#E3F2FD', color: '#1976D2' }} />
))}
</Stack>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 1 }}>Platforms</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap">
{onboardingData.preferences.platforms.map((platform, idx) => (
<Chip key={idx} label={platform} size="small" sx={{ backgroundColor: '#E8F5E8', color: '#2E7D32' }} />
))}
</Stack>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Tone</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.preferences.tone}</Typography>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Frequency</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.preferences.frequency}</Typography>
</Box>
</Box>
</Box>
{/* Goals Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2, color: '#2E7D32' }}>
🎯 Goals
</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: 'repeat(2, 1fr)' }, gap: 2 }}>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Primary Goal</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.goals.primary}</Typography>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 0.5 }}>Secondary Goal</Typography>
<Typography variant="body1" sx={{ fontWeight: 600 }}>{onboardingData.goals.secondary}</Typography>
</Box>
<Box sx={{ p: 2, backgroundColor: 'white', borderRadius: 2, boxShadow: 1, gridColumn: { xs: '1', md: '1 / -1' } }}>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 1 }}>Key Metrics</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap">
{onboardingData.goals.metrics.map((metric, idx) => (
<Chip key={idx} label={metric} size="small" sx={{ backgroundColor: '#FFF3E0', color: '#F57C00' }} />
))}
</Stack>
</Box>
</Box>
</Box>
{/* AI Analysis Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2, color: '#2E7D32' }}>
🤖 AI Analysis
</Typography>
<Box sx={{ p: 3, backgroundColor: 'white', borderRadius: 2, boxShadow: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<PsychologyIcon sx={{ color: '#9C27B0', mr: 1 }} />
<Typography variant="h6" sx={{ fontWeight: 600 }}>
Analysis Score: {onboardingData.aiAnalysis.score}/10
</Typography>
</Box>
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 600, mb: 1, color: '#2E7D32' }}>
Key Insights:
</Typography>
{onboardingData.aiAnalysis.insights.map((insight, idx) => (
<Box key={idx} sx={{ display: 'flex', alignItems: 'flex-start', mb: 1 }}>
<CheckIcon sx={{ color: '#4CAF50', fontSize: 16, mr: 1, mt: 0.5 }} />
<Typography variant="body2">{insight}</Typography>
</Box>
))}
</Box>
<Box>
<Typography variant="subtitle2" sx={{ fontWeight: 600, mb: 1, color: '#2E7D32' }}>
Recommendations:
</Typography>
{onboardingData.aiAnalysis.recommendations.map((rec, idx) => (
<Box key={idx} sx={{ display: 'flex', alignItems: 'flex-start', mb: 1 }}>
<TrendingUpIcon sx={{ color: '#FF9800', fontSize: 16, mr: 1, mt: 0.5 }} />
<Typography variant="body2">{rec}</Typography>
</Box>
))}
</Box>
</Box>
</Box>
{/* Settings Button */}
<Divider sx={{ my: 3 }} />
<Box sx={{ textAlign: 'center' }}>
<Button
variant="contained"
startIcon={<SettingsIcon />}
sx={{
background: 'linear-gradient(135deg, #9C27B0 0%, #6A1B9A 100%)',
color: 'white',
px: 4,
py: 1.5,
borderRadius: 2,
fontWeight: 600,
boxShadow: '0 4px 12px rgba(156, 39, 176, 0.3)',
'&:hover': {
background: 'linear-gradient(135deg, #6A1B9A 0%, #4A148C 100%)',
boxShadow: '0 6px 16px rgba(156, 39, 176, 0.4)'
}
}}
>
Edit Onboarding Data
</Button>
<Typography variant="caption" sx={{ display: 'block', mt: 1, color: 'text.secondary' }}>
Configure your preferences and goals in the Settings page
</Typography>
</Box>
</Box>
</Paper>
</motion.div>
</Modal>
);
};
export default OnboardingModal;

View File

@@ -0,0 +1,576 @@
import React from 'react';
import {
// Plan pillar icons
Assignment as PlanIcon,
PersonAdd as OnboardingIcon,
Business as StrategyIcon,
CalendarMonth as CalendarIcon,
RateReview as ReviewIcon,
// Generate pillar icons
AutoAwesome as GenerateIcon,
ThumbUp as GoodIcon,
ThumbDown as BadIcon,
Warning as UglyIcon,
// Publish pillar icons
Publish as PublishIcon,
// Analyze pillar icons
Analytics as AnalyzeIcon,
// Engage pillar icons
Campaign as EngageIcon,
// Remarket pillar icons
Psychology as RemarketIcon,
// Task icons
Facebook as FacebookIcon,
LinkedIn as LinkedInIcon,
Language as WebsiteIcon,
ChatBubbleOutline as ChatIcon,
Assessment as AssessmentIcon,
Share as ShareIcon,
ThumbUp as ThumbUpIcon,
Refresh as RefreshIcon,
Article as ArticleIcon
} from '@mui/icons-material';
import { TodayTask } from '../../../types/workflow';
// Define the chip interface
export interface PillarChip {
label: string;
icon: React.ComponentType<any>;
color: string;
gradient: string;
bubbles: string[];
value?: number | null;
}
// Define the pillar data interface
export interface PillarData {
id: string;
title: string;
icon: React.ComponentType<any>;
color: string;
gradient: string;
chips: {
[key: string]: PillarChip;
};
todayTasks: TodayTask[];
}
// Enhanced pillar data with Today tasks
export const pillarData: PillarData[] = [
{
id: 'plan',
title: 'Plan',
icon: PlanIcon,
color: '#2E7D32',
gradient: 'linear-gradient(135deg, #2E7D32 0%, #1B5E20 100%)',
chips: {
onboarding: {
label: 'On-Boarding',
icon: OnboardingIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['User Profile Setup', 'Preferences Configured', 'Goals Defined'],
value: 2
},
strategy: {
label: 'Strategy',
icon: StrategyIcon,
color: '#2196F3',
gradient: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
bubbles: ['Content Strategy Defined', 'Target Audience Identified', 'Brand Voice Established'],
value: 7
},
calendar: {
label: 'Calendar',
icon: CalendarIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['Publishing Schedule Set', 'Content Calendar Created', 'Campaign Timeline Planned'],
value: 11
},
review: {
label: 'Review & Optimize',
icon: ReviewIcon,
color: '#9C27B0',
gradient: 'linear-gradient(135deg, #9C27B0 0%, #6A1B9A 100%)',
bubbles: ['Content Calendar Generated', 'SEO Strategy Optimized', 'Topic Clusters Identified'],
value: null
}
},
todayTasks: [
{
id: 'content-calendar',
pillarId: 'plan',
title: 'Create Weekly Content Calendar',
description: 'Plan and schedule content for the upcoming week',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/content-planning-dashboard',
icon: CalendarIcon,
color: '#2E7D32',
enabled: true,
action: () => console.log('Navigate to content calendar')
},
{
id: 'seo-strategy',
pillarId: 'plan',
title: 'Update SEO Strategy',
description: 'Review and optimize SEO keywords and content strategy',
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/seo-strategy',
icon: AssessmentIcon,
color: '#2196F3',
enabled: true,
action: () => console.log('Navigate to SEO strategy')
},
{
id: 'competitor-analysis',
pillarId: 'plan',
title: 'Competitor Analysis',
description: 'Analyze competitor content and identify opportunities',
status: 'pending' as const,
priority: 'low' as const,
estimatedTime: 30,
actionType: 'navigate' as const,
actionUrl: '/competitor-analysis',
icon: AnalyzeIcon,
color: '#FF9800',
enabled: false,
action: () => {}
}
]
},
{
id: 'generate',
title: 'Generate',
icon: GenerateIcon,
color: '#1565C0',
gradient: 'linear-gradient(135deg, #1565C0 0%, #0D47A1 100%)',
chips: {
good: {
label: 'Quality Content',
icon: GoodIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['SEO Optimized', 'Brand Voice Consistent', 'Engaging Headlines']
},
bad: {
label: 'Content Issues',
icon: BadIcon,
color: '#F44336',
gradient: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
bubbles: ['Poor Grammar', 'Weak CTAs', 'Generic Content']
},
ugly: {
label: 'Critical Problems',
icon: UglyIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['No Brand Voice', 'Plagiarized Content', 'No SEO Optimization']
},
review: {
label: 'Review & Optimize',
icon: ReviewIcon,
color: '#2196F3',
gradient: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
bubbles: ['Blog Post Generated', 'Social Media Content Created', 'Email Campaign Written']
}
},
todayTasks: [
{
id: 'facebook-post',
pillarId: 'generate',
title: "Post 'ALwrity AI Content Generation' on Facebook",
description: 'Create and publish engaging Facebook content',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/facebook-writer',
icon: FacebookIcon,
color: '#1877F2',
enabled: true,
action: () => console.log('Navigate to Facebook writer')
},
{
id: 'blog-post',
pillarId: 'generate',
title: 'Write Blog on "AI Image Generation Prompts"',
description: 'Create comprehensive blog post for website',
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 45,
actionType: 'navigate' as const,
actionUrl: '/blog-writer',
icon: ArticleIcon,
color: '#FF6B35',
enabled: false,
action: () => {}
},
{
id: 'linkedin-post',
pillarId: 'generate',
title: "Write & Post on LinkedIn 'AI Agents Frameworks'",
description: 'Create professional LinkedIn content',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/linkedin-writer',
icon: LinkedInIcon,
color: '#0077B5',
enabled: true,
action: () => console.log('Navigate to LinkedIn writer')
}
]
},
{
id: 'publish',
title: 'Publish',
icon: PublishIcon,
color: '#E65100',
gradient: 'linear-gradient(135deg, #E65100 0%, #BF360C 100%)',
chips: {
good: {
label: 'Smooth Publishing',
icon: GoodIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['Multi-Platform Sync', 'Optimal Timing', 'Auto-Scheduling']
},
bad: {
label: 'Publishing Issues',
icon: BadIcon,
color: '#F44336',
gradient: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
bubbles: ['Manual Publishing', 'Poor Timing', 'Platform Errors']
},
ugly: {
label: 'Critical Failures',
icon: UglyIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['No Publishing Strategy', 'Content Not Published', 'Platform Disconnects']
},
review: {
label: 'Review & Optimize',
icon: ReviewIcon,
color: '#2196F3',
gradient: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
bubbles: ['Content Published to LinkedIn', 'Facebook Post Scheduled', 'Twitter Thread Live']
}
},
todayTasks: [
{
id: 'schedule-posts',
pillarId: 'publish',
title: 'Schedule Today\'s Content',
description: 'Schedule all content for optimal engagement times',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 10,
actionType: 'navigate' as const,
actionUrl: '/publishing-scheduler',
icon: PublishIcon,
color: '#E65100',
enabled: true,
action: () => console.log('Navigate to publishing scheduler')
},
{
id: 'cross-platform',
pillarId: 'publish',
title: 'Cross-Platform Publishing',
description: 'Publish content across all connected platforms',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/cross-platform-publisher',
icon: ShareIcon,
color: '#4CAF50',
enabled: true,
action: () => console.log('Navigate to cross-platform publisher')
},
{
id: 'publish-analytics',
pillarId: 'publish',
title: 'Publishing Analytics Review',
description: 'Review publishing performance and optimize timing',
status: 'pending' as const,
priority: 'low' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/publishing-analytics',
icon: AnalyzeIcon,
color: '#2196F3',
enabled: false,
action: () => {}
}
]
},
{
id: 'analyze',
title: 'Analyze',
icon: AnalyzeIcon,
color: '#6A1B9A',
gradient: 'linear-gradient(135deg, #6A1B9A 0%, #4A148C 100%)',
chips: {
good: {
label: 'Great Analytics',
icon: GoodIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['Real-time Tracking', 'Detailed Insights', 'ROI Measurement']
},
bad: {
label: 'Analytics Gaps',
icon: BadIcon,
color: '#F44336',
gradient: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
bubbles: ['Limited Tracking', 'No ROI Data', 'Poor Reporting']
},
ugly: {
label: 'No Analytics',
icon: UglyIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['No Tracking Setup', 'No Performance Data', 'Blind Publishing']
},
review: {
label: 'Review & Optimize',
icon: ReviewIcon,
color: '#2196F3',
gradient: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
bubbles: ['Engagement Rate: +25%', 'Click-Through Rate Improved', 'Social Shares Increased']
}
},
todayTasks: [
{
id: 'performance-report',
pillarId: 'analyze',
title: 'Generate Performance Report',
description: 'Create comprehensive analytics report for this week',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 25,
actionType: 'navigate' as const,
actionUrl: '/analytics-dashboard',
icon: AnalyzeIcon,
color: '#6A1B9A',
enabled: true,
action: () => console.log('Navigate to analytics dashboard')
},
{
id: 'engagement-analysis',
pillarId: 'analyze',
title: 'Engagement Analysis',
description: 'Analyze engagement metrics and identify trends',
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/engagement-analytics',
icon: ThumbUpIcon,
color: '#4CAF50',
enabled: true,
action: () => console.log('Navigate to engagement analytics')
},
{
id: 'roi-calculator',
pillarId: 'analyze',
title: 'ROI Calculator Update',
description: 'Update ROI calculations and performance metrics',
status: 'pending' as const,
priority: 'low' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/roi-calculator',
icon: AssessmentIcon,
color: '#FF9800',
enabled: false,
action: () => {}
}
]
},
{
id: 'engage',
title: 'Engage',
icon: EngageIcon,
color: '#C2185B',
gradient: 'linear-gradient(135deg, #C2185B 0%, #880E4F 100%)',
chips: {
good: {
label: 'High Engagement',
icon: GoodIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['Active Community', 'Quick Responses', 'Viral Content']
},
bad: {
label: 'Low Engagement',
icon: BadIcon,
color: '#F44336',
gradient: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
bubbles: ['Slow Responses', 'Few Interactions', 'Poor Community']
},
ugly: {
label: 'No Engagement',
icon: UglyIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['No Community', 'No Responses', 'Ignored Content']
},
review: {
label: 'Review & Optimize',
icon: ReviewIcon,
color: '#2196F3',
gradient: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
bubbles: ['Comments Responded Automatically', 'Community Engagement Boosted', 'Customer Queries Resolved']
}
},
todayTasks: [
{
id: 'respond-comments',
pillarId: 'engage',
title: 'Respond to Comments',
description: 'Engage with audience comments across all platforms',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 30,
actionType: 'navigate' as const,
actionUrl: '/comment-management',
icon: ChatIcon,
color: '#C2185B',
enabled: true,
action: () => console.log('Navigate to comment management')
},
{
id: 'community-building',
pillarId: 'engage',
title: 'Community Building',
description: 'Foster community engagement and build relationships',
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 25,
actionType: 'navigate' as const,
actionUrl: '/community-tools',
icon: ThumbUpIcon,
color: '#4CAF50',
enabled: true,
action: () => console.log('Navigate to community tools')
},
{
id: 'engagement-strategy',
pillarId: 'engage',
title: 'Engagement Strategy Review',
description: 'Review and optimize engagement strategies',
status: 'pending' as const,
priority: 'low' as const,
estimatedTime: 20,
actionType: 'navigate' as const,
actionUrl: '/engagement-strategy',
icon: AssessmentIcon,
color: '#FF9800',
enabled: false,
action: () => {}
}
]
},
{
id: 'remarket',
title: 'Remarket',
icon: RemarketIcon,
color: '#00695C',
gradient: 'linear-gradient(135deg, #00695C 0%, #004D40 100%)',
chips: {
good: {
label: 'Smart Remarketing',
icon: GoodIcon,
color: '#4CAF50',
gradient: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
bubbles: ['Targeted Campaigns', 'High Conversion', 'ROI Optimized']
},
bad: {
label: 'Poor Remarketing',
icon: BadIcon,
color: '#F44336',
gradient: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
bubbles: ['Low Conversion', 'Poor Targeting', 'Wasted Budget']
},
ugly: {
label: 'No Remarketing',
icon: UglyIcon,
color: '#FF9800',
gradient: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
bubbles: ['No Retargeting', 'Lost Opportunities', 'No Lead Nurturing']
},
review: {
label: 'Review & Optimize',
icon: ReviewIcon,
color: '#2196F3',
gradient: 'linear-gradient(135deg, #2196F3 0%, #1565C0 100%)',
bubbles: ['Remarketing Campaign Launched', 'Content Amplified Successfully', 'Lead Nurturing Sequence Active']
}
},
todayTasks: [
{
id: 'retargeting-campaign',
pillarId: 'remarket',
title: 'Launch Retargeting Campaign',
description: 'Create and launch targeted remarketing campaigns',
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 35,
actionType: 'navigate' as const,
actionUrl: '/remarketing-dashboard',
icon: RemarketIcon,
color: '#00695C',
enabled: true,
action: () => console.log('Navigate to remarketing dashboard')
},
{
id: 'lead-nurturing',
pillarId: 'remarket',
title: 'Lead Nurturing Sequence',
description: 'Set up automated lead nurturing workflows',
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 30,
actionType: 'navigate' as const,
actionUrl: '/lead-nurturing',
icon: RefreshIcon,
color: '#4CAF50',
enabled: true,
action: () => console.log('Navigate to lead nurturing')
},
{
id: 'conversion-optimization',
pillarId: 'remarket',
title: 'Conversion Optimization',
description: 'Optimize remarketing campaigns for better conversion',
status: 'pending' as const,
priority: 'low' as const,
estimatedTime: 25,
actionType: 'navigate' as const,
actionUrl: '/conversion-optimization',
icon: AssessmentIcon,
color: '#FF9800',
enabled: false,
action: () => {}
}
]
}
];
export default pillarData;

View File

@@ -0,0 +1,338 @@
import React from 'react';
import { Box, Chip, useTheme } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import {
Facebook,
LinkedIn,
Twitter,
Instagram,
YouTube,
Article,
CheckCircle
} from '@mui/icons-material';
import EnhancedTodayChip from './EnhancedTodayChip';
import { TodayTask } from '../../../types/workflow';
interface PublishPillarChipsProps {
isHovered: boolean;
pillarColor: string;
}
const PublishPillarChips: React.FC<PublishPillarChipsProps> = ({
isHovered,
pillarColor
}) => {
const theme = useTheme();
const navigate = useNavigate();
// Today's tasks for Publish pillar
const todayTasks: TodayTask[] = [
{
id: "publish-facebook-post",
pillarId: "publish",
title: "Publish reviewed Facebook post",
description: "Post 'ALwrity AI Content Generation' on Facebook",
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 10,
actionType: 'navigate' as const,
actionUrl: '/facebook-writer',
icon: Facebook,
color: "#1877F2",
enabled: true,
action: () => navigate('/facebook-writer')
},
{
id: "publish-linkedin-article",
pillarId: "publish",
title: "Schedule LinkedIn article",
description: "Publish 'AI Agents frameworks latest news' on LinkedIn",
status: 'pending' as const,
priority: 'high' as const,
estimatedTime: 15,
actionType: 'navigate' as const,
actionUrl: '/linkedin-writer',
icon: LinkedIn,
color: "#0077B5",
enabled: true,
action: () => navigate('/linkedin-writer')
},
{
id: "publish-review-content",
pillarId: "publish",
title: "Review pending content",
description: "Review 3 pending blog posts for website",
status: 'pending' as const,
priority: 'medium' as const,
estimatedTime: 25,
actionType: 'navigate' as const,
actionUrl: '/content-review',
icon: Article,
color: "#FF6B35",
enabled: false,
action: () => {}
}
];
const handleChipClick = (platform: string) => {
switch (platform) {
case 'facebook':
navigate('/facebook-writer');
break;
case 'linkedin':
navigate('/linkedin-writer');
break;
default:
break;
}
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, width: '100%' }}>
{/* Today Chip - Always visible */}
<EnhancedTodayChip
pillarId="publish"
pillarTitle="Publish"
pillarColor={pillarColor}
tasks={todayTasks}
delay={0}
/>
{/* Progressive disclosure chips */}
<AnimatePresence>
{isHovered && (
<>
{/* Reviewed Chip */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.1 }}
style={{ position: 'relative' }}
>
<Chip
icon={<CheckCircle sx={{ fontSize: 16 }} />}
label="Reviewed"
sx={{
height: 28,
minWidth: 100,
background: 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)',
color: 'white',
fontWeight: 600,
fontSize: '0.75rem',
border: '2px solid #4CAF50',
boxShadow: '0 4px 12px rgba(76, 175, 80, 0.3), 0 0 0 1px rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
'&:hover': {
transform: 'translateY(-2px) scale(1.05)',
boxShadow: '0 6px 20px rgba(76, 175, 80, 0.4), 0 0 0 1px rgba(255,255,255,0.2)',
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
/>
<Box
sx={{
position: 'absolute',
top: -8,
right: -8,
width: 20,
height: 20,
borderRadius: '50%',
background: 'linear-gradient(135deg, #FF6B35 0%, #F7931E 100%)',
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '0.7rem',
fontWeight: 700,
boxShadow: '0 2px 8px rgba(255, 107, 53, 0.4)',
border: '2px solid white',
}}
>
3
</Box>
</motion.div>
{/* Facebook Chip */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<Chip
icon={<Facebook sx={{ fontSize: 16 }} />}
label="Facebook"
onClick={() => handleChipClick('facebook')}
sx={{
height: 28,
minWidth: 100,
background: 'linear-gradient(135deg, #1877F2 0%, #166FE5 100%)',
color: 'white',
fontWeight: 600,
fontSize: '0.75rem',
border: '2px solid #1877F2',
boxShadow: '0 4px 12px rgba(24, 119, 242, 0.3), 0 0 0 1px rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
cursor: 'pointer',
'&:hover': {
transform: 'translateY(-2px) scale(1.05)',
boxShadow: '0 6px 20px rgba(24, 119, 242, 0.4), 0 0 0 1px rgba(255,255,255,0.2)',
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
/>
</motion.div>
{/* LinkedIn Chip */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.3 }}
>
<Chip
icon={<LinkedIn sx={{ fontSize: 16 }} />}
label="LinkedIn"
onClick={() => handleChipClick('linkedin')}
sx={{
height: 28,
minWidth: 100,
background: 'linear-gradient(135deg, #0077B5 0%, #005885 100%)',
color: 'white',
fontWeight: 600,
fontSize: '0.75rem',
border: '2px solid #0077B5',
boxShadow: '0 4px 12px rgba(0, 119, 181, 0.3), 0 0 0 1px rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
cursor: 'pointer',
'&:hover': {
transform: 'translateY(-2px) scale(1.05)',
boxShadow: '0 6px 20px rgba(0, 119, 181, 0.4), 0 0 0 1px rgba(255,255,255,0.2)',
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
/>
</motion.div>
{/* Disabled Social Media Chips */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<Chip
icon={<Twitter sx={{ fontSize: 16 }} />}
label="Twitter"
disabled
sx={{
height: 28,
minWidth: 100,
background: 'rgba(29, 161, 242, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(29, 161, 242, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.5 }}
>
<Chip
icon={<Instagram sx={{ fontSize: 16 }} />}
label="Instagram"
disabled
sx={{
height: 28,
minWidth: 100,
background: 'rgba(225, 48, 108, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(225, 48, 108, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.6 }}
>
<Chip
icon={<YouTube sx={{ fontSize: 16 }} />}
label="YouTube"
disabled
sx={{
height: 28,
minWidth: 100,
background: 'rgba(255, 0, 0, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(255, 0, 0, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, delay: 0.7 }}
>
<Chip
icon={<Article sx={{ fontSize: 16 }} />}
label="Wix/WordPress"
disabled
sx={{
height: 28,
minWidth: 100,
background: 'rgba(255, 107, 53, 0.1)',
color: 'rgba(255, 255, 255, 0.4)',
fontWeight: 600,
fontSize: '0.75rem',
border: '1px solid rgba(255, 107, 53, 0.2)',
opacity: 0.6,
}}
/>
</motion.div>
</>
)}
</AnimatePresence>
{/* Ellipsis indicator when not hovered */}
{!isHovered && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'rgba(255, 255, 255, 0.6)',
fontSize: '1.2rem',
animation: 'pulse 2s infinite',
'@keyframes pulse': {
'0%, 100%': { opacity: 0.6 },
'50%': { opacity: 1 },
},
}}
>
</Box>
)}
</Box>
);
};
export default PublishPillarChips;

View File

@@ -0,0 +1,354 @@
import React, { useState, useEffect } from 'react';
import {
Box,
Button,
IconButton,
Tooltip,
Typography,
Card,
CardContent,
Chip,
useTheme
} from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion';
import {
ArrowBack as BackIcon,
ArrowForward as ForwardIcon,
PlayArrow as PlayIcon,
Pause as PauseIcon,
SkipNext as SkipIcon,
CheckCircle as CompleteIcon,
Navigation as NavigationIcon
} from '@mui/icons-material';
import { useWorkflowStore } from '../../../stores/workflowStore';
import { taskNavigationService } from '../../../services/TaskNavigationService';
import { taskDependencyManager } from '../../../services/TaskDependencyManager';
interface TaskNavigationControlsProps {
compact?: boolean;
showTaskInfo?: boolean;
onTaskChange?: (taskId: string) => void;
}
const TaskNavigationControls: React.FC<TaskNavigationControlsProps> = ({
compact = false,
showTaskInfo = true,
onTaskChange
}) => {
const theme = useTheme();
const {
currentWorkflow,
navigationState,
moveToNextTask,
moveToPreviousTask,
completeTask,
skipTask,
isLoading
} = useWorkflowStore();
const [isNavigating, setIsNavigating] = useState(false);
const [navigationError, setNavigationError] = useState<string | null>(null);
// Navigation event listener
useEffect(() => {
const handleNavigationEvent = (event: any) => {
console.log('Navigation event:', event);
if (onTaskChange && event.detail?.taskId) {
onTaskChange(event.detail.taskId);
}
};
taskNavigationService.addNavigationListener(handleNavigationEvent);
return () => {
taskNavigationService.removeNavigationListener(handleNavigationEvent);
};
}, [onTaskChange]);
const handleNavigateToNext = async () => {
if (!currentWorkflow || !navigationState?.nextTask) return;
setIsNavigating(true);
setNavigationError(null);
try {
await moveToNextTask();
} catch (error) {
setNavigationError(error instanceof Error ? error.message : 'Navigation failed');
} finally {
setIsNavigating(false);
}
};
const handleNavigateBack = async () => {
if (!currentWorkflow || !navigationState?.canGoBack) return;
setIsNavigating(true);
setNavigationError(null);
try {
await moveToPreviousTask();
} catch (error) {
setNavigationError(error instanceof Error ? error.message : 'Back navigation failed');
} finally {
setIsNavigating(false);
}
};
const handleCompleteCurrentTask = async () => {
if (!currentWorkflow || !navigationState?.currentTask) return;
try {
await completeTask(navigationState.currentTask.id);
} catch (error) {
setNavigationError(error instanceof Error ? error.message : 'Task completion failed');
}
};
const handleSkipCurrentTask = async () => {
if (!currentWorkflow || !navigationState?.currentTask) return;
try {
await skipTask(navigationState.currentTask.id);
} catch (error) {
setNavigationError(error instanceof Error ? error.message : 'Task skip failed');
}
};
const getReadyTasks = () => {
if (!currentWorkflow) return [];
return taskDependencyManager.getReadyTasks(currentWorkflow);
};
const getBlockedTasks = () => {
if (!currentWorkflow) return [];
return taskDependencyManager.getBlockedTasks(currentWorkflow);
};
if (!currentWorkflow || !navigationState) {
return null;
}
const currentTask = navigationState.currentTask;
const readyTasks = getReadyTasks();
const blockedTasks = getBlockedTasks();
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Card
sx={{
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: 2
}}
>
<CardContent sx={{ p: compact ? 2 : 3 }}>
{/* Header */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
<NavigationIcon sx={{ color: theme.palette.primary.main }} />
<Typography variant="h6" sx={{ color: 'white', fontWeight: 600 }}>
Task Navigation
</Typography>
{navigationError && (
<Chip
label={navigationError}
color="error"
size="small"
sx={{ ml: 'auto' }}
/>
)}
</Box>
{/* Current Task Info */}
{showTaskInfo && currentTask && (
<Box sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.7)', mb: 1 }}>
Current Task:
</Typography>
<Box
sx={{
background: 'rgba(255,255,255,0.05)',
borderRadius: 1,
p: 2,
border: '1px solid rgba(255,255,255,0.1)'
}}
>
<Typography variant="subtitle1" sx={{ color: 'white', fontWeight: 600, mb: 0.5 }}>
{currentTask.title}
</Typography>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.8)' }}>
{currentTask.description}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mt: 1 }}>
<Chip
label={currentTask.pillarId}
size="small"
sx={{
background: `${currentTask.color}20`,
color: currentTask.color,
border: `1px solid ${currentTask.color}40`
}}
/>
<Chip
label={`${currentTask.estimatedTime} min`}
size="small"
sx={{
background: 'rgba(255,255,255,0.1)',
color: 'rgba(255,255,255,0.8)'
}}
/>
</Box>
</Box>
</Box>
)}
{/* Navigation Controls */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
{/* Back Button */}
<Tooltip title="Go to Previous Task">
<IconButton
onClick={handleNavigateBack}
disabled={!navigationState.canGoBack || isLoading || isNavigating}
sx={{
background: 'rgba(255,255,255,0.1)',
color: 'white',
'&:hover': {
background: 'rgba(255,255,255,0.2)'
},
'&:disabled': {
background: 'rgba(255,255,255,0.05)',
color: 'rgba(255,255,255,0.3)'
}
}}
>
<BackIcon />
</IconButton>
</Tooltip>
{/* Complete Task Button */}
<Button
variant="contained"
startIcon={<CompleteIcon />}
onClick={handleCompleteCurrentTask}
disabled={!currentTask || isLoading}
sx={{
background: '#4CAF50',
'&:hover': {
background: '#45a049'
},
flexGrow: 1
}}
>
Complete Task
</Button>
{/* Skip Task Button */}
<Tooltip title="Skip Current Task">
<IconButton
onClick={handleSkipCurrentTask}
disabled={!currentTask || isLoading}
sx={{
background: 'rgba(255,152,0,0.2)',
color: '#FF9800',
'&:hover': {
background: 'rgba(255,152,0,0.3)'
}
}}
>
<SkipIcon />
</IconButton>
</Tooltip>
{/* Forward Button */}
<Tooltip title="Go to Next Task">
<IconButton
onClick={handleNavigateToNext}
disabled={!navigationState.canGoForward || isLoading || isNavigating}
sx={{
background: 'rgba(255,255,255,0.1)',
color: 'white',
'&:hover': {
background: 'rgba(255,255,255,0.2)'
},
'&:disabled': {
background: 'rgba(255,255,255,0.05)',
color: 'rgba(255,255,255,0.3)'
}
}}
>
<ForwardIcon />
</IconButton>
</Tooltip>
</Box>
{/* Task Status Summary */}
{!compact && (
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
<Chip
label={`${readyTasks.length} Ready`}
size="small"
sx={{
background: 'rgba(76,175,80,0.2)',
color: '#4CAF50',
border: '1px solid rgba(76,175,80,0.3)'
}}
/>
<Chip
label={`${blockedTasks.length} Blocked`}
size="small"
sx={{
background: 'rgba(244,67,54,0.2)',
color: '#F44336',
border: '1px solid rgba(244,67,54,0.3)'
}}
/>
<Chip
label={`${currentWorkflow.completedTasks}/${currentWorkflow.totalTasks} Complete`}
size="small"
sx={{
background: 'rgba(33,150,243,0.2)',
color: '#2196F3',
border: '1px solid rgba(33,150,243,0.3)'
}}
/>
</Box>
)}
{/* Loading State */}
<AnimatePresence>
{(isLoading || isNavigating) && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'rgba(0,0,0,0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'inherit'
}}
>
<Typography variant="body2" sx={{ color: 'white' }}>
{isNavigating ? 'Navigating...' : 'Loading...'}
</Typography>
</motion.div>
)}
</AnimatePresence>
</CardContent>
</Card>
</motion.div>
);
};
export default TaskNavigationControls;

View File

@@ -0,0 +1,496 @@
import React, { useState } from 'react';
import {
Box,
Card,
CardContent,
Typography,
Button,
Chip,
Stack,
Alert,
AlertTitle,
Divider,
IconButton,
Tooltip,
Collapse,
LinearProgress,
Paper,
Grid
} from '@mui/material';
import {
PlayArrow,
Pause,
Stop,
Info,
ExpandMore,
ExpandLess,
CheckCircle,
Schedule,
TrendingUp,
NavigateNext,
NavigateBefore,
SkipNext,
TaskAlt,
Timer,
Assignment
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { useWorkflowStore } from '../../../stores/workflowStore';
interface WorkflowDemoProps {
compact?: boolean;
}
const WorkflowDemo: React.FC<WorkflowDemoProps> = ({ compact = false }) => {
const [expanded, setExpanded] = useState(false);
const {
currentWorkflow,
workflowProgress,
navigationState,
isLoading,
generateDailyWorkflow,
startWorkflow,
completeTask,
skipTask,
moveToNextTask,
moveToPreviousTask,
isWorkflowComplete
} = useWorkflowStore();
const handleGenerateWorkflow = async () => {
try {
await generateDailyWorkflow('demo-user');
} catch (error) {
console.error('Failed to generate workflow:', error);
}
};
const handleStartWorkflow = async () => {
if (currentWorkflow) {
try {
await startWorkflow(currentWorkflow.id);
} catch (error) {
console.error('Failed to start workflow:', error);
}
}
};
const handleCompleteTask = async (taskId: string) => {
try {
await completeTask(taskId);
} catch (error) {
console.error('Failed to complete task:', error);
}
};
const handleSkipTask = async (taskId: string) => {
try {
await skipTask(taskId);
} catch (error) {
console.error('Failed to skip task:', error);
}
};
const handleNextTask = async () => {
try {
await moveToNextTask();
} catch (error) {
console.error('Failed to move to next task:', error);
}
};
const handlePreviousTask = async () => {
try {
await moveToPreviousTask();
} catch (error) {
console.error('Failed to move to previous task:', error);
}
};
const isComplete = isWorkflowComplete();
const hasWorkflow = !!currentWorkflow;
const isInProgress = currentWorkflow?.workflowStatus === 'in_progress';
if (compact) {
return (
<Card sx={{
background: 'linear-gradient(135deg, rgba(25, 118, 210, 0.1) 0%, rgba(25, 118, 210, 0.05) 100%)',
border: '1px solid rgba(25, 118, 210, 0.2)',
borderRadius: 2,
mb: 2
}}>
<CardContent sx={{ p: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Schedule color="primary" />
<Typography variant="h6" color="primary">
Today's Workflow
</Typography>
{hasWorkflow && (
<Chip
label={isComplete ? 'Complete' : isInProgress ? 'In Progress' : 'Ready'}
color={isComplete ? 'success' : isInProgress ? 'primary' : 'default'}
size="small"
/>
)}
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
{!hasWorkflow && (
<Button
variant="contained"
size="small"
startIcon={<PlayArrow />}
onClick={handleGenerateWorkflow}
disabled={isLoading}
>
Generate
</Button>
)}
{hasWorkflow && !isInProgress && !isComplete && (
<Button
variant="contained"
size="small"
startIcon={<PlayArrow />}
onClick={handleStartWorkflow}
disabled={isLoading}
>
Start
</Button>
)}
<IconButton
size="small"
onClick={() => setExpanded(!expanded)}
>
{expanded ? <ExpandLess /> : <ExpandMore />}
</IconButton>
</Box>
</Box>
<Collapse in={expanded}>
<Box sx={{ mt: 2 }}>
{workflowProgress && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" color="text.secondary">
Progress: {workflowProgress.completedTasks} of {workflowProgress.totalTasks} tasks
</Typography>
</Box>
)}
{currentWorkflow && (
<Stack spacing={1}>
{currentWorkflow.tasks.slice(0, 3).map((task) => (
<Box
key={task.id}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
p: 1,
background: 'rgba(255, 255, 255, 0.05)',
borderRadius: 1,
border: '1px solid rgba(255, 255, 255, 0.1)'
}}
>
<Box>
<Typography variant="body2" fontWeight="medium">
{task.title}
</Typography>
<Typography variant="caption" color="text.secondary">
{task.estimatedTime} min • {task.priority}
</Typography>
</Box>
<Box sx={{ display: 'flex', gap: 0.5 }}>
{task.status === 'pending' && isInProgress && (
<>
<Tooltip title="Complete Task">
<IconButton
size="small"
onClick={() => handleCompleteTask(task.id)}
sx={{ color: 'success.main' }}
>
<CheckCircle fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="Skip Task">
<IconButton
size="small"
onClick={() => handleSkipTask(task.id)}
sx={{ color: 'warning.main' }}
>
<Stop fontSize="small" />
</IconButton>
</Tooltip>
</>
)}
{task.status === 'completed' && (
<CheckCircle color="success" fontSize="small" />
)}
{task.status === 'skipped' && (
<Stop color="warning" fontSize="small" />
)}
</Box>
</Box>
))}
</Stack>
)}
</Box>
</Collapse>
</CardContent>
</Card>
);
}
const getStatusColor = () => {
if (isComplete) return 'success';
if (isInProgress) return 'primary';
return 'default';
};
const getStatusText = () => {
if (isComplete) return 'Workflow Complete! 🎉';
if (isInProgress) return 'In Progress';
if (!hasWorkflow) return 'No Workflow Generated';
return 'Ready to Start';
};
return (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Box
sx={{
background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)',
backdropFilter: 'blur(10px)',
borderRadius: 3,
p: 3,
border: '1px solid rgba(255,255,255,0.1)',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
mb: 3,
overflow: 'hidden'
}}
>
{/* Header Section */}
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Typography
variant="h5"
sx={{
fontWeight: 700,
color: 'white',
display: 'flex',
alignItems: 'center',
gap: 1
}}
>
{isComplete ? <CheckCircle sx={{ color: 'success.main' }} /> :
isInProgress ? <TrendingUp sx={{ color: 'primary.main' }} /> :
<Schedule sx={{ color: 'grey.400' }} />}
Today's Marketing Workflow
</Typography>
<Chip
label={getStatusText()}
size="small"
color={getStatusColor()}
sx={{
background: `${getStatusColor() === 'success' ? 'success.main' : getStatusColor() === 'primary' ? 'primary.main' : 'grey.500'}20`,
color: getStatusColor() === 'success' ? 'success.main' : getStatusColor() === 'primary' ? 'primary.main' : 'grey.500',
border: `1px solid ${getStatusColor() === 'success' ? 'success.main' : getStatusColor() === 'primary' ? 'primary.main' : 'grey.500'}40`,
fontWeight: 600
}}
/>
</Box>
</Box>
{/* Current Task Navigation */}
{navigationState?.currentTask && isInProgress && (
<Box sx={{
p: 2,
mb: 3,
background: 'rgba(76, 175, 80, 0.1)',
border: '1px solid rgba(76, 175, 80, 0.3)',
borderRadius: 2,
backdropFilter: 'blur(10px)'
}}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
<TaskAlt color="success" />
<Typography variant="h6" color="success.main" sx={{ fontWeight: 600 }}>
Current Task
</Typography>
</Box>
<Typography variant="subtitle1" fontWeight="medium" sx={{ mb: 1, color: 'white' }}>
{navigationState.currentTask.title}
</Typography>
<Typography variant="body2" sx={{ mb: 2, color: 'rgba(255,255,255,0.7)' }}>
{navigationState.currentTask.description}
</Typography>
{/* Navigation Controls */}
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
<Button
variant="contained"
size="small"
startIcon={<CheckCircle />}
onClick={() => navigationState.currentTask && handleCompleteTask(navigationState.currentTask.id)}
sx={{ background: 'linear-gradient(135deg, #4caf50 0%, #388e3c 100%)' }}
>
Complete
</Button>
<Button
variant="outlined"
size="small"
startIcon={<SkipNext />}
onClick={() => navigationState.currentTask && handleSkipTask(navigationState.currentTask.id)}
sx={{ borderColor: 'warning.main', color: 'warning.main' }}
>
Skip
</Button>
<Button
variant="outlined"
size="small"
startIcon={<NavigateNext />}
onClick={handleNextTask}
disabled={!navigationState.canGoForward}
sx={{ borderColor: 'primary.main', color: 'primary.main' }}
>
Next
</Button>
<Button
variant="outlined"
size="small"
startIcon={<NavigateBefore />}
onClick={handlePreviousTask}
disabled={!navigationState.canGoBack}
sx={{ borderColor: 'primary.main', color: 'primary.main' }}
>
Previous
</Button>
</Box>
</Box>
)}
{/* Task List */}
{currentWorkflow && (
<Box>
<Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'white', fontWeight: 600 }}>
<Assignment color="primary" />
Today's Tasks
</Typography>
<Grid container spacing={2}>
<AnimatePresence>
{currentWorkflow.tasks.map((task, index) => (
<Grid item xs={12} md={6} key={task.id}>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
>
<Box
sx={{
p: 2,
background: task.id === navigationState?.currentTask?.id
? 'rgba(76, 175, 80, 0.1)'
: 'rgba(255, 255, 255, 0.05)',
border: task.id === navigationState?.currentTask?.id
? '2px solid rgba(76, 175, 80, 0.5)'
: '1px solid rgba(255, 255, 255, 0.1)',
borderRadius: 2,
height: '100%',
backdropFilter: 'blur(10px)'
}}
>
<Box sx={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="subtitle1" fontWeight="medium" sx={{ flexGrow: 1, color: 'white' }}>
{task.title}
</Typography>
{task.id === navigationState?.currentTask?.id && (
<Chip label="Current" color="success" size="small" />
)}
</Box>
<Typography variant="body2" sx={{ mb: 2, color: 'rgba(255,255,255,0.7)' }}>
{task.description}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mb: 2, flexWrap: 'wrap' }}>
<Chip
label={`${task.estimatedTime} min`}
size="small"
variant="outlined"
icon={<Timer />}
/>
<Chip
label={task.priority}
size="small"
color={task.priority === 'high' ? 'error' : task.priority === 'medium' ? 'warning' : 'default'}
/>
<Chip
label={task.status}
size="small"
color={task.status === 'completed' ? 'success' : task.status === 'skipped' ? 'warning' : 'default'}
/>
</Box>
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'flex-end' }}>
{task.status === 'pending' && isInProgress && (
<>
<Tooltip title="Complete Task">
<IconButton
size="small"
onClick={() => handleCompleteTask(task.id)}
sx={{
color: 'success.main',
'&:hover': { background: 'rgba(76, 175, 80, 0.1)' }
}}
>
<CheckCircle />
</IconButton>
</Tooltip>
<Tooltip title="Skip Task">
<IconButton
size="small"
onClick={() => handleSkipTask(task.id)}
sx={{
color: 'warning.main',
'&:hover': { background: 'rgba(255, 152, 0, 0.1)' }
}}
>
<Stop />
</IconButton>
</Tooltip>
</>
)}
{task.status === 'completed' && (
<CheckCircle color="success" />
)}
{task.status === 'skipped' && (
<Stop color="warning" />
)}
</Box>
</Box>
</motion.div>
</Grid>
))}
</AnimatePresence>
</Grid>
</Box>
)}
{/* Help Section */}
{!hasWorkflow && (
<Alert severity="info" sx={{ mt: 3, background: 'rgba(33, 150, 243, 0.1)', border: '1px solid rgba(33, 150, 243, 0.3)' }}>
<AlertTitle>Getting Started</AlertTitle>
Generate today's workflow to see your personalized marketing tasks. The system will guide you through each task with clear instructions and navigation controls.
</Alert>
)}
</Box>
</motion.div>
);
};
export default WorkflowDemo;

View File

@@ -0,0 +1,251 @@
import React from 'react';
import {
Box,
Typography,
LinearProgress,
Chip,
IconButton,
Tooltip,
useTheme
} from '@mui/material';
import { motion } from 'framer-motion';
import {
PlayArrow,
Pause,
CheckCircle,
Schedule,
TrendingUp
} from '@mui/icons-material';
import { useWorkflowStore } from '../../../stores/workflowStore';
interface WorkflowProgressBarProps {
onStartWorkflow?: () => void;
onPauseWorkflow?: () => void;
onResumeWorkflow?: () => void;
showControls?: boolean;
compact?: boolean;
}
const WorkflowProgressBar: React.FC<WorkflowProgressBarProps> = ({
onStartWorkflow,
onPauseWorkflow,
onResumeWorkflow,
showControls = true,
compact = false
}) => {
const theme = useTheme();
const {
currentWorkflow,
workflowProgress,
navigationState,
isLoading,
startWorkflow,
isWorkflowComplete,
getCompletionPercentage,
generateDailyWorkflow
} = useWorkflowStore();
const completionPercentage = getCompletionPercentage();
const isComplete = isWorkflowComplete();
const currentTask = navigationState?.currentTask;
// Always show the progress bar, even if no workflow exists yet
const handleStartWorkflow = async () => {
try {
if (currentWorkflow) {
await startWorkflow(currentWorkflow.id);
onStartWorkflow?.();
} else {
// Generate a new workflow if none exists
await generateDailyWorkflow('demo-user');
onStartWorkflow?.();
}
} catch (error) {
console.error('Failed to start workflow:', error);
}
};
const getStatusColor = () => {
if (isComplete) return theme.palette.success.main;
if (currentWorkflow?.workflowStatus === 'in_progress') return theme.palette.primary.main;
return theme.palette.grey[500];
};
const getStatusText = () => {
if (isComplete) return 'Workflow Complete! 🎉';
if (currentWorkflow?.workflowStatus === 'in_progress') return 'In Progress';
if (!currentWorkflow) return 'No Workflow Generated';
return 'Ready to Start';
};
return (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Box
sx={{
background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)',
backdropFilter: 'blur(10px)',
borderRadius: 2,
p: compact ? 2 : 3,
border: '1px solid rgba(255,255,255,0.1)',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
mb: 3
}}
>
{/* Header */}
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Typography
variant={compact ? "h6" : "h5"}
sx={{
fontWeight: 700,
color: 'white',
display: 'flex',
alignItems: 'center',
gap: 1
}}
>
{isComplete ? <CheckCircle sx={{ color: theme.palette.success.main }} /> :
currentWorkflow?.workflowStatus === 'in_progress' ? <TrendingUp sx={{ color: theme.palette.primary.main }} /> :
<Schedule sx={{ color: theme.palette.grey[400] }} />}
Today's Marketing Workflow
</Typography>
<Chip
label={getStatusText()}
size="small"
sx={{
background: `${getStatusColor()}20`,
color: getStatusColor(),
border: `1px solid ${getStatusColor()}40`,
fontWeight: 600
}}
/>
</Box>
{/* Controls */}
{showControls && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{(currentWorkflow?.workflowStatus === 'not_started' || !currentWorkflow) && (
<Tooltip title={currentWorkflow ? "Start Today's Workflow" : "Generate & Start Workflow"}>
<IconButton
onClick={handleStartWorkflow}
disabled={isLoading}
sx={{
background: theme.palette.primary.main,
color: 'white',
'&:hover': {
background: theme.palette.primary.dark,
}
}}
>
<PlayArrow />
</IconButton>
</Tooltip>
)}
{currentWorkflow?.workflowStatus === 'in_progress' && (
<Tooltip title="Pause Workflow">
<IconButton
onClick={onPauseWorkflow}
disabled={isLoading}
sx={{
background: theme.palette.warning.main,
color: 'white',
'&:hover': {
background: theme.palette.warning.dark,
}
}}
>
<Pause />
</IconButton>
</Tooltip>
)}
</Box>
)}
</Box>
{/* Progress Bar */}
<Box sx={{ mb: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.8)' }}>
Progress
</Typography>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.8)' }}>
{workflowProgress?.completedTasks || 0} of {workflowProgress?.totalTasks || 0} tasks
</Typography>
</Box>
<LinearProgress
variant="determinate"
value={currentWorkflow ? completionPercentage : 0}
sx={{
height: 8,
borderRadius: 4,
background: 'rgba(255,255,255,0.1)',
'& .MuiLinearProgress-bar': {
background: isComplete
? `linear-gradient(90deg, ${theme.palette.success.main} 0%, ${theme.palette.success.light} 100%)`
: `linear-gradient(90deg, ${theme.palette.primary.main} 0%, ${theme.palette.primary.light} 100%)`,
borderRadius: 4,
boxShadow: `0 0 10px ${isComplete ? theme.palette.success.main : theme.palette.primary.main}40`
}
}}
/>
<Typography
variant="caption"
sx={{
color: 'rgba(255,255,255,0.6)',
mt: 0.5,
display: 'block',
textAlign: 'right'
}}
>
{currentWorkflow ? `${completionPercentage}% complete` : 'No workflow active'}
</Typography>
</Box>
{/* Current Task Info */}
{currentTask && !isComplete && (
<Box
sx={{
background: 'rgba(255,255,255,0.05)',
borderRadius: 1,
p: 2,
border: '1px solid rgba(255,255,255,0.1)'
}}
>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.7)', mb: 0.5 }}>
Current Task:
</Typography>
<Typography variant="body1" sx={{ color: 'white', fontWeight: 600 }}>
{currentTask.title}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
{currentTask.description}
</Typography>
</Box>
)}
{/* Time Information */}
{workflowProgress && (
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 2 }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
Time Spent: {workflowProgress.actualTimeSpent} min
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
Est. Remaining: {workflowProgress.estimatedTimeRemaining} min
</Typography>
</Box>
)}
</Box>
</motion.div>
);
};
export default WorkflowProgressBar;

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { Box, Typography, Chip } from '@mui/material';
import { Box, Typography, Chip, Button, CircularProgress } from '@mui/material';
import { PlayArrow, Pause, Stop } from '@mui/icons-material';
import { ShimmerHeader } from './styled';
import { DashboardHeaderProps } from './types';
@@ -7,32 +8,226 @@ const DashboardHeader: React.FC<DashboardHeaderProps> = ({
title,
subtitle,
statusChips = [],
rightContent
rightContent,
customIcon,
workflowControls
}) => {
return (
<ShimmerHeader sx={{ mb: 5 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 3 }}>
<Box>
<Typography variant="h2" component="h1" sx={{
fontWeight: 800,
color: 'white',
textShadow: '0 4px 8px rgba(0,0,0,0.3)',
mb: 1,
fontSize: { xs: '2rem', md: '3rem' },
background: 'linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%)',
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}>
{title}
</Typography>
<Typography variant="h5" sx={{
color: 'rgba(255, 255, 255, 0.9)',
fontWeight: 400,
fontSize: { xs: '1rem', md: '1.25rem' },
}}>
{subtitle}
</Typography>
<ShimmerHeader sx={{ mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
{customIcon && (
<Box
component="img"
src={customIcon}
alt="Alwrity Logo"
sx={{
width: { xs: 40, md: 48 },
height: { xs: 40, md: 48 },
filter: 'drop-shadow(0 4px 8px rgba(0,0,0,0.3))',
}}
/>
)}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Box>
<Typography variant="h2" component="h1" sx={{
fontWeight: 800,
color: 'white',
textShadow: '0 4px 8px rgba(0,0,0,0.3)',
mb: subtitle ? 1 : 0,
fontSize: { xs: '2rem', md: '3rem' },
background: 'linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%)',
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}>
{title}
</Typography>
{subtitle && (
<Typography variant="h5" sx={{
color: 'rgba(255, 255, 255, 0.9)',
fontWeight: 400,
fontSize: { xs: '1rem', md: '1.25rem' },
}}>
{subtitle}
</Typography>
)}
</Box>
{/* Workflow Controls */}
{workflowControls && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{/* Workflow Control Buttons */}
{!workflowControls.isWorkflowActive ? (
/* Start Button with Badge and Lightning Glow */
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<Button
variant="contained"
size="small"
startIcon={<PlayArrow />}
onClick={workflowControls.onStartWorkflow}
disabled={workflowControls.isLoading}
sx={{
position: 'relative',
overflow: 'hidden',
background: 'linear-gradient(135deg, #4caf50 0%, #388e3c 100%)',
border: '2px solid transparent',
'&:hover': {
background: 'linear-gradient(135deg, #388e3c 0%, #2e7d32 100%)',
},
minWidth: 'auto',
px: 2,
'&::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: -2,
left: -2,
right: -2,
bottom: -2,
background: 'linear-gradient(45deg, #4caf50, #8bc34a, #4caf50, #8bc34a)',
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%' },
},
}}
>
Start
</Button>
<Box
sx={{
position: 'absolute',
top: -8,
right: -8,
backgroundColor: '#1976d2',
color: 'white',
borderRadius: '12px',
px: 0.75,
py: 0.25,
fontSize: '0.65rem',
fontWeight: 700,
boxShadow: '0 2px 6px rgba(0,0,0,0.3)'
}}
>
{`${workflowControls.completedTasks}/${workflowControls.totalTasks}`}
</Box>
</Box>
) : (
/* In-Progress/Completed Controls with Enhanced Styling */
<Box sx={{ display: 'flex', gap: 1 }}>
{/* In-Progress/Completed Status with Lightning Glow */}
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<Button
variant="contained"
size="small"
onClick={workflowControls.onResumePlanModal}
disabled={workflowControls.isLoading}
sx={{
position: 'relative',
overflow: 'hidden',
background: workflowControls.completedTasks === workflowControls.totalTasks
? 'linear-gradient(135deg, #4caf50 0%, #388e3c 100%)'
: 'linear-gradient(135deg, #2196f3 0%, #1976d2 100%)',
color: 'white',
minWidth: 'auto',
px: 2,
border: '2px solid transparent',
boxShadow: workflowControls.completedTasks === workflowControls.totalTasks
? '0 8px 25px rgba(76, 175, 80, 0.4), 0 0 0 1px rgba(255,255,255,0.2)'
: '0 8px 25px rgba(33, 150, 243, 0.4), 0 0 0 1px rgba(255,255,255,0.2)',
'&:hover': {
background: workflowControls.completedTasks === workflowControls.totalTasks
? 'linear-gradient(135deg, #388e3c 0%, #2e7d32 100%)'
: 'linear-gradient(135deg, #1976d2 0%, #1565c0 100%)',
transform: 'translateY(-2px)',
boxShadow: workflowControls.completedTasks === workflowControls.totalTasks
? '0 12px 35px rgba(76, 175, 80, 0.6), 0 0 0 1px rgba(255,255,255,0.3)'
: '0 12px 35px rgba(33, 150, 243, 0.6), 0 0 0 1px rgba(255,255,255,0.3)',
},
'&::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: -2,
left: -2,
right: -2,
bottom: -2,
background: workflowControls.completedTasks === workflowControls.totalTasks
? 'linear-gradient(45deg, #4caf50, #8bc34a, #4caf50, #8bc34a)'
: 'linear-gradient(45deg, #2196f3, #64b5f6, #2196f3, #64b5f6)',
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)',
}}
title={workflowControls.completedTasks === workflowControls.totalTasks
? '🎉 All tasks completed! Click to review workflow progress.'
: 'Workflow in progress. Click to resume or check current tasks.'}
>
{workflowControls.completedTasks === workflowControls.totalTasks ? 'Completed' : 'In Progress'}
</Button>
<Box
sx={{
position: 'absolute',
top: -8,
right: -8,
backgroundColor: '#1976d2',
color: 'white',
borderRadius: '12px',
px: 0.75,
py: 0.25,
fontSize: '0.65rem',
fontWeight: 700,
boxShadow: '0 2px 6px rgba(0,0,0,0.3)'
}}
>
{`${workflowControls.completedTasks}/${workflowControls.totalTasks}`}
</Box>
</Box>
</Box>
)}
</Box>
)}
</Box>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
{statusChips.length > 0 && (

View File

@@ -26,6 +26,15 @@ const SearchFilter: React.FC<SearchFilterProps> = ({
toolCategories,
theme
}) => {
// Helper function to get tool count from a category
const getToolCount = (category: any): number => {
if ('tools' in category) {
return category.tools.length;
} else if ('subCategories' in category) {
return Object.values(category.subCategories).reduce((total: number, subCat: any) => total + subCat.tools.length, 0);
}
return 0;
};
return (
<SearchContainer>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mb: 3 }}>
@@ -74,13 +83,14 @@ const SearchFilter: React.FC<SearchFilterProps> = ({
</Tooltip>
</Box>
{/* Enhanced Category Filter */}
{/* 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) => (
<CategoryChip
@@ -89,6 +99,7 @@ const SearchFilter: React.FC<SearchFilterProps> = ({
onClick={() => onCategoryChange(category)}
active={selectedCategory === category}
theme={theme}
toolCount={getToolCount(toolCategories[category])}
/>
))}
</Box>

View File

@@ -96,15 +96,24 @@ export const SearchContainer = styled(Box)(({ theme }) => ({
}));
export const CategoryChip = styled(Chip, {
shouldForwardProp: (prop) => prop !== 'active',
})<{ active?: boolean }>(({ theme, active }) => ({
background: active ? 'rgba(255, 255, 255, 0.25)' : 'rgba(255, 255, 255, 0.1)',
shouldForwardProp: (prop) => prop !== 'active' && prop !== 'toolCount',
})<{ active?: boolean; toolCount?: number }>(({ theme, active, toolCount }) => ({
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)',
color: 'white',
fontWeight: 600,
fontWeight: active ? 700 : 600,
fontSize: '0.9rem',
padding: theme.spacing(1, 2),
border: `1px solid ${active ? 'rgba(255, 255, 255, 0.4)' : 'rgba(255, 255, 255, 0.2)'}`,
border: active
? '2px solid rgba(255, 255, 255, 0.6)'
: '1px solid rgba(255, 255, 255, 0.2)',
boxShadow: active
? '0 6px 20px rgba(255, 255, 255, 0.2), 0 0 0 1px rgba(255,255,255,0.1)'
: 'none',
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)',
transform: 'translateY(-2px)',
@@ -112,7 +121,29 @@ export const CategoryChip = styled(Chip, {
},
'& .MuiChip-label': {
padding: theme.spacing(0.5, 1),
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
},
// Tool count badge
...(toolCount && {
'&::after': {
content: `"${toolCount}"`,
position: 'absolute',
top: -6,
right: -6,
backgroundColor: active ? '#4caf50' : 'rgba(255, 255, 255, 0.8)',
color: active ? 'white' : 'rgba(0, 0, 0, 0.8)',
borderRadius: '10px',
padding: '2px 6px',
fontSize: '0.7rem',
fontWeight: 700,
minWidth: '18px',
textAlign: 'center',
boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
border: active ? '1px solid rgba(255,255,255,0.3)' : '1px solid rgba(0,0,0,0.1)',
},
}),
}));
export const EnhancedGlassCard = styled(GlassCard)(({ theme }) => ({

View File

@@ -77,13 +77,24 @@ export interface SearchFilterProps {
export interface DashboardHeaderProps {
title: string;
subtitle: string;
subtitle?: string;
statusChips?: Array<{
label: string;
color: string;
icon: React.ReactElement;
}>;
rightContent?: React.ReactNode;
customIcon?: string;
workflowControls?: {
onStartWorkflow: () => void;
onPauseWorkflow?: () => void;
onStopWorkflow?: () => void;
onResumePlanModal?: () => void;
isWorkflowActive: boolean;
completedTasks: number;
totalTasks: number;
isLoading: boolean;
};
}
export interface LoadingSkeletonProps {