Alwrity version 0.5.4

This commit is contained in:
ajaysi
2025-08-12 22:35:21 +05:30
parent 39b96c44da
commit 66ece49705
44 changed files with 9577 additions and 2112 deletions

View File

@@ -50,7 +50,8 @@ import {
Lightbulb as LightbulbIcon,
Psychology as PsychologyIcon,
Timeline as TimelineIcon,
FiberManualRecord as FiberManualRecordIcon
FiberManualRecord as FiberManualRecordIcon,
Schedule as ScheduleIcon
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { useEnhancedStrategyStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/enhancedStrategyStore';
@@ -63,6 +64,7 @@ import DataSourceTransparency from './DataSourceTransparency';
import { useCategoryReview } from './ContentStrategyBuilder/hooks/useCategoryReview';
import { useProgressTracking } from './ContentStrategyBuilder/hooks/useProgressTracking';
import { useAutoPopulation } from './ContentStrategyBuilder/hooks/useAutoPopulation';
import { useActionButtonsBusinessLogic } from './ContentStrategyBuilder/components/ActionButtons';
// Import extracted utilities
import { getCategoryIcon, getCategoryColor, getCategoryName, getCategoryStatus } from './ContentStrategyBuilder/utils/categoryHelpers';
@@ -72,7 +74,12 @@ import { getEducationalContent } from './ContentStrategyBuilder/utils/educationa
import CategoryList from './ContentStrategyBuilder/components/CategoryList';
import ProgressTracker from './ContentStrategyBuilder/components/ProgressTracker';
import HeaderSection from './ContentStrategyBuilder/components/HeaderSection';
import EducationalModal from './ContentStrategyBuilder/components/EducationalModal';
import ActionButtons from './ContentStrategyBuilder/components/ActionButtons';
import StrategyDisplay from './ContentStrategyBuilder/components/StrategyDisplay';
import ErrorAlert from './ContentStrategyBuilder/components/ErrorAlert';
import { contentPlanningApi } from '../../../services/contentPlanningApi';
import CategoryDetailView from './ContentStrategyBuilder/components/CategoryDetailView';
const ContentStrategyBuilder: React.FC = () => {
const {
@@ -155,6 +162,25 @@ const ContentStrategyBuilder: React.FC = () => {
completionStats
});
// Use ActionButtons business logic hook
const { handleCreateStrategy, handleSaveStrategy } = useActionButtonsBusinessLogic({
formData,
error,
currentStrategy,
setAIGenerating,
setError,
setCurrentStrategy,
setSaving,
setGenerationProgress,
setEducationalContent,
setShowEducationalModal,
validateAllFields,
getCompletionStats,
generateAIRecommendations,
createEnhancedStrategy,
contentPlanningApi
});
// Auto-populate from onboarding on first load
useEffect(() => {
if (!autoPopulateAttempted) {
@@ -211,294 +237,6 @@ const ContentStrategyBuilder: React.FC = () => {
};
}, []);
const handleCreateStrategy = async () => {
try {
setAIGenerating(true);
setError(null);
console.log('Starting strategy creation...');
console.log('Current formData:', formData);
console.log('FormData ID:', formData.id);
// If we have a saved strategy, use its ID
if (formData.id) {
console.log('Using existing strategy ID:', formData.id);
await generateAIRecommendations(formData.id);
} else {
console.log('No strategy ID found, creating new strategy...');
// If no strategy is saved yet, save it first, then generate AI insights
const isValid = validateAllFields();
console.log('Form validation result:', isValid);
if (isValid) {
const completionStats = getCompletionStats();
const strategyData = {
...formData,
completion_percentage: completionStats.completion_percentage,
user_id: 1, // This would come from auth context
name: formData.name || 'Enhanced Content Strategy',
industry: formData.industry || 'General'
};
console.log('Attempting to create strategy with data:', strategyData);
// Use SSE streaming endpoint for strategy generation with educational content
await generateStrategyWithSSE(strategyData);
} else {
setError('Please fill in all required fields before generating AI insights.');
console.error('Form validation failed. Cannot generate AI insights.');
}
}
} catch (err: any) {
setError(`Error generating AI recommendations: ${err.message || 'Unknown error'}`);
console.error('Error in handleCreateStrategy:', err);
} finally {
setAIGenerating(false);
}
};
const generateStrategyWithSSE = async (strategyData: any) => {
try {
console.log('🚀 Starting SSE strategy generation...');
// Initialize progress and educational content
setGenerationProgress(0);
setEducationalContent({
title: '🤖 AI-Powered Strategy Generation',
description: 'Initializing AI analysis and preparing educational content...',
details: [
'🔧 Setting up AI services',
'📊 Loading user context',
'🎯 Preparing strategy framework',
'📚 Generating educational content'
],
insight: 'We\'re getting everything ready for your personalized AI strategy generation.',
estimated_time: '2-3 minutes total'
});
// Show educational modal
setShowEducationalModal(true);
// Create basic strategy first
const newStrategy = await createEnhancedStrategy(strategyData);
console.log('Basic strategy created:', newStrategy);
if (newStrategy && newStrategy.id) {
console.log('Starting AI generation for strategy ID:', newStrategy.id);
// Set a timeout for the entire process (5 minutes)
const processTimeout = setTimeout(async () => {
console.error('⏰ Strategy generation timeout after 5 minutes');
// Try to check if the strategy was actually created
try {
const existingStrategy = await contentPlanningApi.getEnhancedStrategy(newStrategy.id.toString());
if (existingStrategy) {
console.log('✅ Strategy was created successfully despite SSE timeout');
setCurrentStrategy(existingStrategy);
setError('Strategy created successfully! The AI generation may still be running in the background. Check the Strategic Intelligence tab for detailed insights.');
} else {
setError('Strategy generation is taking longer than expected. The process may still be running in the background. Please check the Strategic Intelligence tab for results.');
}
} catch (checkError) {
console.error('Error checking strategy status:', checkError);
setError('Strategy generation is taking longer than expected. The process may still be running in the background. Please check the Strategic Intelligence tab for results.');
}
setShowEducationalModal(false);
}, 5 * 60 * 1000); // 5 minutes
// Add heartbeat monitoring
let lastMessageTime = Date.now();
const heartbeatInterval = setInterval(() => {
const timeSinceLastMessage = Date.now() - lastMessageTime;
if (timeSinceLastMessage > 30000) { // 30 seconds without message
console.warn('⚠️ No SSE messages received for 30 seconds');
setEducationalContent({
title: '🤖 AI-Powered Strategy Generation',
description: 'AI analysis is still running in the background. This may take a few more minutes.',
details: [
'⏳ Processing complex AI analysis',
'📊 Analyzing market data',
'🎯 Generating strategic insights',
'📈 Calculating performance predictions'
],
insight: 'The AI is working on comprehensive analysis. This is normal for complex strategies.',
estimated_time: 'Additional 1-2 minutes'
});
}
}, 10000); // Check every 10 seconds
// Use SSE endpoint for AI generation with educational content
const eventSource = await contentPlanningApi.streamStrategyGeneration(Number(newStrategy.id));
console.log('🔌 SSE EventSource created:', eventSource);
console.log('🔌 SSE readyState:', eventSource.readyState);
// Handle SSE data with proper parsing
eventSource.onmessage = (event) => {
try {
console.log('📨 Raw SSE message:', event.data);
// Update last message time for heartbeat
lastMessageTime = Date.now();
// Parse the SSE data
const data = JSON.parse(event.data);
console.log('📨 Parsed SSE data:', data);
console.log('📨 Message type analysis:', {
hasStep: data.step !== undefined,
hasProgress: data.progress !== undefined,
hasEducationalContent: !!data.educational_content,
hasError: !!data.error,
hasSuccess: !!data.success,
hasType: !!data.type,
step: data.step,
progress: data.progress,
message: data.message
});
// Handle different types of messages
if (data.error) {
console.error('❌ SSE Error:', data.error);
clearTimeout(processTimeout);
clearInterval(heartbeatInterval);
setError(`AI generation failed: ${data.error}`);
setShowEducationalModal(false);
eventSource.close();
return;
}
// Handle step and progress updates (backend sends these)
if (data.step !== undefined) {
console.log('🔢 Updating step:', data.step);
// Calculate progress from step (each step is 10%)
const stepProgress = Math.min(data.step * 10, 100);
console.log('📊 Calculated progress from step:', stepProgress);
setGenerationProgress(stepProgress);
}
// Handle explicit progress updates
if (data.progress !== undefined) {
console.log('📊 Updating progress:', data.progress);
setGenerationProgress(data.progress);
}
// Handle educational content updates
if (data.educational_content) {
console.log('📚 Updating educational content:', data.educational_content);
setEducationalContent(data.educational_content);
}
// Handle completion
if (data.step === 10 && data.success) {
console.log('✅ Strategy generation completed successfully!');
clearTimeout(processTimeout);
clearInterval(heartbeatInterval);
setCurrentStrategy(data.strategy);
setShowEducationalModal(false);
setError('Strategy created successfully! Check the Strategic Intelligence tab for detailed insights.');
eventSource.close();
}
// Handle educational content from AI service manager
if (data.type === 'educational_content' && data.educational_content) {
console.log('📚 AI Service educational content:', data.educational_content);
setEducationalContent(data.educational_content);
}
// Handle success messages for individual steps
if (data.success && data.message) {
console.log('✅ Step completed:', data.message);
// Progress is already updated above, just log the success
}
} catch (parseError) {
console.error('❌ Error parsing SSE message:', parseError);
console.error('Raw message:', event.data);
}
};
// Handle SSE errors
eventSource.onerror = (error) => {
console.error('❌ SSE connection error:', error);
console.error(' ReadyState:', eventSource.readyState);
// Check connection state
switch (eventSource.readyState) {
case EventSource.CONNECTING:
console.log('🔄 SSE connection is connecting...');
break;
case EventSource.OPEN:
console.log('✅ SSE connection is open');
break;
case EventSource.CLOSED:
console.log('🔌 SSE connection is closed');
clearTimeout(processTimeout);
clearInterval(heartbeatInterval);
setError('Connection lost during AI generation. The process may still be running in the background. Please check the Strategic Intelligence tab for results.');
setShowEducationalModal(false);
break;
}
};
// Handle SSE connection open
eventSource.onopen = () => {
console.log('✅ SSE connection opened successfully');
console.log(' ReadyState:', eventSource.readyState);
console.log(' URL:', eventSource.url);
// Update educational content to show connection is established
setEducationalContent({
title: '🔌 Connection Established',
description: 'Successfully connected to AI generation service. Starting analysis...',
details: [
'✅ SSE connection active',
'🤖 AI service ready',
'📊 Data processing initialized',
'🎯 Strategy generation starting'
],
insight: 'The connection is now established and AI analysis is beginning.',
estimated_time: '2-3 minutes total'
});
};
} else {
setError('Failed to create strategy or get strategy ID for AI generation.');
console.error('Failed to create strategy or get strategy ID for AI generation.');
setShowEducationalModal(false);
}
} catch (error: any) {
console.error('Error in SSE strategy generation:', error);
setError(`Error in strategy generation: ${error.message || 'Unknown error'}`);
setShowEducationalModal(false);
}
};
const handleSaveStrategy = async () => {
try {
setSaving(true);
setError(null);
const completionStats = getCompletionStats();
const strategyData = {
...formData,
completion_percentage: completionStats.completion_percentage,
user_id: 1,
name: formData.name || 'Enhanced Content Strategy',
industry: formData.industry || 'General'
};
const newStrategy = await createEnhancedStrategy(strategyData);
setCurrentStrategy(newStrategy);
setError('Strategy saved successfully!');
} catch (err: any) {
setError(`Error saving strategy: ${err.message || 'Unknown error'}`);
} finally {
setSaving(false);
}
};
const handleReviewCategory = (categoryId: string) => {
setActiveCategory(activeCategory === categoryId ? null : categoryId);
};
@@ -519,75 +257,19 @@ const ContentStrategyBuilder: React.FC = () => {
<HeaderSection autoPopulatedFields={autoPopulatedFields} />
{/* Error Alert */}
{error && (
<Alert
severity="error"
sx={{ mb: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
action={
<Box sx={{ display: 'flex', gap: 1 }}>
<Button size="small" variant="outlined" onClick={() => autoPopulateFromOnboarding(true)} startIcon={<RefreshIcon />}>Retry</Button>
<Button size="small" variant="contained" color="primary" onClick={() => setShowDataSourceTransparency(true)} startIcon={<InfoIcon />}>Why?</Button>
</Box>
}
>
<Box>
<Typography variant="subtitle2">Real data required</Typography>
<Typography variant="body2">{error || 'We could not auto-populate because required onboarding/analysis data is missing. Connect sources or complete onboarding, then retry.'}</Typography>
</Box>
</Alert>
)}
<ErrorAlert
error={error}
onRetry={() => autoPopulateFromOnboarding(true)}
onShowDataSourceTransparency={() => setShowDataSourceTransparency(true)}
/>
{/* Success Alert */}
{!error && currentStrategy && (
<Alert severity="success" sx={{ mb: 3 }}>
Strategy "{currentStrategy.name}" created successfully! Check the Strategic Intelligence tab for detailed insights.
</Alert>
)}
{/* Strategy Display */}
{currentStrategy && (
<Paper sx={{ p: 3, mb: 3 }}>
<Typography variant="h5" gutterBottom>
Created Strategy: {currentStrategy.name}
</Typography>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="text.secondary">
Industry: {currentStrategy.industry}
</Typography>
<Typography variant="subtitle1" color="text.secondary">
Completion: {currentStrategy.completion_percentage}%
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="text.secondary">
Created: {new Date(currentStrategy.created_at).toLocaleDateString()}
</Typography>
<Typography variant="subtitle1" color="text.secondary">
ID: {currentStrategy.id}
</Typography>
</Grid>
</Grid>
<Box sx={{ mt: 2 }}>
<Button
variant="outlined"
onClick={() => window.location.href = '/content-planning?tab=strategic-intelligence'}
startIcon={<AssessmentIcon />}
>
View Strategic Intelligence
</Button>
</Box>
</Paper>
)}
{categoryCompletionMessage && (
<Alert
severity="success"
sx={{ mb: 3, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
{categoryCompletionMessage}
</Alert>
)}
{/* Strategy Display and Success Alerts */}
<StrategyDisplay
currentStrategy={currentStrategy}
error={error}
categoryCompletionMessage={categoryCompletionMessage}
onViewStrategicIntelligence={() => window.location.href = '/content-planning?tab=strategic-intelligence'}
/>
<Grid container spacing={3}>
{/* Category Overview Panel */}
@@ -781,212 +463,41 @@ const ContentStrategyBuilder: React.FC = () => {
{/* Main Content Area */}
<Grid item xs={12} md={8}>
<Paper sx={{ p: 3, minHeight: '600px', background: 'linear-gradient(180deg, #faf7ff, #f1f0ff)' }}>
{activeCategory ? (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
{/* Category Header */}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
{getCategoryIcon(activeCategory)}
<Typography variant="h5" sx={{ ml: 1 }}>
{activeCategory.split('_').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ')}
</Typography>
<Chip
label={`${Math.round(completionStats.category_completion[activeCategory])}% Complete`}
color={getCategoryColor(activeCategory) as any}
sx={{ ml: 'auto' }}
/>
</Box>
{/* Educational Info Dialog */}
<Dialog
open={!!showEducationalInfo}
onClose={() => setShowEducationalInfo(null)}
maxWidth="md"
fullWidth
>
<DialogTitle>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SchoolIcon />
{showEducationalInfo && getEducationalContent(showEducationalInfo).title}
</Box>
</DialogTitle>
<DialogContent>
<Typography variant="body1" paragraph>
{showEducationalInfo && getEducationalContent(showEducationalInfo).description}
</Typography>
<Typography variant="h6" gutterBottom>
Key Points:
</Typography>
<List>
{showEducationalInfo && getEducationalContent(showEducationalInfo).points.map((point, index) => (
<ListItem key={index} sx={{ py: 0.5 }}>
<ListItemIcon>
<LightbulbIcon color="primary" fontSize="small" />
</ListItemIcon>
<ListItemText primary={point} />
</ListItem>
))}
</List>
<Typography variant="h6" gutterBottom>
Pro Tips:
</Typography>
<List>
{showEducationalInfo && getEducationalContent(showEducationalInfo).tips.map((tip, index) => (
<ListItem key={index} sx={{ py: 0.5 }}>
<ListItemIcon>
<PsychologyIcon color="secondary" fontSize="small" />
</ListItemIcon>
<ListItemText primary={tip} />
</ListItem>
))}
</List>
</DialogContent>
<DialogActions>
<Button onClick={() => setShowEducationalInfo(null)}>
Got it!
</Button>
</DialogActions>
</Dialog>
{/* Category Fields */}
<Box sx={{ mt: 1 }}>
<Grid container spacing={2}>
{STRATEGIC_INPUT_FIELDS
.filter(field => field.category === activeCategory)
.map((field, index) => {
// Determine grid size based on field type for better layout organization
const type = field.type;
const isWideField = type === 'json';
const isMediumField = type === 'multiselect' || type === 'select' || type === 'text';
const isCompactField = type === 'number' || type === 'boolean';
const forceFullWidth = field.id === 'content_budget' || field.id === 'team_size';
const gridMd = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
const gridLg = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
const gridSm = 12;
return (
<Grid item xs={12} sm={gridSm} md={gridMd} lg={gridLg} key={field.id}>
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.25, delay: index * 0.03 }}>
<StrategicInputField
fieldId={field.id}
value={formData[field.id]}
error={formErrors[field.id]}
autoPopulated={!!autoPopulatedFields[field.id]}
dataSource={dataSources[field.id]}
confidenceLevel={autoPopulatedFields[field.id] ? 0.8 : undefined}
dataQuality={autoPopulatedFields[field.id] ? 'High Quality' : undefined}
personalizationData={personalizationData[field.id]}
onChange={(value: any) => updateFormField(field.id, value)}
onValidate={() => validateFormField(field.id)}
onShowTooltip={() => setShowTooltip(field.id)}
onViewDataSource={() => setShowDataSourceTransparency(true)}
accentColorKey={getCategoryColor(activeCategory) as any}
isCompact={isCompactField}
/>
</motion.div>
</Grid>
);
})}
</Grid>
</Box>
{/* Category Actions */}
<Box sx={{ mt: 3, display: 'flex', gap: 2 }}>
{(() => {
const isReviewed = reviewedCategories.has(activeCategory);
console.log('🔍 Category review status:', {
activeCategory,
isReviewed,
reviewedCategories: Array.from(reviewedCategories)
});
return !isReviewed ? (
<Button
variant="contained"
onClick={() => {
console.log('🔘 Button clicked! activeCategory:', activeCategory);
console.log('🔘 reviewedCategories:', Array.from(reviewedCategories));
console.log('🔘 isMarkingReviewed:', isMarkingReviewed);
handleConfirmCategoryReviewWrapper();
}}
startIcon={isMarkingReviewed ? <CircularProgress size={20} /> : <CheckCircleIcon />}
disabled={isMarkingReviewed}
>
{isMarkingReviewed ? 'Marking as Reviewed...' : 'Mark as Reviewed'}
</Button>
) : (
<Chip
label="Category Reviewed"
color="success"
icon={<CheckCircleIcon />}
sx={{ px: 2, py: 1 }}
/>
);
})()}
<Button
variant="outlined"
onClick={() => setActiveCategory(null)}
>
Back to Overview
</Button>
</Box>
</motion.div>
) : (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<Box sx={{ textAlign: 'center', py: 8 }}>
<TimelineIcon sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h5" gutterBottom>
Select a Category to Review
</Typography>
<Typography variant="body1" color="text.secondary">
Click on any category from the left panel to review and complete the fields.
</Typography>
</Box>
</motion.div>
)}
<CategoryDetailView
activeCategory={activeCategory}
formData={formData}
formErrors={formErrors}
autoPopulatedFields={autoPopulatedFields}
dataSources={dataSources}
personalizationData={personalizationData}
completionStats={completionStats}
reviewedCategories={reviewedCategories}
isMarkingReviewed={isMarkingReviewed}
showEducationalInfo={showEducationalInfo}
STRATEGIC_INPUT_FIELDS={STRATEGIC_INPUT_FIELDS}
onUpdateFormField={updateFormField}
onValidateFormField={validateFormField}
onShowTooltip={setShowTooltip}
onViewDataSource={() => setShowDataSourceTransparency(true)}
onConfirmCategoryReview={handleConfirmCategoryReviewWrapper}
onSetActiveCategory={setActiveCategory}
onSetShowEducationalInfo={setShowEducationalInfo}
getCategoryIcon={getCategoryIcon}
getCategoryColor={getCategoryColor}
getEducationalContent={getEducationalContent}
/>
</Paper>
</Grid>
</Grid>
{/* Action Buttons */}
<Box sx={{ mt: 3, display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
<MuiTooltip
title={reviewProgressPercentage < 20 ? `Complete at least 20% of the form (currently ${Math.round(reviewProgressPercentage)}%)` : 'Create a comprehensive content strategy with AI insights'}
placement="top"
>
<span>
<Button
variant="outlined"
startIcon={<AutoAwesomeIcon />}
onClick={handleCreateStrategy}
disabled={aiGenerating || reviewProgressPercentage < 20}
>
{aiGenerating ? 'Creating...' : 'Create Strategy'}
</Button>
</span>
</MuiTooltip>
<Button
variant="contained"
startIcon={<SaveIcon />}
onClick={handleSaveStrategy}
disabled={saving || reviewProgressPercentage < 30}
>
{saving ? 'Saving...' : 'Save Strategy'}
</Button>
</Box>
<ActionButtons
aiGenerating={aiGenerating}
saving={saving}
reviewProgressPercentage={reviewProgressPercentage}
onCreateStrategy={handleCreateStrategy}
onSaveStrategy={handleSaveStrategy}
/>
{/* AI Recommendations Modal */}
<Dialog
@@ -1009,179 +520,13 @@ const ContentStrategyBuilder: React.FC = () => {
</DialogContent>
</Dialog>
{/* Educational Modal for Strategy Generation */}
<Dialog
{/* Enhanced Educational Modal for Strategy Generation */}
<EducationalModal
open={showEducationalModal}
onClose={() => setShowEducationalModal(false)}
maxWidth="lg"
fullWidth
>
<DialogTitle>
<Box display="flex" alignItems="center" gap={1}>
<SchoolIcon color="primary" />
{educationalContent?.title || 'AI Strategy Generation'}
</Box>
</DialogTitle>
<DialogContent>
{educationalContent ? (
<Box>
{/* Progress Bar */}
<Box sx={{ mb: 3 }}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={1}>
<Typography variant="body2" color="text.secondary">
Progress: {generationProgress}%
</Typography>
<Typography variant="body2" color="text.secondary">
Step {Math.ceil(generationProgress / 10)} of 10
</Typography>
</Box>
<LinearProgress
variant="determinate"
value={generationProgress}
sx={{ height: 8, borderRadius: 4 }}
/>
</Box>
{/* Educational Content */}
<Typography variant="h6" gutterBottom color="primary">
{educationalContent.title || 'AI Strategy Generation'}
</Typography>
{educationalContent.description && (
<Typography variant="body1" paragraph>
{educationalContent.description}
</Typography>
)}
{educationalContent.details && (
<Box sx={{ mb: 2 }}>
<Typography variant="subtitle2" gutterBottom>
What's happening:
</Typography>
<List dense>
{educationalContent.details.map((detail: string, index: number) => (
<ListItem key={index} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 32 }}>
<FiberManualRecordIcon sx={{ fontSize: 8 }} />
</ListItemIcon>
<ListItemText primary={detail} />
</ListItem>
))}
</List>
</Box>
)}
{educationalContent.insight && (
<Box sx={{ mb: 2, p: 2, bgcolor: 'grey.50', borderRadius: 1 }}>
<Typography variant="subtitle2" color="primary" gutterBottom>
💡 Insight:
</Typography>
<Typography variant="body2">
{educationalContent.insight}
</Typography>
</Box>
)}
{educationalContent.ai_prompt_preview && (
<Box sx={{ mb: 2, p: 2, bgcolor: 'blue.50', borderRadius: 1 }}>
<Typography variant="subtitle2" color="primary" gutterBottom>
🤖 AI Prompt Preview:
</Typography>
<Typography variant="body2" fontFamily="monospace" fontSize="0.875rem">
{educationalContent.ai_prompt_preview}
</Typography>
</Box>
)}
{educationalContent.estimated_time && (
<Box sx={{ mb: 2, p: 2, bgcolor: 'orange.50', borderRadius: 1 }}>
<Typography variant="subtitle2" color="warning.main" gutterBottom>
⏱️ Estimated Time:
</Typography>
<Typography variant="body2">
{educationalContent.estimated_time}
</Typography>
</Box>
)}
{educationalContent.achievement && (
<Box sx={{ mb: 2, p: 2, bgcolor: 'green.50', borderRadius: 1 }}>
<Typography variant="subtitle2" color="success.main" gutterBottom>
✅ Achievement:
</Typography>
<Typography variant="body2">
{educationalContent.achievement}
</Typography>
</Box>
)}
{educationalContent.next_step && (
<Box sx={{ mb: 2, p: 2, bgcolor: 'purple.50', borderRadius: 1 }}>
<Typography variant="subtitle2" color="secondary.main" gutterBottom>
🔄 Next Step:
</Typography>
<Typography variant="body2">
{educationalContent.next_step}
</Typography>
</Box>
)}
{/* Summary for completion */}
{educationalContent.summary && (
<Box sx={{ mt: 3, p: 2, bgcolor: 'primary.50', borderRadius: 1 }}>
<Typography variant="h6" gutterBottom color="primary">
🎉 Strategy Generation Summary
</Typography>
<Grid container spacing={2}>
<Grid item xs={6}>
<Typography variant="body2">
<strong>Components:</strong> {educationalContent.summary.successful_components}/{educationalContent.summary.total_components}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
<strong>Content Pieces:</strong> {educationalContent.summary.total_content_pieces}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
<strong>Estimated ROI:</strong> {educationalContent.summary.estimated_roi}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
<strong>Timeline:</strong> {educationalContent.summary.implementation_timeline}
</Typography>
</Grid>
</Grid>
</Box>
)}
</Box>
) : (
/* Loading state when educational content is not yet available */
<Box sx={{ textAlign: 'center', py: 4 }}>
<CircularProgress sx={{ mb: 2 }} />
<Typography variant="h6" gutterBottom>
🤖 AI-Powered Strategy Generation
</Typography>
<Typography variant="body1" color="text.secondary" paragraph>
Initializing AI analysis and preparing educational content...
</Typography>
<Typography variant="body2" color="text.secondary">
This may take a few moments as we set up the AI services.
</Typography>
</Box>
)}
</DialogContent>
<DialogActions>
<Button
onClick={() => setShowEducationalModal(false)}
disabled={generationProgress < 100}
>
{generationProgress < 100 ? 'Please wait...' : 'Close'}
</Button>
</DialogActions>
</Dialog>
educationalContent={educationalContent}
generationProgress={generationProgress}
/>
{/* Data Source Transparency Modal */}
<Dialog

View File

@@ -0,0 +1,227 @@
import React from 'react';
import {
Box,
Button,
Tooltip as MuiTooltip
} from '@mui/material';
import {
AutoAwesome as AutoAwesomeIcon,
Save as SaveIcon
} from '@mui/icons-material';
import { ActionButtonsProps, ActionButtonsBusinessLogicProps } from '../types/contentStrategy.types';
// Business Logic Hook
export const useActionButtonsBusinessLogic = ({
formData,
error,
currentStrategy,
setAIGenerating,
setError,
setCurrentStrategy,
setSaving,
setGenerationProgress,
setEducationalContent,
setShowEducationalModal,
validateAllFields,
getCompletionStats,
generateAIRecommendations,
createEnhancedStrategy,
contentPlanningApi
}: ActionButtonsBusinessLogicProps) => {
const handleCreateStrategy = async () => {
try {
setAIGenerating(true);
setError(null);
console.log('Starting strategy creation...');
console.log('Current formData:', formData);
console.log('FormData ID:', formData.id);
// If we have a saved strategy, use its ID
if (formData.id) {
console.log('Using existing strategy ID:', formData.id);
await generateAIRecommendations(formData.id);
} else {
console.log('No strategy ID found, creating new strategy...');
// If no strategy is saved yet, save it first, then generate AI insights
const isValid = validateAllFields();
console.log('Form validation result:', isValid);
if (isValid) {
const completionStats = getCompletionStats();
const strategyData = {
...formData,
completion_percentage: completionStats.completion_percentage,
user_id: 1, // This would come from auth context
name: formData.name || 'Enhanced Content Strategy',
industry: formData.industry || 'General'
};
console.log('Attempting to create strategy with data:', strategyData);
// Use SSE streaming endpoint for strategy generation with educational content
await generateStrategyWithPolling(strategyData);
} else {
setError('Please fill in all required fields before generating AI insights.');
console.error('Form validation failed. Cannot generate AI insights.');
}
}
} catch (err: any) {
setError(`Error generating AI recommendations: ${err.message || 'Unknown error'}`);
console.error('Error in handleCreateStrategy:', err);
} finally {
setAIGenerating(false);
}
};
const generateStrategyWithPolling = async (strategyData: any) => {
try {
console.log('🚀 Starting polling-based strategy generation...');
// Initialize progress and educational content
setGenerationProgress(0);
setEducationalContent({
title: '🤖 AI-Powered Strategy Generation',
description: 'Initializing AI analysis and preparing educational content...',
details: [
'🔧 Setting up AI services',
'📊 Loading user context',
'🎯 Preparing strategy framework',
'📚 Generating educational content'
],
insight: 'We\'re getting everything ready for your personalized AI strategy generation.',
estimated_time: '2-3 minutes total'
});
// Show educational modal
setShowEducationalModal(true);
// Start polling-based strategy generation directly (no basic strategy creation)
const generationResult = await contentPlanningApi.startStrategyGenerationPolling(1, 'Enhanced Content Strategy');
console.log('Strategy generation started:', generationResult);
if (generationResult && generationResult.task_id) {
const taskId = generationResult.task_id;
console.log('Task ID received:', taskId);
// Start polling for status updates
contentPlanningApi.pollStrategyGeneration(
taskId,
// onProgress callback
(status: any) => {
console.log('📊 Progress update:', status);
// Update progress
if (status.progress !== undefined) {
setGenerationProgress(status.progress);
}
// Update educational content
if (status.educational_content) {
console.log('📚 Updating educational content:', status.educational_content);
setEducationalContent(status.educational_content);
}
// Update message
if (status.message) {
console.log('📝 Status message:', status.message);
}
},
// onComplete callback
(strategy: any) => {
console.log('✅ Strategy generation completed successfully!');
setCurrentStrategy(strategy);
setShowEducationalModal(false);
setError('Strategy created successfully! Check the Strategic Intelligence tab for detailed insights.');
},
// onError callback
(error: string) => {
console.error('❌ Strategy generation failed:', error);
setError(`Strategy generation failed: ${error}`);
setShowEducationalModal(false);
},
5000, // 5 second polling interval for faster updates
72 // 6 minutes max (72 * 5 seconds)
);
} else {
setError('Failed to start strategy generation. No task ID received.');
setShowEducationalModal(false);
}
} catch (error: any) {
console.error('Error in polling-based strategy generation:', error);
setError(`Error in strategy generation: ${error.message || 'Unknown error'}`);
setShowEducationalModal(false);
}
};
const handleSaveStrategy = async () => {
try {
setSaving(true);
setError(null);
const completionStats = getCompletionStats();
const strategyData = {
...formData,
completion_percentage: completionStats.completion_percentage,
user_id: 1,
name: formData.name || 'Enhanced Content Strategy',
industry: formData.industry || 'General'
};
const newStrategy = await createEnhancedStrategy(strategyData);
setCurrentStrategy(newStrategy);
setError('Strategy saved successfully!');
} catch (err: any) {
setError(`Error saving strategy: ${err.message || 'Unknown error'}`);
} finally {
setSaving(false);
}
};
return {
handleCreateStrategy,
handleSaveStrategy
};
};
// UI Component
const ActionButtons: React.FC<ActionButtonsProps> = ({
aiGenerating,
saving,
reviewProgressPercentage,
onCreateStrategy,
onSaveStrategy
}) => {
return (
<Box sx={{ mt: 3, display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
<MuiTooltip
title={reviewProgressPercentage < 20 ? `Complete at least 20% of the form (currently ${Math.round(reviewProgressPercentage)}%)` : 'Create a comprehensive content strategy with AI insights'}
placement="top"
>
<span>
<Button
variant="outlined"
startIcon={<AutoAwesomeIcon />}
onClick={onCreateStrategy}
disabled={aiGenerating || reviewProgressPercentage < 20}
>
{aiGenerating ? 'Creating...' : 'Create Strategy'}
</Button>
</span>
</MuiTooltip>
<Button
variant="contained"
startIcon={<SaveIcon />}
onClick={onSaveStrategy}
disabled={saving || reviewProgressPercentage < 30}
>
{saving ? 'Saving...' : 'Save Strategy'}
</Button>
</Box>
);
};
export default ActionButtons;

View File

@@ -0,0 +1,257 @@
import React from 'react';
import {
Box,
Typography,
Chip,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
List,
ListItem,
ListItemIcon,
ListItemText,
Grid,
CircularProgress
} from '@mui/material';
import {
School as SchoolIcon,
Lightbulb as LightbulbIcon,
Psychology as PsychologyIcon,
Timeline as TimelineIcon,
CheckCircle as CheckCircleIcon
} from '@mui/icons-material';
import { motion } from 'framer-motion';
import StrategicInputField from '../StrategicInputField';
import { CategoryDetailViewProps, EducationalInfoDialogProps } from '../types/contentStrategy.types';
const EducationalInfoDialog: React.FC<EducationalInfoDialogProps> = ({
open,
onClose,
categoryId,
getEducationalContent
}) => {
if (!categoryId) return null;
const educationalContent = getEducationalContent(categoryId);
return (
<Dialog
open={open}
onClose={onClose}
maxWidth="md"
fullWidth
>
<DialogTitle>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SchoolIcon />
{educationalContent?.title}
</Box>
</DialogTitle>
<DialogContent>
<Typography variant="body1" paragraph>
{educationalContent?.description}
</Typography>
<Typography variant="h6" gutterBottom>
Key Points:
</Typography>
<List>
{educationalContent?.points?.map((point: string, index: number) => (
<ListItem key={index} sx={{ py: 0.5 }}>
<ListItemIcon>
<LightbulbIcon color="primary" fontSize="small" />
</ListItemIcon>
<ListItemText primary={point} />
</ListItem>
))}
</List>
<Typography variant="h6" gutterBottom>
Pro Tips:
</Typography>
<List>
{educationalContent?.tips?.map((tip: string, index: number) => (
<ListItem key={index} sx={{ py: 0.5 }}>
<ListItemIcon>
<PsychologyIcon color="secondary" fontSize="small" />
</ListItemIcon>
<ListItemText primary={tip} />
</ListItem>
))}
</List>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
Got it!
</Button>
</DialogActions>
</Dialog>
);
};
const CategoryDetailView: React.FC<CategoryDetailViewProps> = ({
activeCategory,
formData,
formErrors,
autoPopulatedFields,
dataSources,
personalizationData,
completionStats,
reviewedCategories,
isMarkingReviewed,
showEducationalInfo,
STRATEGIC_INPUT_FIELDS,
onUpdateFormField,
onValidateFormField,
onShowTooltip,
onViewDataSource,
onConfirmCategoryReview,
onSetActiveCategory,
onSetShowEducationalInfo,
getCategoryIcon,
getCategoryColor,
getEducationalContent
}) => {
if (!activeCategory) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<Box sx={{ textAlign: 'center', py: 8 }}>
<TimelineIcon sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h5" gutterBottom>
Select a Category to Review
</Typography>
<Typography variant="body1" color="text.secondary">
Click on any category from the left panel to review and complete the fields.
</Typography>
</Box>
</motion.div>
);
}
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
{/* Category Header */}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
{getCategoryIcon(activeCategory)}
<Typography variant="h5" sx={{ ml: 1 }}>
{activeCategory.split('_').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ')}
</Typography>
<Chip
label={`${Math.round(completionStats.category_completion[activeCategory])}% Complete`}
color={getCategoryColor(activeCategory) as any}
sx={{ ml: 'auto' }}
/>
</Box>
{/* Educational Info Dialog */}
<EducationalInfoDialog
open={!!showEducationalInfo}
onClose={() => onSetShowEducationalInfo(null)}
categoryId={showEducationalInfo}
getEducationalContent={getEducationalContent}
/>
{/* Category Fields */}
<Box sx={{ mt: 1 }}>
<Grid container spacing={2}>
{STRATEGIC_INPUT_FIELDS
.filter(field => field.category === activeCategory)
.map((field, index) => {
// Determine grid size based on field type for better layout organization
const type = field.type;
const isWideField = type === 'json';
const isMediumField = type === 'multiselect' || type === 'select' || type === 'text';
const isCompactField = type === 'number' || type === 'boolean';
const forceFullWidth = field.id === 'content_budget' || field.id === 'team_size';
const gridMd = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
const gridLg = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
const gridSm = 12;
return (
<Grid item xs={12} sm={gridSm} md={gridMd} lg={gridLg} key={field.id}>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25, delay: index * 0.03 }}
>
<StrategicInputField
fieldId={field.id}
value={formData[field.id]}
error={formErrors[field.id]}
autoPopulated={!!autoPopulatedFields[field.id]}
dataSource={dataSources[field.id]}
confidenceLevel={autoPopulatedFields[field.id] ? 0.8 : undefined}
dataQuality={autoPopulatedFields[field.id] ? 'High Quality' : undefined}
personalizationData={personalizationData[field.id]}
onChange={(value: any) => onUpdateFormField(field.id, value)}
onValidate={() => onValidateFormField(field.id)}
onShowTooltip={() => onShowTooltip(field.id)}
onViewDataSource={onViewDataSource}
accentColorKey={getCategoryColor(activeCategory) as any}
isCompact={isCompactField}
/>
</motion.div>
</Grid>
);
})}
</Grid>
</Box>
{/* Category Actions */}
<Box sx={{ mt: 3, display: 'flex', gap: 2 }}>
{(() => {
const isReviewed = reviewedCategories.has(activeCategory);
console.log('🔍 Category review status:', {
activeCategory,
isReviewed,
reviewedCategories: Array.from(reviewedCategories)
});
return !isReviewed ? (
<Button
variant="contained"
onClick={() => {
console.log('🔘 Button clicked! activeCategory:', activeCategory);
console.log('🔘 reviewedCategories:', Array.from(reviewedCategories));
console.log('🔘 isMarkingReviewed:', isMarkingReviewed);
onConfirmCategoryReview();
}}
startIcon={isMarkingReviewed ? <CircularProgress size={20} /> : <CheckCircleIcon />}
disabled={isMarkingReviewed}
>
{isMarkingReviewed ? 'Marking as Reviewed...' : 'Mark as Reviewed'}
</Button>
) : (
<Chip
label="Category Reviewed"
color="success"
icon={<CheckCircleIcon />}
sx={{ px: 2, py: 1 }}
/>
);
})()}
<Button
variant="outlined"
onClick={() => onSetActiveCategory(null)}
>
Back to Overview
</Button>
</Box>
</motion.div>
);
};
export default CategoryDetailView;

View File

@@ -0,0 +1,554 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Typography,
Box,
Grid,
List,
ListItem,
ListItemIcon,
ListItemText,
CircularProgress
} from '@mui/material';
import {
School as SchoolIcon,
Lightbulb as LightbulbIcon,
Timeline as TimelineIcon,
AutoAwesome as AutoAwesomeIcon,
CheckCircle as CheckCircleIcon,
Schedule as ScheduleIcon,
TrendingUp as TrendingUpIcon,
FiberManualRecord as FiberManualRecordIcon
} from '@mui/icons-material';
import { motion } from 'framer-motion';
import { EducationalModalProps } from '../types/contentStrategy.types';
const EducationalModal: React.FC<EducationalModalProps> = ({
open,
onClose,
educationalContent,
generationProgress
}) => {
return (
<Dialog
open={open}
onClose={onClose}
maxWidth="lg"
fullWidth
PaperProps={{
sx: {
borderRadius: 4,
background: 'linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%)',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.15)',
border: '1px solid rgba(102, 126, 234, 0.1)',
overflow: 'hidden'
}
}}
>
<DialogTitle sx={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
position: 'relative',
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(45deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%)',
pointerEvents: 'none'
}
}}>
<Box display="flex" alignItems="center" gap={2} sx={{ position: 'relative', zIndex: 1 }}>
<Box sx={{
p: 1,
borderRadius: 2,
background: 'rgba(255, 255, 255, 0.2)',
backdropFilter: 'blur(10px)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
}}>
<SchoolIcon sx={{ color: 'white', fontSize: 24 }} />
</Box>
<Box>
<Typography variant="h5" sx={{ fontWeight: 700, mb: 0.5 }}>
{educationalContent?.title || 'AI Strategy Generation'}
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9, fontWeight: 500 }}>
Creating your comprehensive content strategy
</Typography>
</Box>
</Box>
</DialogTitle>
<DialogContent sx={{ p: 4 }}>
{educationalContent ? (
<Box>
{/* Enhanced Progress Section */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<Box sx={{ mb: 4 }}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
<Box>
<Typography variant="h6" sx={{ fontWeight: 700, color: '#667eea', mb: 0.5 }}>
Progress: {generationProgress}%
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ fontWeight: 500 }}>
Step {Math.ceil(generationProgress / 10)} of 8 {Math.ceil((100 - generationProgress) / 10)} steps remaining
</Typography>
</Box>
<Box sx={{ textAlign: 'right' }}>
<Typography variant="h4" sx={{ fontWeight: 700, color: '#667eea' }}>
{generationProgress}%
</Typography>
<Typography variant="caption" color="text.secondary">
Complete
</Typography>
</Box>
</Box>
{/* Enhanced Progress Bar with Glitch Effect */}
<Box sx={{ position: 'relative', mb: 1 }}>
<Box sx={{
height: 12,
borderRadius: 6,
background: 'linear-gradient(90deg, #f0f0f0 0%, #e0e0e0 100%)',
overflow: 'hidden',
boxShadow: 'inset 0 2px 4px rgba(0, 0, 0, 0.1)',
position: 'relative'
}}>
<motion.div
initial={{ width: 0 }}
animate={{ width: `${generationProgress}%` }}
transition={{
duration: 0.8,
ease: "easeOut",
delay: 0.2
}}
style={{
height: '100%',
background: 'linear-gradient(90deg, #667eea 0%, #764ba2 50%, #667eea 100%)',
borderRadius: 6,
position: 'relative',
overflow: 'hidden'
}}
>
{/* Glitch Effect Overlay */}
<motion.div
animate={{
x: [0, -2, 2, 0],
opacity: [0.8, 1, 0.8]
}}
transition={{
duration: 0.1,
repeat: Infinity,
repeatType: "reverse"
}}
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%)',
borderRadius: 6
}}
/>
{/* Shimmer Effect */}
<motion.div
animate={{
x: ['-100%', '100%']
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "linear"
}}
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.4) 50%, transparent 100%)',
borderRadius: 6
}}
/>
</motion.div>
</Box>
{/* Progress Markers */}
<Box sx={{
display: 'flex',
justifyContent: 'space-between',
mt: 1,
px: 1
}}>
{[0, 25, 50, 75, 100].map((marker) => (
<Box key={marker} sx={{
width: 8,
height: 8,
borderRadius: '50%',
background: generationProgress >= marker ? '#667eea' : '#e0e0e0',
boxShadow: generationProgress >= marker ? '0 2px 8px rgba(102, 126, 234, 0.3)' : 'none',
transition: 'all 0.3s ease'
}} />
))}
</Box>
</Box>
</Box>
</motion.div>
{/* Enhanced Educational Content */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
>
<Grid container spacing={3}>
{/* Main Content */}
<Grid item xs={12} lg={8}>
{educationalContent.description && (
<Box sx={{ mb: 3 }}>
<Typography variant="h6" gutterBottom sx={{
fontWeight: 600,
color: '#667eea',
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<LightbulbIcon sx={{ fontSize: 20 }} />
What's Happening
</Typography>
<Typography variant="body1" sx={{
lineHeight: 1.7,
color: 'text.primary',
fontWeight: 500
}}>
{educationalContent.description}
</Typography>
</Box>
)}
{educationalContent.details && (
<Box sx={{ mb: 3 }}>
<Typography variant="h6" gutterBottom sx={{
fontWeight: 600,
color: '#667eea',
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<TimelineIcon sx={{ fontSize: 20 }} />
Current Activities
</Typography>
<Box sx={{
background: 'rgba(102, 126, 234, 0.05)',
borderRadius: 3,
p: 2,
border: '1px solid rgba(102, 126, 234, 0.1)'
}}>
<List dense sx={{ py: 0 }}>
{educationalContent.details.map((detail: string, index: number) => (
<motion.div
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.1 * index }}
>
<ListItem sx={{ py: 1, px: 0 }}>
<ListItemIcon sx={{ minWidth: 36 }}>
<Box sx={{
p: 0.5,
borderRadius: 1,
background: 'rgba(102, 126, 234, 0.1)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<FiberManualRecordIcon sx={{
fontSize: 8,
color: '#667eea'
}} />
</Box>
</ListItemIcon>
<ListItemText
primary={detail}
primaryTypographyProps={{
variant: 'body2',
sx: {
fontWeight: 500,
lineHeight: 1.5,
color: 'text.primary'
}
}}
/>
</ListItem>
</motion.div>
))}
</List>
</Box>
</Box>
)}
{educationalContent.ai_prompt_preview && (
<Box sx={{ mb: 3 }}>
<Typography variant="h6" gutterBottom sx={{
fontWeight: 600,
color: '#667eea',
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<AutoAwesomeIcon sx={{ fontSize: 20 }} />
AI Processing
</Typography>
<Box sx={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: 3,
p: 2.5,
color: 'white',
position: 'relative',
overflow: 'hidden'
}}>
<Box sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(45deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%)',
pointerEvents: 'none'
}} />
<Typography variant="body2" sx={{
fontFamily: '"JetBrains Mono", "Fira Code", monospace',
fontSize: '0.875rem',
lineHeight: 1.6,
position: 'relative',
zIndex: 1
}}>
{educationalContent.ai_prompt_preview}
</Typography>
</Box>
</Box>
)}
</Grid>
{/* Sidebar with Insights and Info */}
<Grid item xs={12} lg={4}>
<Box sx={{
background: 'rgba(102, 126, 234, 0.02)',
borderRadius: 3,
p: 3,
border: '1px solid rgba(102, 126, 234, 0.1)',
height: 'fit-content'
}}>
<Typography variant="h6" gutterBottom sx={{
fontWeight: 600,
color: '#667eea',
mb: 2
}}>
Insights & Information
</Typography>
{educationalContent.insight && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{
fontWeight: 600,
color: '#4caf50',
display: 'flex',
alignItems: 'center',
gap: 1,
mb: 1
}}>
<LightbulbIcon sx={{ fontSize: 16 }} />
Key Insight
</Typography>
<Typography variant="body2" sx={{
lineHeight: 1.6,
color: 'text.secondary',
fontWeight: 500
}}>
{educationalContent.insight}
</Typography>
</Box>
)}
{educationalContent.estimated_time && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{
fontWeight: 600,
color: '#ff9800',
display: 'flex',
alignItems: 'center',
gap: 1,
mb: 1
}}>
<ScheduleIcon sx={{ fontSize: 16 }} />
Time Estimate
</Typography>
<Typography variant="body2" sx={{
lineHeight: 1.6,
color: 'text.secondary',
fontWeight: 500
}}>
{educationalContent.estimated_time}
</Typography>
</Box>
)}
{educationalContent.achievement && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{
fontWeight: 600,
color: '#4caf50',
display: 'flex',
alignItems: 'center',
gap: 1,
mb: 1
}}>
<CheckCircleIcon sx={{ fontSize: 16 }} />
Achievement
</Typography>
<Typography variant="body2" sx={{
lineHeight: 1.6,
color: 'text.secondary',
fontWeight: 500
}}>
{educationalContent.achievement}
</Typography>
</Box>
)}
{educationalContent.next_step && (
<Box>
<Typography variant="subtitle2" sx={{
fontWeight: 600,
color: '#9c27b0',
display: 'flex',
alignItems: 'center',
gap: 1,
mb: 1
}}>
<TrendingUpIcon sx={{ fontSize: 16 }} />
Next Step
</Typography>
<Typography variant="body2" sx={{
lineHeight: 1.6,
color: 'text.secondary',
fontWeight: 500
}}>
{educationalContent.next_step}
</Typography>
</Box>
)}
</Box>
</Grid>
</Grid>
{/* Summary for completion */}
{educationalContent.summary && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.5 }}
>
<Box sx={{
mt: 4,
p: 3,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: 3,
color: 'white',
position: 'relative',
overflow: 'hidden'
}}>
<Box sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(45deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%)',
pointerEvents: 'none'
}} />
<Typography variant="h6" gutterBottom sx={{
fontWeight: 700,
position: 'relative',
zIndex: 1,
display: 'flex',
alignItems: 'center',
gap: 1
}}>
<AutoAwesomeIcon />
Strategy Generation Summary
</Typography>
<Grid container spacing={2} sx={{ position: 'relative', zIndex: 1 }}>
{Object.entries(educationalContent.summary).map(([key, value]) => (
<Grid item xs={6} md={3} key={key}>
<Box sx={{ textAlign: 'center' }}>
<Typography variant="h6" sx={{ fontWeight: 700, mb: 0.5 }}>
{value as string}
</Typography>
<Typography variant="caption" sx={{ opacity: 0.9, fontWeight: 500 }}>
{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
</Typography>
</Box>
</Grid>
))}
</Grid>
</Box>
</motion.div>
)}
</motion.div>
</Box>
) : (
<Box sx={{ textAlign: 'center', py: 4 }}>
<CircularProgress size={60} sx={{ color: '#667eea', mb: 2 }} />
<Typography variant="h6" sx={{ fontWeight: 600, color: '#667eea' }}>
Initializing Strategy Generation
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
Preparing AI models and analyzing your data...
</Typography>
</Box>
)}
</DialogContent>
<DialogActions sx={{
p: 3,
pt: 0,
justifyContent: 'center'
}}>
<Button
variant="outlined"
onClick={onClose}
sx={{
borderRadius: 2,
px: 4,
py: 1.5,
fontWeight: 600,
borderColor: 'rgba(102, 126, 234, 0.3)',
color: '#667eea',
'&:hover': {
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.05)'
}
}}
>
Close
</Button>
</DialogActions>
</Dialog>
);
};
export default EducationalModal;

View File

@@ -0,0 +1,57 @@
import React from 'react';
import {
Box,
Alert,
Button,
Typography
} from '@mui/material';
import {
Refresh as RefreshIcon,
Info as InfoIcon
} from '@mui/icons-material';
import { ErrorAlertProps } from '../types/contentStrategy.types';
const ErrorAlert: React.FC<ErrorAlertProps> = ({
error,
onRetry,
onShowDataSourceTransparency
}) => {
if (!error) return null;
return (
<Alert
severity="error"
sx={{ mb: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
action={
<Box sx={{ display: 'flex', gap: 1 }}>
<Button
size="small"
variant="outlined"
onClick={onRetry}
startIcon={<RefreshIcon />}
>
Retry
</Button>
<Button
size="small"
variant="contained"
color="primary"
onClick={onShowDataSourceTransparency}
startIcon={<InfoIcon />}
>
Why?
</Button>
</Box>
}
>
<Box>
<Typography variant="subtitle2">Real data required</Typography>
<Typography variant="body2">
{error || 'We could not auto-populate because required onboarding/analysis data is missing. Connect sources or complete onboarding, then retry.'}
</Typography>
</Box>
</Alert>
);
};
export default ErrorAlert;

View File

@@ -0,0 +1,79 @@
import React from 'react';
import {
Box,
Paper,
Typography,
Button,
Alert,
Grid
} from '@mui/material';
import {
Assessment as AssessmentIcon
} from '@mui/icons-material';
import { StrategyDisplayProps } from '../types/contentStrategy.types';
const StrategyDisplay: React.FC<StrategyDisplayProps> = ({
currentStrategy,
error,
categoryCompletionMessage,
onViewStrategicIntelligence
}) => {
return (
<>
{/* Success Alert */}
{!error && currentStrategy && (
<Alert severity="success" sx={{ mb: 3 }}>
Strategy "{currentStrategy.name}" created successfully! Check the Strategic Intelligence tab for detailed insights.
</Alert>
)}
{/* Strategy Display */}
{currentStrategy && (
<Paper sx={{ p: 3, mb: 3 }}>
<Typography variant="h5" gutterBottom>
Created Strategy: {currentStrategy.name}
</Typography>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="text.secondary">
Industry: {currentStrategy.industry}
</Typography>
<Typography variant="subtitle1" color="text.secondary">
Completion: {currentStrategy.completion_percentage}%
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="text.secondary">
Created: {new Date(currentStrategy.created_at).toLocaleDateString()}
</Typography>
<Typography variant="subtitle1" color="text.secondary">
ID: {currentStrategy.id}
</Typography>
</Grid>
</Grid>
<Box sx={{ mt: 2 }}>
<Button
variant="outlined"
onClick={onViewStrategicIntelligence}
startIcon={<AssessmentIcon />}
>
View Strategic Intelligence
</Button>
</Box>
</Paper>
)}
{/* Category Completion Message */}
{categoryCompletionMessage && (
<Alert
severity="success"
sx={{ mb: 3, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
{categoryCompletionMessage}
</Alert>
)}
</>
);
};
export default StrategyDisplay;

View File

@@ -0,0 +1,18 @@
// Main Content Strategy Builder
export { default as ContentStrategyBuilder } from '../ContentStrategyBuilder';
// Components
export { default as HeaderSection } from './components/HeaderSection';
export { default as CategoryList } from './components/CategoryList';
export { default as ProgressTracker } from './components/ProgressTracker';
export { default as EducationalModal } from './components/EducationalModal';
export { default as CategoryDetailView } from './components/CategoryDetailView';
export { default as ActionButtons } from './components/ActionButtons';
export { default as StrategyDisplay } from './components/StrategyDisplay';
export { default as ErrorAlert } from './components/ErrorAlert';
// Hooks
export { useActionButtonsBusinessLogic } from './components/ActionButtons';
// Types
export * from './types/contentStrategy.types';

View File

@@ -0,0 +1,124 @@
// Educational Content Types
export interface EducationalContent {
title?: string;
description?: string;
details?: string[];
insight?: string;
estimated_time?: string;
achievement?: string;
next_step?: string;
ai_prompt_preview?: string;
summary?: Record<string, string>;
}
// Educational Modal Props
export interface EducationalModalProps {
open: boolean;
onClose: () => void;
educationalContent: EducationalContent | null;
generationProgress: number;
}
// Category Detail View Types
export interface CategoryDetailViewProps {
activeCategory: string | null;
formData: Record<string, any>;
formErrors: Record<string, any>;
autoPopulatedFields: Record<string, any>;
dataSources: Record<string, any>;
personalizationData: Record<string, any>;
completionStats: any;
reviewedCategories: Set<string>;
isMarkingReviewed: boolean;
showEducationalInfo: string | null;
STRATEGIC_INPUT_FIELDS: any[];
onUpdateFormField: (fieldId: string, value: any) => void;
onValidateFormField: (fieldId: string) => boolean;
onShowTooltip: (fieldId: string) => void;
onViewDataSource: () => void;
onConfirmCategoryReview: () => void;
onSetActiveCategory: (category: string | null) => void;
onSetShowEducationalInfo: (categoryId: string | null) => void;
getCategoryIcon: (category: string) => React.ReactNode;
getCategoryColor: (category: string) => string;
getEducationalContent: (categoryId: string) => any;
}
// Educational Info Dialog Types
export interface EducationalInfoDialogProps {
open: boolean;
onClose: () => void;
categoryId: string | null;
getEducationalContent: (categoryId: string) => any;
}
// Action Buttons Types
export interface ActionButtonsProps {
aiGenerating: boolean;
saving: boolean;
reviewProgressPercentage: number;
onCreateStrategy: () => void;
onSaveStrategy: () => void;
}
// Action Buttons Business Logic Types
export interface ActionButtonsBusinessLogicProps {
formData: Record<string, any>;
error: string | null;
currentStrategy: any;
setAIGenerating: (generating: boolean) => void;
setError: (error: string | null) => void;
setCurrentStrategy: (strategy: any) => void;
setSaving: (saving: boolean) => void;
setGenerationProgress: (progress: number) => void;
setEducationalContent: (content: any) => void;
setShowEducationalModal: (show: boolean) => void;
validateAllFields: () => boolean;
getCompletionStats: () => any;
generateAIRecommendations: (strategyId: string) => Promise<void>;
createEnhancedStrategy: (strategyData: any) => Promise<any>;
contentPlanningApi: any;
}
// Strategy Generation Types
export interface StrategyGenerationStatus {
progress?: number;
message?: string;
educational_content?: EducationalContent;
error?: string;
}
export interface StrategyGenerationCallbacks {
onProgress: (status: StrategyGenerationStatus) => void;
onComplete: (strategy: any) => void;
onError: (error: string) => void;
}
// Category Navigation Types
export interface CategoryNavigationProps {
activeCategory: string | null;
onCategorySelect: (category: string | null) => void;
completionStats: any;
reviewedCategories: Set<string>;
}
// Strategy Display Types
export interface StrategyDisplayProps {
currentStrategy: any;
error: string | null;
categoryCompletionMessage: string | null;
onViewStrategicIntelligence: () => void;
}
// Error Alert Types
export interface ErrorAlertProps {
error: string | null;
onRetry: () => void;
onShowDataSourceTransparency: () => void;
}
// Success Alert Types
export interface SuccessAlertProps {
currentStrategy: any;
categoryCompletionMessage: string | null;
}

View File

@@ -0,0 +1,103 @@
import React from 'react';
import { Box, CircularProgress, Alert, Typography, Grid } from '@mui/material';
import { useStrategyData } from './hooks/useStrategyData';
import { useStrategyActions } from './hooks/useStrategyActions';
import StrategyHeader from './components/StrategyHeader';
import StrategicInsightsCard from './components/StrategicInsightsCard';
import CompetitiveAnalysisCard from './components/CompetitiveAnalysisCard';
import PerformancePredictionsCard from './components/PerformancePredictionsCard';
import ImplementationRoadmapCard from './components/ImplementationRoadmapCard';
import RiskAssessmentCard from './components/RiskAssessmentCard';
import StrategyActions from './components/StrategyActions';
import ConfirmationDialog from './components/ConfirmationDialog';
const StrategyIntelligenceTab: React.FC = () => {
const { strategyData, loading, error, loadStrategyData } = useStrategyData();
const {
strategyConfirmed,
showConfirmDialog,
setShowConfirmDialog,
handleConfirmStrategy,
confirmStrategy,
handleGenerateContentCalendar
} = useStrategyActions();
const handleConfirmStrategyClick = () => {
handleConfirmStrategy();
};
const handleConfirmStrategyAction = async () => {
await confirmStrategy(strategyData);
};
const handleGenerateContentCalendarAction = async () => {
try {
await handleGenerateContentCalendar(strategyData);
} catch (error) {
console.error('Error generating content calendar:', error);
}
};
if (loading) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: 400 }}>
<CircularProgress />
</Box>
);
}
if (error) {
return (
<Alert severity="error" sx={{ m: 2 }}>
{error}
</Alert>
);
}
if (!strategyData) {
return (
<Box sx={{ textAlign: 'center', p: 4 }}>
<Typography variant="h6" color="text.secondary" gutterBottom>
No Strategy Data Available
</Typography>
<Typography variant="body2" color="text.secondary">
Generate a comprehensive strategy first to view strategic intelligence.
</Typography>
</Box>
);
}
return (
<Box sx={{ p: 3 }}>
{/* Header Section */}
<StrategyHeader strategyData={strategyData} strategyConfirmed={strategyConfirmed} />
{/* Strategy Components Grid */}
<Grid container spacing={2}>
<StrategicInsightsCard strategyData={strategyData} />
<CompetitiveAnalysisCard strategyData={strategyData} />
<PerformancePredictionsCard strategyData={strategyData} />
<ImplementationRoadmapCard strategyData={strategyData} />
<RiskAssessmentCard strategyData={strategyData} />
</Grid>
{/* Action Buttons */}
<StrategyActions
strategyData={strategyData}
strategyConfirmed={strategyConfirmed}
onConfirmStrategy={handleConfirmStrategyClick}
onGenerateContentCalendar={handleGenerateContentCalendarAction}
onRefreshData={loadStrategyData}
/>
{/* Confirmation Dialog */}
<ConfirmationDialog
open={showConfirmDialog}
onClose={() => setShowConfirmDialog(false)}
onConfirm={handleConfirmStrategyAction}
/>
</Box>
);
};
export default StrategyIntelligenceTab;

View File

@@ -0,0 +1,435 @@
import React, { useState } from 'react';
import {
Grid,
Typography,
Box,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Accordion,
AccordionSummary,
AccordionDetails,
Popover
} from '@mui/material';
import {
TrendingUp as TrendingUpIcon,
ExpandMore as ExpandMoreIcon,
Business as BusinessIcon,
Star as StarIcon,
Warning as WarningIcon,
CheckCircle as CheckCircleIcon,
Cancel as CancelIcon,
Lightbulb as LightbulbIcon
} from '@mui/icons-material';
import { StrategyData } from '../types/strategy.types';
import {
ANALYSIS_CARD_STYLES,
getSectionStyles,
getAccordionStyles,
getEnhancedChipStyles,
getListItemStyles
} from '../styles';
import ProgressiveCard from './ProgressiveCard';
interface CompetitiveAnalysisCardProps {
strategyData: StrategyData | null;
}
const CompetitiveAnalysisCard: React.FC<CompetitiveAnalysisCardProps> = ({ strategyData }) => {
const [chipModal, setChipModal] = useState<{
open: boolean;
anchorEl: HTMLElement | null;
content: string;
type: 'strength' | 'weakness';
}>({
open: false,
anchorEl: null,
content: '',
type: 'strength'
});
// Get style objects
const sectionStyles = getSectionStyles();
const accordionStyles = getAccordionStyles();
const listItemStyles = getListItemStyles();
// Helper function to extract company name from description
const extractCompanyName = (description: string): string => {
if (description.includes('Jasper')) return 'Jasper AI';
if (description.includes('Copy.ai')) return 'Copy.ai';
if (description.includes('Writesonic')) return 'Writesonic';
if (description.includes('Grammarly')) return 'Grammarly';
if (description.includes('Surfer')) return 'Surfer SEO';
if (description.includes('Clearscope')) return 'Clearscope';
if (description.includes('Ahrefs')) return 'Ahrefs';
if (description.includes('SEMrush')) return 'SEMrush';
return description.split(' ').slice(0, 2).join(' ');
};
if (!strategyData?.competitive_analysis) {
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Competitive Analysis"
subtitle="Market positioning insights"
icon={<TrendingUpIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="body1" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Competitive analysis data not available
</Typography>
</Box>
}
details={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'}
</Typography>
</Box>
}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
}
// Summary content - always visible
const summaryContent = (
<Box>
{/* Competitive Overview */}
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.warning} 0%, ${ANALYSIS_CARD_STYLES.colors.error} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600,
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.warning}30`
}}>
{strategyData.competitive_analysis.competitors?.length || 0}
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Key Competitors
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
{strategyData.competitive_analysis.competitors?.length || 0} competitors analyzed
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label={`${strategyData.competitive_analysis.market_gaps?.length || 0} Gaps`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
<Chip
label={`${strategyData.competitive_analysis.opportunities?.length || 0} Opportunities`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
</Box>
</Box>
</Box>
{/* Competitors Preview */}
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 600 }}>
Top Competitors
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{strategyData.competitive_analysis.competitors?.slice(0, 3).map((competitor: any, index: number) => (
<Chip
key={index}
label={extractCompanyName(competitor.name || competitor.description || 'Unknown')}
size="small"
icon={<BusinessIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
))}
{(strategyData.competitive_analysis.competitors?.length || 0) > 3 && (
<Chip
label={`+${(strategyData.competitive_analysis.competitors?.length || 0) - 3} more`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
/>
)}
</Box>
</Box>
</Box>
);
// Detailed content - shown on expansion
const detailedContent = (
<Box>
{/* Competitors Analysis */}
{strategyData.competitive_analysis.competitors && strategyData.competitive_analysis.competitors.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Competitor Analysis ({strategyData.competitive_analysis.competitors.length})
</Typography>
{strategyData.competitive_analysis.competitors.map((competitor: any, index: number) => (
<Accordion key={index} defaultExpanded={false} sx={accordionStyles.accordion}>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={accordionStyles.expandIcon} />}
sx={accordionStyles.accordionSummary}
>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Box sx={{ mr: 1.5 }}>
<BusinessIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.primary, fontSize: 20 }} />
</Box>
<Box sx={{ flex: 1 }}>
<Typography variant="body2" sx={accordionStyles.accordionTitle}>
{extractCompanyName(competitor.name || competitor.description || 'Unknown Competitor')}
</Typography>
<Typography variant="caption" sx={accordionStyles.accordionSubtitle}>
{competitor.description || 'Competitor analysis'}
</Typography>
</Box>
</Box>
</AccordionSummary>
<AccordionDetails sx={{ pt: 0 }}>
<Box sx={sectionStyles.sectionContainer}>
{/* Content Strategy */}
{competitor.content_strategy && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.primary, fontWeight: 600, mb: 1 }}>
Content Strategy
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{competitor.content_strategy}
</Typography>
</Box>
)}
{/* Strengths */}
{competitor.strengths && competitor.strengths.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
Strengths
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{competitor.strengths.map((strength: string, strengthIndex: number) => (
<Chip
key={strengthIndex}
label={strength}
size="small"
icon={<CheckCircleIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
onClick={(e) => setChipModal({
open: true,
anchorEl: e.currentTarget,
content: strength,
type: 'strength'
})}
/>
))}
</Box>
</Box>
)}
{/* Weaknesses */}
{competitor.weaknesses && competitor.weaknesses.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.warning, fontWeight: 600, mb: 1 }}>
Weaknesses
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{competitor.weaknesses.map((weakness: string, weaknessIndex: number) => (
<Chip
key={weaknessIndex}
label={weakness}
size="small"
icon={<CancelIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip}
onClick={(e) => setChipModal({
open: true,
anchorEl: e.currentTarget,
content: weakness,
type: 'weakness'
})}
/>
))}
</Box>
</Box>
)}
</Box>
</AccordionDetails>
</Accordion>
))}
</Box>
)}
{/* Market Gaps */}
{strategyData.competitive_analysis.market_gaps && strategyData.competitive_analysis.market_gaps.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Market Gaps ({strategyData.competitive_analysis.market_gaps.length})
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{strategyData.competitive_analysis.market_gaps.map((gap: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.info,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={gap}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
</Box>
)}
{/* Opportunities */}
{strategyData.competitive_analysis.opportunities && strategyData.competitive_analysis.opportunities.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Opportunities ({strategyData.competitive_analysis.opportunities.length})
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{strategyData.competitive_analysis.opportunities.map((opportunity: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.success,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={opportunity}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
</Box>
)}
{/* Strategic Recommendations */}
{strategyData.competitive_analysis.recommendations && strategyData.competitive_analysis.recommendations.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Strategic Recommendations ({strategyData.competitive_analysis.recommendations.length})
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{strategyData.competitive_analysis.recommendations.map((recommendation: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.accent,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={recommendation}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
</Box>
)}
</Box>
);
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Competitive Analysis"
subtitle="Market positioning insights"
icon={<TrendingUpIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={summaryContent}
details={detailedContent}
trigger="hover"
autoCollapseDelay={3000}
/>
{/* Chip Modal for detailed view */}
<Popover
open={chipModal.open}
anchorEl={chipModal.anchorEl}
onClose={() => setChipModal({ ...chipModal, open: false })}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
PaperProps={{
sx: {
background: ANALYSIS_CARD_STYLES.colors.background.dark,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`,
backdropFilter: 'blur(10px)',
p: 2,
maxWidth: 300
}
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
{chipModal.type === 'strength' ? (
<CheckCircleIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontSize: 16, mr: 1 }} />
) : (
<CancelIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.warning, fontSize: 16, mr: 1 }} />
)}
<Typography variant="body2" sx={{
color: chipModal.type === 'strength' ? ANALYSIS_CARD_STYLES.colors.success : ANALYSIS_CARD_STYLES.colors.warning,
fontWeight: 600
}}>
{chipModal.type === 'strength' ? 'Strength' : 'Weakness'}
</Typography>
</Box>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{chipModal.content}
</Typography>
</Popover>
</Grid>
);
};
export default CompetitiveAnalysisCard;

View File

@@ -0,0 +1,97 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Typography,
Alert,
Box
} from '@mui/material';
import {
CheckCircle as CheckCircleIcon
} from '@mui/icons-material';
import { ConfirmationDialogProps } from '../types/strategy.types';
const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
open,
onClose,
onConfirm
}) => {
return (
<Dialog
open={open}
onClose={onClose}
maxWidth="sm"
fullWidth
PaperProps={{
sx: {
borderRadius: 3,
boxShadow: '0 16px 48px rgba(0, 0, 0, 0.2)'
}
}}
>
<DialogTitle>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
p: 1,
borderRadius: 2,
background: 'linear-gradient(135deg, #4caf50 0%, #8bc34a 100%)',
mr: 1.5,
boxShadow: '0 4px 12px rgba(76, 175, 80, 0.3)'
}}>
<CheckCircleIcon sx={{ color: 'white', fontSize: 20 }} />
</Box>
<Typography variant="h6" sx={{ fontWeight: 600 }}>
Confirm Strategy
</Typography>
</Box>
</DialogTitle>
<DialogContent>
<Typography variant="body1" sx={{ mb: 2, fontWeight: 500 }}>
Are you sure you want to confirm this strategy? Once confirmed, you'll be able to generate a content calendar based on this strategy.
</Typography>
<Alert severity="info" sx={{
mb: 2,
borderRadius: 2,
border: '1px solid rgba(33, 150, 243, 0.3)'
}}>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
<strong>Next Steps:</strong> After confirmation, you can generate a comprehensive content calendar that follows this strategy.
</Typography>
</Alert>
</DialogContent>
<DialogActions sx={{ p: 3, pt: 0 }}>
<Button
onClick={onClose}
sx={{
borderRadius: 2,
px: 3,
fontWeight: 600
}}
>
Cancel
</Button>
<Button
onClick={onConfirm}
variant="contained"
color="success"
sx={{
borderRadius: 2,
px: 3,
fontWeight: 600,
boxShadow: '0 4px 12px rgba(76, 175, 80, 0.3)',
'&:hover': {
boxShadow: '0 6px 16px rgba(76, 175, 80, 0.4)'
}
}}
>
Confirm Strategy
</Button>
</DialogActions>
</Dialog>
);
};
export default ConfirmationDialog;

View File

@@ -0,0 +1,453 @@
import React from 'react';
import {
Grid,
Typography,
Box,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Accordion,
AccordionSummary,
AccordionDetails
} from '@mui/material';
import {
Timeline as TimelineIcon,
ExpandMore as ExpandMoreIcon,
Group as GroupIcon,
AttachMoney as MoneyIcon,
CheckCircle as CheckCircleIcon
} from '@mui/icons-material';
import { StrategyData } from '../types/strategy.types';
import {
ANALYSIS_CARD_STYLES,
getSectionStyles,
getAccordionStyles,
getEnhancedChipStyles,
getListItemStyles
} from '../styles';
import ProgressiveCard from './ProgressiveCard';
interface ImplementationRoadmapCardProps {
strategyData: StrategyData | null;
}
const ImplementationRoadmapCard: React.FC<ImplementationRoadmapCardProps> = ({ strategyData }) => {
// Get style objects
const sectionStyles = getSectionStyles();
const accordionStyles = getAccordionStyles();
const listItemStyles = getListItemStyles();
// Helper function to format budget allocation
const formatBudgetAllocation = (budgetAllocation: any): string => {
if (typeof budgetAllocation === 'string') return budgetAllocation;
if (typeof budgetAllocation === 'object' && budgetAllocation !== null) {
return Object.entries(budgetAllocation)
.map(([key, value]) => `${key}: ${value}`)
.join(', ');
}
return 'Budget allocation not specified';
};
if (!strategyData?.implementation_roadmap) {
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Implementation Roadmap"
subtitle="Project timeline and phases"
icon={<TimelineIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="body1" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Implementation roadmap data not available
</Typography>
</Box>
}
details={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'}
</Typography>
</Box>
}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
}
// Summary content - always visible
const summaryContent = (
<Box>
{/* Project Overview */}
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.info} 0%, ${ANALYSIS_CARD_STYLES.colors.primary} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600,
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.info}30`
}}>
{strategyData.implementation_roadmap.phases?.length || 0}
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Project Phases
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
{strategyData.implementation_roadmap.total_duration || '6 months'} implementation
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label={`${strategyData.implementation_roadmap.phases?.reduce((total: number, phase: any) => total + (phase.tasks?.length || 0), 0) || 0} Tasks`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
<Chip
label={`${strategyData.implementation_roadmap.phases?.reduce((total: number, phase: any) => total + (phase.milestones?.length || 0), 0) || 0} Milestones`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
</Box>
</Box>
</Box>
{/* Phases Preview */}
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 600 }}>
Implementation Phases
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{strategyData.implementation_roadmap.phases?.slice(0, 4).map((phase: any, index: number) => (
<Chip
key={index}
label={`Phase ${index + 1}`}
size="small"
icon={<TimelineIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
))}
{(strategyData.implementation_roadmap.phases?.length || 0) > 4 && (
<Chip
label={`+${(strategyData.implementation_roadmap.phases?.length || 0) - 4} more`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
/>
)}
</Box>
</Box>
</Box>
);
// Detailed content - shown on expansion
const detailedContent = (
<Box>
{/* Project Timeline */}
{strategyData.implementation_roadmap.timeline && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Project Timeline
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Duration: {strategyData.implementation_roadmap.total_duration || '6 months'}
</Typography>
</Box>
{strategyData.implementation_roadmap.timeline.start_date && (
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary, mb: 1 }}>
Start Date: {strategyData.implementation_roadmap.timeline.start_date}
</Typography>
)}
{strategyData.implementation_roadmap.timeline.end_date && (
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary, mb: 1 }}>
End Date: {strategyData.implementation_roadmap.timeline.end_date}
</Typography>
)}
{strategyData.implementation_roadmap.timeline.key_milestones && strategyData.implementation_roadmap.timeline.key_milestones.length > 0 && (
<Box sx={{ mt: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600, mb: 1 }}>
Key Milestones:
</Typography>
<List dense>
{strategyData.implementation_roadmap.timeline.key_milestones.map((milestone: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.success,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={milestone}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
)}
</Box>
</Box>
)}
{/* Implementation Phases */}
{strategyData.implementation_roadmap.phases && strategyData.implementation_roadmap.phases.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Implementation Phases ({strategyData.implementation_roadmap.phases.length})
</Typography>
{strategyData.implementation_roadmap.phases.map((phase: any, index: number) => (
<Accordion key={index} defaultExpanded={false} sx={accordionStyles.accordion}>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={accordionStyles.expandIcon} />}
sx={accordionStyles.accordionSummary}
>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Box sx={{
width: 32,
height: 32,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.primary} 0%, ${ANALYSIS_CARD_STYLES.colors.secondary} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1rem',
fontWeight: 600
}}>
{index + 1}
</Box>
<Box sx={{ flex: 1 }}>
<Typography variant="body2" sx={accordionStyles.accordionTitle}>
{phase.name || `Phase ${index + 1}`}
</Typography>
<Typography variant="caption" sx={accordionStyles.accordionSubtitle}>
{phase.duration || '2 months'} {phase.tasks?.length || 0} tasks {phase.milestones?.length || 0} milestones
</Typography>
</Box>
</Box>
</AccordionSummary>
<AccordionDetails sx={{ pt: 0 }}>
<Box sx={sectionStyles.sectionContainer}>
{/* Phase Description */}
{phase.description && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{phase.description}
</Typography>
</Box>
)}
{/* Tasks */}
{phase.tasks && phase.tasks.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.primary, fontWeight: 600, mb: 1 }}>
Tasks ({phase.tasks.length})
</Typography>
<List dense>
{phase.tasks.map((task: string, taskIndex: number) => (
<ListItem key={taskIndex} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.primary,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={task}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
)}
{/* Milestones */}
{phase.milestones && phase.milestones.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
Milestones ({phase.milestones.length})
</Typography>
<List dense>
{phase.milestones.map((milestone: string, milestoneIndex: number) => (
<ListItem key={milestoneIndex} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<CheckCircleIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontSize: 16 }} />
</ListItemIcon>
<ListItemText
primary={milestone}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
)}
{/* Resources */}
{phase.resources && phase.resources.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.warning, fontWeight: 600, mb: 1 }}>
Resources ({phase.resources.length})
</Typography>
<List dense>
{phase.resources.map((resource: string, resourceIndex: number) => (
<ListItem key={resourceIndex} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.warning,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={resource}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
)}
</Box>
</AccordionDetails>
</Accordion>
))}
</Box>
)}
{/* Resource Allocation */}
{strategyData.implementation_roadmap.resource_allocation && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Resource Allocation
</Typography>
<Box sx={sectionStyles.sectionContainer}>
{/* Team Members */}
{strategyData.implementation_roadmap.resource_allocation.team_members && strategyData.implementation_roadmap.resource_allocation.team_members.length > 0 && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.primary, fontWeight: 600, mb: 1 }}>
Team Members ({strategyData.implementation_roadmap.resource_allocation.team_members.length})
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{strategyData.implementation_roadmap.resource_allocation.team_members.map((member: string, index: number) => (
<Chip
key={index}
label={member}
size="small"
icon={<GroupIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
))}
</Box>
</Box>
)}
{/* Budget Allocation */}
{strategyData.implementation_roadmap.resource_allocation.budget_allocation && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
Budget Allocation
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{formatBudgetAllocation(strategyData.implementation_roadmap.resource_allocation.budget_allocation)}
</Typography>
</Box>
)}
</Box>
</Box>
)}
{/* Success Metrics */}
{strategyData.implementation_roadmap.success_metrics && strategyData.implementation_roadmap.success_metrics.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Success Metrics ({strategyData.implementation_roadmap.success_metrics.length})
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{strategyData.implementation_roadmap.success_metrics.map((metric: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.success,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={metric}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
</Box>
)}
</Box>
);
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Implementation Roadmap"
subtitle="Project timeline and phases"
icon={<TimelineIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={summaryContent}
details={detailedContent}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
};
export default ImplementationRoadmapCard;

View File

@@ -0,0 +1,539 @@
import React from 'react';
import {
Grid,
Typography,
Box,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Divider
} from '@mui/material';
import {
ShowChart as ShowChartIcon,
TrendingUp as TrendingUpIcon,
Timeline as TimelineIcon,
Assessment as AssessmentIcon
} from '@mui/icons-material';
import { StrategyData } from '../types/strategy.types';
import {
ANALYSIS_CARD_STYLES,
getSectionStyles,
getEnhancedChipStyles,
getListItemStyles
} from '../styles';
import ProgressiveCard from './ProgressiveCard';
interface PerformancePredictionsCardProps {
strategyData: StrategyData | null;
}
const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({ strategyData }) => {
// Get style objects
const sectionStyles = getSectionStyles();
const listItemStyles = getListItemStyles();
if (!strategyData?.performance_predictions) {
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Performance Predictions"
subtitle="ROI and success metrics"
icon={<ShowChartIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="body1" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Performance predictions data not available
</Typography>
</Box>
}
details={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'}
</Typography>
</Box>
}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
}
// Summary content - always visible
const summaryContent = (
<Box>
{/* ROI Summary */}
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
alignItems: { xs: 'flex-start', sm: 'center' },
justifyContent: 'space-between',
mb: 2,
gap: 2,
width: '100%'
}}>
<Box sx={{
display: 'flex',
alignItems: 'center',
flex: 1,
minWidth: 0,
overflow: 'hidden'
}}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.success} 0%, ${ANALYSIS_CARD_STYLES.colors.accent} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600,
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.success}30`,
flexShrink: 0
}}>
{strategyData.performance_predictions.roi_predictions?.estimated_roi || '25%'}
</Box>
<Box sx={{
minWidth: 0,
flex: 1,
overflow: 'hidden'
}}>
<Typography variant="h6" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.primary,
fontWeight: 600,
fontSize: '1rem',
lineHeight: 1.2,
mb: 0.5,
wordBreak: 'break-word'
}}>
ROI Predictions
</Typography>
<Typography variant="caption" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.7rem',
lineHeight: 1.2,
wordBreak: 'break-word'
}}>
Expected return on investment
</Typography>
</Box>
</Box>
<Box sx={{
display: 'flex',
gap: 1,
flexWrap: 'wrap',
justifyContent: { xs: 'flex-start', sm: 'flex-end' },
flexShrink: 0
}}>
<Chip
label={`${(strategyData.performance_predictions as any)?.success_probability || '85%'} Success`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
<Chip
label={`${(strategyData.performance_predictions as any)?.implementation_timeline || '6 months'}`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
</Box>
</Box>
{/* ROI Description - Full width container */}
<Box sx={{ mt: 2, width: '100%' }}>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.8rem',
lineHeight: 1.5,
wordBreak: 'break-word',
textAlign: 'left'
}}>
ROI of {strategyData.performance_predictions.roi_predictions?.estimated_roi || '300-350%'} is achievable leveraging the strong cost-per-lead to lifetime-value ratio.
</Typography>
</Box>
</Box>
{/* Key Metrics Preview */}
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.primary,
mb: 1.5,
fontWeight: 600,
fontSize: '0.85rem'
}}>
Key Metrics Preview
</Typography>
<Box sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 1,
justifyContent: 'flex-start'
}}>
<Chip
label="Traffic Growth"
size="small"
icon={<TrendingUpIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
<Chip
label="Engagement"
size="small"
icon={<AssessmentIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
/>
<Chip
label="Conversion"
size="small"
icon={<ShowChartIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.accent).chip}
/>
</Box>
</Box>
</Box>
);
// Detailed content - shown on expansion
const detailedContent = (
<Box>
{/* ROI Summary */}
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
ROI Summary
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.85rem',
lineHeight: 1.5,
wordBreak: 'break-word'
}}>
Estimated ROI: {strategyData.performance_predictions.roi_predictions?.estimated_roi || '25%'}
</Typography>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.85rem',
lineHeight: 1.5,
wordBreak: 'break-word'
}}>
Success Probability: {(strategyData.performance_predictions as any)?.success_probability || '85%'}
</Typography>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.85rem',
lineHeight: 1.5,
wordBreak: 'break-word'
}}>
Implementation Timeline: {(strategyData.performance_predictions as any)?.implementation_timeline || '6 months'}
</Typography>
</Box>
</Box>
</Box>
{/* Key Metrics */}
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Key Metrics
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{
display: 'grid',
gridTemplateColumns: { xs: '1fr', sm: 'repeat(auto-fit, minmax(200px, 1fr))' },
gap: 2
}}>
{/* Traffic Predictions */}
{strategyData.performance_predictions.traffic_predictions && (
<Box sx={{
p: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.success}`,
borderRadius: 2,
background: `rgba(76, 175, 80, 0.1)`,
minHeight: 80,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
overflow: 'hidden'
}}>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.success,
fontWeight: 600,
mb: 1,
fontSize: '0.8rem',
lineHeight: 1.2,
wordBreak: 'break-word'
}}>
Traffic Growth
</Typography>
<Typography variant="caption" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.7rem',
lineHeight: 1.3,
wordBreak: 'break-word'
}}>
{strategyData.performance_predictions.traffic_predictions.growth_rate || '150% increase'}
</Typography>
</Box>
)}
{/* Engagement Predictions */}
{strategyData.performance_predictions.engagement_predictions && (
<Box sx={{
p: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.secondary}`,
borderRadius: 2,
background: `rgba(118, 75, 162, 0.1)`,
minHeight: 80,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
overflow: 'hidden'
}}>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.secondary,
fontWeight: 600,
mb: 1,
fontSize: '0.8rem',
lineHeight: 1.2,
wordBreak: 'break-word'
}}>
Engagement Rate
</Typography>
<Typography variant="caption" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.7rem',
lineHeight: 1.3,
wordBreak: 'break-word'
}}>
{strategyData.performance_predictions.engagement_predictions.engagement_rate || '45% improvement'}
</Typography>
</Box>
)}
{/* Conversion Predictions */}
{strategyData.performance_predictions.conversion_predictions && (
<Box sx={{
p: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.accent}`,
borderRadius: 2,
background: `rgba(240, 147, 251, 0.1)`,
minHeight: 80,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
overflow: 'hidden'
}}>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.accent,
fontWeight: 600,
mb: 1,
fontSize: '0.8rem',
lineHeight: 1.2,
wordBreak: 'break-word'
}}>
Conversion Rate
</Typography>
<Typography variant="caption" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.7rem',
lineHeight: 1.3,
wordBreak: 'break-word'
}}>
{strategyData.performance_predictions.conversion_predictions.conversion_rate || '3.2% to 5.8%'}
</Typography>
</Box>
)}
</Box>
</Box>
</Box>
<Divider sx={{ my: 2, opacity: 0.2, borderColor: ANALYSIS_CARD_STYLES.colors.border.secondary }} />
{/* Detailed Predictions */}
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Detailed Predictions
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{/* Traffic Predictions */}
{strategyData.performance_predictions.traffic_predictions && (
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.success,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary="Traffic Growth"
secondary={`${strategyData.performance_predictions.traffic_predictions.growth_rate || '150% increase'} in organic visitors`}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 0.5 }
}}
secondaryTypographyProps={{
variant: 'caption',
fontSize: '0.75rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.text.secondary, lineHeight: 1.4 }
}}
/>
</ListItem>
)}
{/* Engagement Predictions */}
{strategyData.performance_predictions.engagement_predictions && (
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.secondary,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary="Engagement Rate"
secondary={`${strategyData.performance_predictions.engagement_predictions.engagement_rate || '45% improvement'} in user interaction`}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.secondary, fontWeight: 600, mb: 0.5 }
}}
secondaryTypographyProps={{
variant: 'caption',
fontSize: '0.75rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.text.secondary, lineHeight: 1.4 }
}}
/>
</ListItem>
)}
{/* Conversion Predictions */}
{strategyData.performance_predictions.conversion_predictions && (
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.accent,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary="Conversion Rate"
secondary={`${strategyData.performance_predictions.conversion_predictions.conversion_rate || '3.2% to 5.8%'} improvement`}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.accent, fontWeight: 600, mb: 0.5 }
}}
secondaryTypographyProps={{
variant: 'caption',
fontSize: '0.75rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.text.secondary, lineHeight: 1.4 }
}}
/>
</ListItem>
)}
{/* ROI Predictions */}
{strategyData.performance_predictions.roi_predictions && (
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.success,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary="Revenue Impact"
secondary={`${(strategyData.performance_predictions.roi_predictions as any)?.revenue_impact || '$50K'} additional monthly revenue`}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 0.5 }
}}
secondaryTypographyProps={{
variant: 'caption',
fontSize: '0.75rem',
sx: { color: ANALYSIS_CARD_STYLES.colors.text.secondary, lineHeight: 1.4 }
}}
/>
</ListItem>
)}
</List>
</Box>
</Box>
{/* Timeline Projections */}
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Timeline Projections
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.8rem',
lineHeight: 1.5
}}>
Month 1-2: Initial setup and foundation building
</Typography>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.8rem',
lineHeight: 1.5
}}>
Month 3-4: Content creation and optimization
</Typography>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.8rem',
lineHeight: 1.5
}}>
Month 5-6: Scaling and performance optimization
</Typography>
<Typography variant="body2" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.8rem',
lineHeight: 1.5
}}>
Ongoing: Continuous monitoring and improvement
</Typography>
</Box>
</Box>
</Box>
</Box>
);
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Performance Predictions"
subtitle="ROI and success metrics"
icon={<ShowChartIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={summaryContent}
details={detailedContent}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
};
export default PerformancePredictionsCard;

View File

@@ -0,0 +1,259 @@
import React, { useState, useRef } from 'react';
import {
Card,
CardContent,
Box,
Button,
Typography,
Fade,
useTheme
} from '@mui/material';
import {
ExpandMore as ExpandMoreIcon,
ExpandLess as ExpandLessIcon,
KeyboardArrowDown as ArrowDownIcon
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import {
ANALYSIS_CARD_STYLES,
getAnalysisCardStyles,
getEnhancedChipStyles
} from '../styles';
interface ProgressiveCardProps {
summary: React.ReactNode;
details: React.ReactNode;
trigger?: 'hover' | 'click';
title?: string;
subtitle?: string;
icon?: React.ReactNode;
autoCollapseDelay?: number; // milliseconds
className?: string;
}
const ProgressiveCard: React.FC<ProgressiveCardProps> = ({
summary,
details,
trigger = 'click',
title,
subtitle,
icon,
autoCollapseDelay = 3000, // 3 seconds default
className
}) => {
const [isExpanded, setIsExpanded] = useState(false);
const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const theme = useTheme();
const cardStyles = getAnalysisCardStyles();
// Handle hover interactions
const handleMouseEnter = () => {
if (trigger === 'hover') {
// Clear any existing timeout
if (hoverTimeoutRef.current) {
clearTimeout(hoverTimeoutRef.current);
hoverTimeoutRef.current = null;
}
setIsExpanded(true);
}
};
const handleMouseLeave = () => {
if (trigger === 'hover') {
// Set timeout to auto-collapse
hoverTimeoutRef.current = setTimeout(() => {
setIsExpanded(false);
hoverTimeoutRef.current = null;
}, autoCollapseDelay);
}
};
// Handle click interactions
const handleToggle = () => {
if (trigger === 'click') {
setIsExpanded(!isExpanded);
}
};
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }}
whileHover={{ y: -4 }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={className}
>
<Card sx={{
...cardStyles.card,
'& .shimmer-text': {
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.primary} 0%, ${ANALYSIS_CARD_STYLES.colors.secondary} 100%)`,
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
animation: 'shimmer 2s ease-in-out infinite'
},
'& .bounce-icon': {
animation: 'bounce 2s infinite'
},
'@keyframes shimmer': {
'0%, 100%': { opacity: 1 },
'50%': { opacity: 0.8 }
},
'@keyframes bounce': {
'0%, 20%, 50%, 80%, 100%': { transform: 'translateY(0)' },
'40%': { transform: 'translateY(-4px)' },
'60%': { transform: 'translateY(-2px)' }
}
}}>
<CardContent sx={cardStyles.cardContent}>
{/* Header Section */}
{title && (
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: 2
}}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{icon && (
<Box sx={{
p: 1,
borderRadius: 2,
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.primary} 0%, ${ANALYSIS_CARD_STYLES.colors.secondary} 100%)`,
mr: 1.5,
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.primary}30`
}}>
{icon}
</Box>
)}
<Box>
<Typography variant="h6" className="shimmer-text" sx={{
fontWeight: 600
}}>
{title}
</Typography>
{subtitle && (
<Typography variant="caption" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: '0.75rem'
}}>
{subtitle}
</Typography>
)}
</Box>
</Box>
{/* Trigger Button */}
{trigger === 'click' && (
<Button
onClick={handleToggle}
variant="text"
size="small"
sx={{
color: ANALYSIS_CARD_STYLES.colors.primary,
'&:hover': {
background: 'rgba(102, 126, 234, 0.1)'
},
minWidth: 'auto',
px: 1.5,
py: 0.5,
borderRadius: 2,
fontSize: '0.75rem',
fontWeight: 600,
textTransform: 'none'
}}
endIcon={
isExpanded ? (
<ExpandLessIcon sx={{ fontSize: 16 }} />
) : (
<ExpandMoreIcon sx={{ fontSize: 16 }} />
)
}
>
{isExpanded ? 'Show Less' : 'Read More'}
</Button>
)}
</Box>
)}
{/* Summary Section - Always Visible */}
<Box sx={{ mb: trigger === 'click' ? 2 : 0 }}>
{summary}
</Box>
{/* Progressive Details Section */}
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{
height: 0,
opacity: 0,
overflow: 'hidden'
}}
animate={{
height: 'auto',
opacity: 1,
overflow: 'visible'
}}
exit={{
height: 0,
opacity: 0,
overflow: 'hidden'
}}
transition={{
duration: 0.4,
ease: [0.4, 0.0, 0.2, 1],
opacity: { duration: 0.3 }
}}
>
<Fade in={isExpanded} timeout={300}>
<Box sx={{
pt: 2,
borderTop: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`,
opacity: 0.9
}}>
{details}
</Box>
</Fade>
</motion.div>
)}
</AnimatePresence>
{/* Hover Indicator (for hover trigger) */}
{trigger === 'hover' && !isExpanded && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 0.6 }}
transition={{ duration: 0.3 }}
>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mt: 1,
py: 0.5
}}>
<ArrowDownIcon className="bounce-icon" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontSize: 16
}} />
<Typography variant="caption" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
ml: 0.5,
fontSize: '0.7rem'
}}>
Hover to see more
</Typography>
</Box>
</motion.div>
)}
</CardContent>
</Card>
</motion.div>
);
};
export default ProgressiveCard;

View File

@@ -0,0 +1,413 @@
import React from 'react';
import {
Grid,
Typography,
Box,
Chip
} from '@mui/material';
import {
Lightbulb as LightbulbIcon,
TrendingUp as TrendingUpIcon,
Security as SecurityIcon,
Schedule as ScheduleIcon,
Assessment as AssessmentIcon
} from '@mui/icons-material';
import ProgressiveCard from './ProgressiveCard';
import { ANALYSIS_CARD_STYLES, getEnhancedChipStyles } from '../styles';
const ProgressiveDemo: React.FC = () => {
return (
<Box sx={{ p: 3 }}>
<Typography variant="h4" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.primary,
mb: 3,
textAlign: 'center',
fontWeight: 600
}}>
Progressive Disclosure Demo
</Typography>
<Typography variant="body1" sx={{
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
mb: 4,
textAlign: 'center',
maxWidth: 800,
mx: 'auto'
}}>
Experience the new progressive disclosure system. Cards show a summary initially,
with detailed content revealed through user interaction.
</Typography>
<Grid container spacing={3}>
{/* Click Trigger Example */}
<Grid item xs={12} md={6}>
<ProgressiveCard
title="Strategic Insights"
subtitle="Click to expand"
icon={<LightbulbIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: 2
}}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.success} 0%, ${ANALYSIS_CARD_STYLES.colors.info} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600
}}>
85%
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Market Analysis
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Strong positioning identified
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label="High Growth"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
<Chip
label="6 months"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
</Box>
</Box>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Chip label="Market" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} />
<Chip label="Consumer" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip} />
<Chip label="Business" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.accent).chip} />
</Box>
</Box>
}
details={
<Box>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Detailed Strategic Insights
</Typography>
<Box sx={{
p: 2,
background: ANALYSIS_CARD_STYLES.colors.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`,
backdropFilter: 'blur(10px)'
}}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Market positioning analysis reveals strong competitive advantages
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Consumer behavior patterns indicate high engagement potential
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Business model optimization opportunities identified
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary }}>
Revenue growth projections show 25% increase potential
</Typography>
</Box>
</Box>
}
trigger="click"
/>
</Grid>
{/* Hover Trigger Example */}
<Grid item xs={12} md={6}>
<ProgressiveCard
title="Performance Predictions"
subtitle="Hover to expand"
icon={<TrendingUpIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: 2
}}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.warning} 0%, ${ANALYSIS_CARD_STYLES.colors.error} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600
}}>
92%
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
ROI Predictions
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
High success probability
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label="25% ROI"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
<Chip
label="6 months"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
</Box>
</Box>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Chip label="Traffic" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} />
<Chip label="Engagement" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip} />
<Chip label="Conversion" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.accent).chip} />
</Box>
</Box>
}
details={
<Box>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Detailed Performance Metrics
</Typography>
<Box sx={{
p: 2,
background: ANALYSIS_CARD_STYLES.colors.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`,
backdropFilter: 'blur(10px)'
}}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Traffic growth: 150% increase in organic visitors
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Engagement rate: 45% improvement in user interaction
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Conversion rate: 3.2% to 5.8% improvement
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary }}>
Revenue impact: $50K additional monthly revenue
</Typography>
</Box>
</Box>
}
trigger="hover"
autoCollapseDelay={5000}
/>
</Grid>
{/* Risk Assessment Example */}
<Grid item xs={12} md={6}>
<ProgressiveCard
title="Risk Assessment"
subtitle="Click to expand"
icon={<SecurityIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: 2
}}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.error} 0%, ${ANALYSIS_CARD_STYLES.colors.warning} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600
}}>
Med
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Risk Level
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Medium risk identified
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label="7 Risks"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.error).chip}
/>
<Chip
label="Mitigated"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
</Box>
</Box>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Chip label="Technical" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.error).chip} />
<Chip label="Market" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip} />
<Chip label="Operational" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip} />
</Box>
</Box>
}
details={
<Box>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Risk Categories & Mitigation
</Typography>
<Box sx={{
p: 2,
background: ANALYSIS_CARD_STYLES.colors.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`,
backdropFilter: 'blur(10px)'
}}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Technical Risks: Infrastructure scalability challenges
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Market Risks: Competitive pressure and market saturation
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Operational Risks: Resource allocation and team scaling
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary }}>
Mitigation: Comprehensive monitoring and contingency plans
</Typography>
</Box>
</Box>
}
trigger="click"
/>
</Grid>
{/* Implementation Roadmap Example */}
<Grid item xs={12} md={6}>
<ProgressiveCard
title="Implementation Roadmap"
subtitle="Hover to expand"
icon={<ScheduleIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: 2
}}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.info} 0%, ${ANALYSIS_CARD_STYLES.colors.primary} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600
}}>
6M
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Timeline
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
6-month implementation
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label="4 Phases"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
<Chip
label="5 Team"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
</Box>
</Box>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Chip label="Phase 1" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip} />
<Chip label="Phase 2" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip} />
<Chip label="Phase 3" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.error).chip} />
<Chip label="Phase 4" size="small" sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip} />
</Box>
</Box>
}
details={
<Box>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Implementation Phases
</Typography>
<Box sx={{
p: 2,
background: ANALYSIS_CARD_STYLES.colors.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`,
backdropFilter: 'blur(10px)'
}}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Phase 1 (Months 1-2): Foundation and setup
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Phase 2 (Months 3-4): Core implementation
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1 }}>
Phase 3 (Months 5-6): Optimization and scaling
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary }}>
Phase 4 (Ongoing): Monitoring and maintenance
</Typography>
</Box>
</Box>
}
trigger="hover"
autoCollapseDelay={4000}
/>
</Grid>
</Grid>
</Box>
);
};
export default ProgressiveDemo;

View File

@@ -0,0 +1,404 @@
import React from 'react';
import {
Grid,
Typography,
Box,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Accordion,
AccordionSummary,
AccordionDetails
} from '@mui/material';
import {
Security as SecurityIcon,
ExpandMore as ExpandMoreIcon,
Warning as WarningIcon,
CheckCircle as CheckCircleIcon,
Error as ErrorIcon,
Info as InfoIcon
} from '@mui/icons-material';
import { StrategyData } from '../types/strategy.types';
import {
ANALYSIS_CARD_STYLES,
getSectionStyles,
getAccordionStyles,
getEnhancedChipStyles,
getListItemStyles
} from '../styles';
import ProgressiveCard from './ProgressiveCard';
interface RiskAssessmentCardProps {
strategyData: StrategyData | null;
}
const RiskAssessmentCard: React.FC<RiskAssessmentCardProps> = ({ strategyData }) => {
// Get style objects
const sectionStyles = getSectionStyles();
const accordionStyles = getAccordionStyles();
const listItemStyles = getListItemStyles();
// Helper function to get risk level color
const getRiskLevelColor = (level: string) => {
const lowerLevel = level.toLowerCase();
if (lowerLevel.includes('high')) return ANALYSIS_CARD_STYLES.colors.error;
if (lowerLevel.includes('medium')) return ANALYSIS_CARD_STYLES.colors.warning;
if (lowerLevel.includes('low')) return ANALYSIS_CARD_STYLES.colors.success;
return ANALYSIS_CARD_STYLES.colors.info;
};
if (!strategyData?.risk_assessment) {
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Risk Assessment"
subtitle="Risk analysis and mitigation"
icon={<SecurityIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="body1" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Risk assessment data not available
</Typography>
</Box>
}
details={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'}
</Typography>
</Box>
}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
}
// Summary content - always visible
const summaryContent = (
<Box>
{/* Risk Overview */}
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${getRiskLevelColor(strategyData.risk_assessment.overall_risk_level || 'Medium')} 0%, ${ANALYSIS_CARD_STYLES.colors.warning} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600,
boxShadow: `0 4px 12px ${getRiskLevelColor(strategyData.risk_assessment.overall_risk_level || 'Medium')}30`
}}>
{strategyData.risk_assessment.risks?.length || 0}
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Risk Level
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
{strategyData.risk_assessment.overall_risk_level || 'Medium'} overall risk
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label={`${strategyData.risk_assessment.risks?.length || 0} Risks`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.error).chip}
/>
<Chip
label={`${strategyData.risk_assessment.mitigation_strategies?.length || 0} Mitigations`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
</Box>
</Box>
</Box>
{/* Risk Categories Preview */}
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 600 }}>
Risk Categories
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{strategyData.risk_assessment.risk_categories && Object.keys(strategyData.risk_assessment.risk_categories).slice(0, 4).map((category: string) => (
<Chip
key={category}
label={category.replace(/_/g, ' ')}
size="small"
icon={<WarningIcon />}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip}
/>
))}
{strategyData.risk_assessment.risk_categories && Object.keys(strategyData.risk_assessment.risk_categories).length > 4 && (
<Chip
label={`+${Object.keys(strategyData.risk_assessment.risk_categories).length - 4} more`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
/>
)}
</Box>
</Box>
</Box>
);
// Detailed content - shown on expansion
const detailedContent = (
<Box>
{/* Overall Risk Assessment */}
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Overall Risk Assessment
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Chip
label={strategyData.risk_assessment.overall_risk_level || 'Medium'}
size="medium"
icon={<SecurityIcon />}
sx={getEnhancedChipStyles(getRiskLevelColor(strategyData.risk_assessment.overall_risk_level || 'Medium')).chip}
/>
</Box>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
{strategyData.risk_assessment.risks?.length || 0} identified risks with {strategyData.risk_assessment.mitigation_strategies?.length || 0} mitigation strategies
</Typography>
</Box>
</Box>
{/* Individual Risks */}
{strategyData.risk_assessment.risks && strategyData.risk_assessment.risks.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Individual Risks ({strategyData.risk_assessment.risks.length})
</Typography>
{strategyData.risk_assessment.risks.map((risk: any, index: number) => (
<Accordion key={index} defaultExpanded={false} sx={accordionStyles.accordion}>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={accordionStyles.expandIcon} />}
sx={accordionStyles.accordionSummary}
>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Box sx={{ mr: 1.5 }}>
<ErrorIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.error, fontSize: 20 }} />
</Box>
<Box sx={{ flex: 1 }}>
<Typography variant="body2" sx={accordionStyles.accordionTitle}>
{typeof risk === 'string' ? risk : risk.risk || 'Risk'}
</Typography>
<Typography variant="caption" sx={accordionStyles.accordionSubtitle}>
{typeof risk === 'object' && risk.probability ? `${risk.probability} probability, ${risk.impact} impact` : 'Risk assessment'}
</Typography>
</Box>
</Box>
</AccordionSummary>
<AccordionDetails sx={{ pt: 0 }}>
<Box sx={sectionStyles.sectionContainer}>
{typeof risk === 'object' && (
<>
{/* Risk Details */}
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem', mb: 1 }}>
{risk.risk}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mb: 1 }}>
<Chip
label={`Probability: ${risk.probability || 'Unknown'}`}
size="small"
sx={getEnhancedChipStyles(
risk.probability === 'High' ? ANALYSIS_CARD_STYLES.colors.error :
risk.probability === 'Medium' ? ANALYSIS_CARD_STYLES.colors.warning :
ANALYSIS_CARD_STYLES.colors.success
).chip}
/>
<Chip
label={`Impact: ${risk.impact || 'Unknown'}`}
size="small"
sx={getEnhancedChipStyles(
risk.impact === 'High' ? ANALYSIS_CARD_STYLES.colors.error :
risk.impact === 'Medium' ? ANALYSIS_CARD_STYLES.colors.warning :
ANALYSIS_CARD_STYLES.colors.success
).chip}
/>
</Box>
</Box>
{/* Mitigation Strategy */}
{risk.mitigation && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
Mitigation Strategy
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{risk.mitigation}
</Typography>
</Box>
)}
{/* Contingency Plan */}
{risk.contingency && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.warning, fontWeight: 600, mb: 1 }}>
Contingency Plan
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{risk.contingency}
</Typography>
</Box>
)}
</>
)}
{typeof risk === 'string' && (
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{risk}
</Typography>
)}
</Box>
</AccordionDetails>
</Accordion>
))}
</Box>
)}
{/* Mitigation Strategies */}
{strategyData.risk_assessment.mitigation_strategies && strategyData.risk_assessment.mitigation_strategies.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Mitigation Strategies ({strategyData.risk_assessment.mitigation_strategies.length})
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{strategyData.risk_assessment.mitigation_strategies.map((strategy: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<CheckCircleIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontSize: 16 }} />
</ListItemIcon>
<ListItemText
primary={strategy}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
</Box>
)}
{/* Risk Categories */}
{strategyData.risk_assessment.risk_categories && Object.keys(strategyData.risk_assessment.risk_categories).length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Risk Categories
</Typography>
{Object.entries(strategyData.risk_assessment.risk_categories).map(([category, risks]: [string, any]) => (
<Accordion key={category} defaultExpanded={false} sx={accordionStyles.accordion}>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={accordionStyles.expandIcon} />}
sx={accordionStyles.accordionSummary}
>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Box sx={{ mr: 1.5 }}>
<WarningIcon sx={{ color: ANALYSIS_CARD_STYLES.colors.warning, fontSize: 20 }} />
</Box>
<Box sx={{ flex: 1 }}>
<Typography variant="body2" sx={accordionStyles.accordionTitle}>
{category.replace(/_/g, ' ')}
</Typography>
<Typography variant="caption" sx={accordionStyles.accordionSubtitle}>
{Array.isArray(risks) ? `${risks.length} risks` : 'Risk category'}
</Typography>
</Box>
</Box>
</AccordionSummary>
<AccordionDetails sx={{ pt: 0 }}>
<Box sx={sectionStyles.sectionContainer}>
{Array.isArray(risks) ? (
<List dense>
{risks.map((risk: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.warning,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={risk}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
) : (
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{typeof risks === 'string' ? risks : 'Risk category details'}
</Typography>
)}
</Box>
</AccordionDetails>
</Accordion>
))}
</Box>
)}
{/* Monitoring Framework */}
{strategyData.risk_assessment.monitoring_framework && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Monitoring Framework
</Typography>
<Box sx={sectionStyles.sectionContainer}>
{Object.entries(strategyData.risk_assessment.monitoring_framework).map(([key, value]: [string, any]) => (
<Box key={key} sx={{ mb: 1 }}>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.primary, fontWeight: 600, mb: 0.5 }}>
{key.replace(/_/g, ' ')}
</Typography>
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontSize: '0.875rem' }}>
{typeof value === 'string' ? value : JSON.stringify(value)}
</Typography>
</Box>
))}
</Box>
</Box>
)}
</Box>
);
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Risk Assessment"
subtitle="Risk analysis and mitigation"
icon={<SecurityIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={summaryContent}
details={detailedContent}
trigger="hover"
autoCollapseDelay={3000}
/>
</Grid>
);
};
export default RiskAssessmentCard;

View File

@@ -0,0 +1,330 @@
import React from 'react';
import {
Grid,
Typography,
Box,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Accordion,
AccordionSummary,
AccordionDetails
} from '@mui/material';
import {
Lightbulb as LightbulbIcon,
ExpandMore as ExpandMoreIcon,
TrendingUp as TrendingUpIcon,
Psychology as PsychologyIcon,
Business as BusinessIcon,
Analytics as AnalyticsIcon
} from '@mui/icons-material';
import { motion } from 'framer-motion';
import { StrategyData } from '../types/strategy.types';
import {
ANALYSIS_CARD_STYLES,
getSectionStyles,
getAccordionStyles,
getEnhancedChipStyles,
getListItemStyles,
getAnimationStyles
} from '../styles';
import ProgressiveCard from './ProgressiveCard';
interface StrategicInsightsCardProps {
strategyData: StrategyData | null;
}
const StrategicInsightsCard: React.FC<StrategicInsightsCardProps> = ({ strategyData }) => {
// Get style objects
const sectionStyles = getSectionStyles();
const accordionStyles = getAccordionStyles();
const listItemStyles = getListItemStyles();
const animationStyles = getAnimationStyles();
console.log('🔍 StrategicInsightsCard - strategyData:', strategyData);
console.log('🔍 StrategicInsightsCard - strategic_insights:', strategyData?.strategic_insights);
if (!strategyData?.strategic_insights) {
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Strategic Insights"
subtitle="AI-powered market analysis"
icon={<LightbulbIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="body1" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Strategic insights data not available
</Typography>
</Box>
}
details={
<Box sx={{ textAlign: 'center', py: 2 }}>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'}
</Typography>
</Box>
}
trigger="click"
/>
</Grid>
);
}
// Helper function to get insight icon
const getInsightIcon = (type: string) => {
switch (type?.toLowerCase()) {
case 'market': return <TrendingUpIcon />;
case 'consumer': return <PsychologyIcon />;
case 'business': return <BusinessIcon />;
case 'analytics': return <AnalyticsIcon />;
default: return <LightbulbIcon />;
}
};
// Summary content - always visible
const summaryContent = (
<Box>
{/* Market Analysis Summary */}
<Box sx={sectionStyles.sectionContainer}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{
width: 40,
height: 40,
borderRadius: '50%',
background: `linear-gradient(135deg, ${ANALYSIS_CARD_STYLES.colors.success} 0%, ${ANALYSIS_CARD_STYLES.colors.info} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mr: 2,
color: 'white',
fontSize: '1.2rem',
fontWeight: 600,
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.success}30`
}}>
85%
</Box>
<Box>
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
Market Analysis
</Typography>
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
Strong market positioning identified
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label="High Growth"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
/>
<Chip
label="6 months"
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
</Box>
</Box>
</Box>
{/* Key Insights Preview */}
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 600 }}>
Key Insights Preview
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{strategyData.strategic_insights.insights?.slice(0, 3).map((insight: any, index: number) => (
<Chip
key={index}
label={insight.type}
size="small"
icon={getInsightIcon(insight.type)}
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
/>
))}
{(strategyData.strategic_insights.insights?.length || 0) > 3 && (
<Chip
label={`+${(strategyData.strategic_insights.insights?.length || 0) - 3} more`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
/>
)}
</Box>
</Box>
</Box>
);
// Detailed content - shown on expansion
const detailedContent = (
<Box>
{/* Strategic Insights by Type */}
{strategyData.strategic_insights.insights && strategyData.strategic_insights.insights.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Detailed Strategic Insights ({strategyData.strategic_insights.insights.length})
</Typography>
{/* Group insights by type */}
{Object.entries(
strategyData.strategic_insights.insights.reduce((acc: any, insight: any) => {
const type = insight.type || 'General';
if (!acc[type]) acc[type] = [];
acc[type].push(insight);
return acc;
}, {})
).map(([type, insights]: [string, any]) => (
<Accordion key={type} defaultExpanded={false} sx={accordionStyles.accordion}>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={accordionStyles.expandIcon} />}
sx={accordionStyles.accordionSummary}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ mr: 1.5 }}>
{getInsightIcon(type)}
</Box>
<Box>
<Typography variant="body2" sx={accordionStyles.accordionTitle}>
{type} Insights ({insights.length})
</Typography>
<Typography variant="caption" sx={accordionStyles.accordionSubtitle}>
{insights.length} strategic {insights.length === 1 ? 'insight' : 'insights'}
</Typography>
</Box>
</Box>
</AccordionSummary>
<AccordionDetails sx={{ pt: 0 }}>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{insights.map((insight: any, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.primary,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={insight.insight}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
secondary={
<Box sx={{ mt: 0.5 }}>
<Box sx={{ display: 'flex', gap: 1, mb: 0.5 }}>
<Chip
label={`P: ${insight.priority || 'Medium'}`}
size="small"
sx={getEnhancedChipStyles(
insight.priority === 'High' ? ANALYSIS_CARD_STYLES.colors.error :
insight.priority === 'Medium' ? ANALYSIS_CARD_STYLES.colors.warning :
ANALYSIS_CARD_STYLES.colors.success
).chip}
/>
<Chip
label={`I: ${insight.estimated_impact || 'Medium'}`}
size="small"
sx={getEnhancedChipStyles(
insight.estimated_impact === 'High' ? ANALYSIS_CARD_STYLES.colors.error :
insight.estimated_impact === 'Medium' ? ANALYSIS_CARD_STYLES.colors.warning :
ANALYSIS_CARD_STYLES.colors.success
).chip}
/>
<Chip
label={`C: ${insight.confidence_level || 'Medium'}`}
size="small"
sx={getEnhancedChipStyles(
insight.confidence_level === 'High' ? ANALYSIS_CARD_STYLES.colors.success :
insight.confidence_level === 'Medium' ? ANALYSIS_CARD_STYLES.colors.warning :
ANALYSIS_CARD_STYLES.colors.error
).chip}
/>
<Chip
label={`T: ${insight.implementation_time || '3 months'}`}
size="small"
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
/>
</Box>
{insight.reasoning && (
<Typography variant="caption" sx={{
display: 'block',
fontSize: '0.75rem',
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
fontStyle: 'italic'
}}>
<strong>Reasoning:</strong> {insight.reasoning}
</Typography>
)}
</Box>
}
/>
</ListItem>
))}
</List>
</Box>
</AccordionDetails>
</Accordion>
))}
</Box>
)}
{/* Content Opportunities */}
{strategyData.strategic_insights.content_opportunities && strategyData.strategic_insights.content_opportunities.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
Content Opportunities ({strategyData.strategic_insights.content_opportunities.length})
</Typography>
<Box sx={sectionStyles.sectionContainer}>
<List dense>
{strategyData.strategic_insights.content_opportunities.map((opportunity: string, index: number) => (
<ListItem key={index} sx={listItemStyles.listItem}>
<ListItemIcon sx={listItemStyles.listItemIcon}>
<Box sx={{
width: 6,
height: 6,
borderRadius: '50%',
background: ANALYSIS_CARD_STYLES.colors.success,
opacity: 0.7
}} />
</ListItemIcon>
<ListItemText
primary={opportunity}
primaryTypographyProps={{
variant: 'body2',
fontSize: '0.875rem',
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
}}
/>
</ListItem>
))}
</List>
</Box>
</Box>
)}
</Box>
);
return (
<Grid item xs={12} lg={6}>
<ProgressiveCard
title="Strategic Insights"
subtitle="AI-powered market analysis"
icon={<LightbulbIcon sx={{ color: 'white', fontSize: 20 }} />}
summary={summaryContent}
details={detailedContent}
trigger="hover"
autoCollapseDelay={2000}
/>
</Grid>
);
};
export default StrategicInsightsCard;

View File

@@ -0,0 +1,162 @@
import React from 'react';
import {
Box,
Button,
Alert
} from '@mui/material';
import {
Check as CheckIcon,
CalendarToday as CalendarIcon,
Refresh as RefreshIcon
} from '@mui/icons-material';
import { motion } from 'framer-motion';
import { StrategyData } from '../types/strategy.types';
interface StrategyActionsProps {
strategyData: StrategyData | null;
strategyConfirmed: boolean;
onConfirmStrategy: () => void;
onGenerateContentCalendar: () => void;
onRefreshData: () => void;
}
const StrategyActions: React.FC<StrategyActionsProps> = ({
strategyData,
strategyConfirmed,
onConfirmStrategy,
onGenerateContentCalendar,
onRefreshData
}) => {
return (
<Box sx={{ mt: 4 }}>
{/* Strategy Confirmation Status */}
{strategyConfirmed && (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Alert
severity="success"
sx={{
mb: 3,
borderRadius: 2,
boxShadow: '0 4px 12px rgba(76, 175, 80, 0.2)',
border: '1px solid rgba(76, 175, 80, 0.3)'
}}
action={
<Button
color="inherit"
size="small"
onClick={onGenerateContentCalendar}
startIcon={<CalendarIcon />}
sx={{
fontWeight: 600,
'&:hover': {
backgroundColor: 'rgba(76, 175, 80, 0.1)',
transform: 'translateY(-1px)'
},
transition: 'all 0.3s ease'
}}
>
Generate Content Calendar
</Button>
}
>
Strategy confirmed! You can now generate a content calendar based on this strategy.
</Alert>
</motion.div>
)}
{/* Action Buttons */}
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
{!strategyConfirmed ? (
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Button
variant="contained"
size="large"
onClick={onConfirmStrategy}
startIcon={<CheckIcon />}
sx={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: 3,
px: 4,
py: 1.5,
fontWeight: 600,
boxShadow: '0 8px 32px rgba(102, 126, 234, 0.3)',
'&:hover': {
background: 'linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%)',
boxShadow: '0 12px 40px rgba(102, 126, 234, 0.4)',
transform: 'translateY(-2px)'
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
}}
>
Confirm Strategy
</Button>
</motion.div>
) : (
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Button
variant="contained"
size="large"
onClick={onGenerateContentCalendar}
startIcon={<CalendarIcon />}
color="success"
sx={{
borderRadius: 3,
px: 4,
py: 1.5,
fontWeight: 600,
boxShadow: '0 8px 32px rgba(76, 175, 80, 0.3)',
'&:hover': {
boxShadow: '0 12px 40px rgba(76, 175, 80, 0.4)',
transform: 'translateY(-2px)'
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
}}
>
Generate Content Calendar
</Button>
</motion.div>
)}
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Button
variant="outlined"
size="large"
onClick={onRefreshData}
startIcon={<RefreshIcon />}
sx={{
borderRadius: 3,
px: 4,
py: 1.5,
fontWeight: 600,
borderColor: 'rgba(102, 126, 234, 0.3)',
color: '#667eea',
'&:hover': {
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.05)',
transform: 'translateY(-2px)'
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
}}
>
Refresh Data
</Button>
</motion.div>
</Box>
</Box>
);
};
export default StrategyActions;

View File

@@ -0,0 +1,619 @@
import React, { useState } from 'react';
import {
Paper,
Grid,
Typography,
Chip,
Box,
Tooltip,
LinearProgress,
Badge,
Card,
CardContent,
CircularProgress,
Button
} from '@mui/material';
import {
Psychology as PsychologyIcon,
TrendingUp as TrendingUpIcon,
CheckCircle as CheckCircleIcon,
Warning as WarningIcon,
Schedule as ScheduleIcon,
CalendarToday as CalendarTodayIcon,
AutoAwesome as AutoAwesomeIcon,
Timeline as TimelineIcon,
Info as InfoIcon,
Analytics as AnalyticsIcon,
Assessment as AssessmentIcon,
Business as BusinessIcon,
ShowChart as ShowChartIcon,
Security as SecurityIcon,
ArrowForward as ArrowForwardIcon,
Star as StarIcon,
DataUsage as DataUsageIcon,
Input as InputIcon,
Storage as StorageIcon,
Person as PersonIcon
} from '@mui/icons-material';
import { motion } from 'framer-motion';
import { StrategyData } from '../types/strategy.types';
import { getStrategyName, getStrategyGenerationDate } from '../utils/strategyTransformers';
interface StrategyHeaderProps {
strategyData: StrategyData | null;
strategyConfirmed: boolean;
}
const StrategyHeader: React.FC<StrategyHeaderProps> = ({ strategyData, strategyConfirmed }) => {
const [showNextStepText, setShowNextStepText] = useState(false);
if (!strategyData) return null;
// Helper function to get percentage value from string
const getPercentageValue = (value: string | undefined) => {
if (!value) return 0;
const match = value.match(/(\d+)/);
return match ? parseInt(match[1]) : 0;
};
// Helper function to get risk color
const getRiskColor = (riskLevel: string | undefined) => {
switch (riskLevel?.toLowerCase()) {
case 'low': return '#4caf50';
case 'medium': return '#ff9800';
case 'high': return '#f44336';
case 'high-medium': return '#ff5722';
default: return '#ff9800';
}
};
// Helper function to extract company name from strategy name
const getCompanyName = (strategyName: string) => {
// Extract company name from strategy name (e.g., "Enhanced Content Strategy" -> "ALwrity")
// For now, we'll use a default company name
return "ALwrity";
};
// Helper function to get timeline percentage
const getTimelinePercentage = (timeline: string | undefined) => {
if (!timeline) return 0;
const match = timeline.match(/(\d+)/);
return match ? Math.min(parseInt(match[1]) * 10, 100) : 0; // Convert months to percentage
};
const roiValue = getPercentageValue(strategyData.summary?.estimated_roi);
const successValue = getPercentageValue(strategyData.summary?.success_probability);
const timelineValue = getTimelinePercentage(strategyData.summary?.implementation_timeline);
const riskColor = getRiskColor(strategyData.summary?.risk_level);
const companyName = getCompanyName(getStrategyName(strategyData));
// Analysis components data
const analysisComponents = [
{ name: 'Strategic Insights', icon: <AnalyticsIcon />, color: '#667eea' },
{ name: 'Competitive Analysis', icon: <BusinessIcon />, color: '#4caf50' },
{ name: 'Performance Predictions', icon: <ShowChartIcon />, color: '#2196f3' },
{ name: 'Implementation Roadmap', icon: <TimelineIcon />, color: '#ff9800' },
{ name: 'Risk Assessment', icon: <SecurityIcon />, color: '#f44336' }
];
// Mock data sources and user inputs (replace with actual data from strategyData)
const dataSources = [
{ name: 'Industry Analysis', type: 'Market Research', icon: <DataUsageIcon /> },
{ name: 'Competitor Data', type: 'External Sources', icon: <StorageIcon /> },
{ name: 'User Preferences', type: 'Input Survey', icon: <PersonIcon /> },
{ name: 'Content Performance', type: 'Analytics', icon: <InputIcon /> }
];
return (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
>
<Card
sx={{
mb: 3,
background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #533483 100%)',
color: 'white',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(102, 126, 234, 0.3)',
borderRadius: 3,
position: 'relative',
overflow: 'hidden',
border: '1px solid rgba(102, 126, 234, 0.3)',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%)',
pointerEvents: 'none'
},
'&::after': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.1) 50%, transparent 70%)',
animation: 'shimmer 3s infinite',
pointerEvents: 'none'
},
'@keyframes shimmer': {
'0%': { transform: 'translateX(-100%)' },
'100%': { transform: 'translateX(100%)' }
},
'@keyframes gradient': {
'0%': { backgroundPosition: '0% 50%' },
'50%': { backgroundPosition: '100% 50%' },
'100%': { backgroundPosition: '0% 50%' }
}
}}
>
{/* Animated Border Lights */}
<motion.div
animate={{
boxShadow: [
'0 0 20px rgba(102, 126, 234, 0.5)',
'0 0 40px rgba(102, 126, 234, 0.8)',
'0 0 20px rgba(102, 126, 234, 0.5)'
]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
borderRadius: '12px',
pointerEvents: 'none'
}}
/>
<CardContent sx={{ position: 'relative', zIndex: 1, p: 2.5 }}>
{/* Header Section - More Compact */}
<Grid container spacing={2} alignItems="center" sx={{ mb: 1.5 }}>
<Grid item xs={12} md={6}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 0.5 }}>
<motion.div
animate={{ rotate: [0, 10, -10, 0] }}
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
>
<PsychologyIcon sx={{ fontSize: 28, color: '#667eea', mr: 1.5 }} />
</motion.div>
<Box>
<Typography variant="h5" sx={{
fontWeight: 800,
background: 'linear-gradient(45deg, #667eea, #764ba2, #f093fb)',
backgroundSize: '200% 200%',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
animation: 'gradient 3s ease infinite',
mb: 0.25
}}>
{companyName} Content Strategy
</Typography>
<Typography variant="body2" sx={{
opacity: 0.8,
color: '#e0e0e0',
display: 'flex',
alignItems: 'center',
gap: 1,
fontSize: '0.75rem'
}}>
<ScheduleIcon sx={{ fontSize: 12 }} />
Generated on {getStrategyGenerationDate(strategyData)}
</Typography>
</Box>
</Box>
{/* Strategy Metadata Chips - More Compact */}
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75, mb: 1 }}>
<Tooltip title="AI-Generated Strategy using advanced machine learning" arrow>
<Badge badgeContent={<CheckCircleIcon sx={{ fontSize: 10, color: '#4caf50' }} />} color="default">
<Chip
icon={<AutoAwesomeIcon />}
label="AI Generated"
size="small"
sx={{
background: 'rgba(102, 126, 234, 0.2)',
color: '#667eea',
border: '1px solid rgba(102, 126, 234, 0.3)',
fontWeight: 600,
fontSize: '0.65rem',
height: 24
}}
/>
</Badge>
</Tooltip>
<Tooltip title="Comprehensive analysis covering all strategic aspects" arrow>
<Badge badgeContent={<CheckCircleIcon sx={{ fontSize: 10, color: '#4caf50' }} />} color="default">
<Chip
icon={<PsychologyIcon />}
label="Comprehensive"
size="small"
sx={{
background: 'rgba(76, 175, 80, 0.2)',
color: '#4caf50',
border: '1px solid rgba(76, 175, 80, 0.3)',
fontWeight: 600,
fontSize: '0.65rem',
height: 24
}}
/>
</Badge>
</Tooltip>
<Tooltip title="Content calendar generation status" arrow>
<Badge badgeContent={<WarningIcon sx={{ fontSize: 10, color: '#ff9800' }} />} color="default">
<Chip
icon={<CalendarTodayIcon />}
label={strategyData.strategy_metadata?.content_calendar_ready ? "Calendar Ready" : "Calendar Pending"}
size="small"
color={strategyData.strategy_metadata?.content_calendar_ready ? "success" : "warning"}
sx={{
background: strategyData.strategy_metadata?.content_calendar_ready
? 'rgba(76, 175, 80, 0.2)'
: 'rgba(255, 152, 0, 0.2)',
border: '1px solid rgba(255, 255, 255, 0.2)',
fontWeight: 600,
fontSize: '0.65rem',
height: 24
}}
/>
</Badge>
</Tooltip>
</Box>
</Grid>
{/* Key Metrics Section with 4 Circular Progress Charts */}
<Grid item xs={12} md={6}>
<Box sx={{ display: 'flex', justifyContent: 'space-around', alignItems: 'center' }}>
{/* ROI Circular Progress */}
<Tooltip title="Estimated Return on Investment for content marketing efforts" arrow>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0.5 }}>
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress
variant="determinate"
value={roiValue}
size={50}
thickness={3}
sx={{
color: '#4caf50',
'& .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" sx={{ color: '#4caf50', fontWeight: 700, fontSize: '0.6rem' }}>
{roiValue}%
</Typography>
</Box>
</Box>
<Typography variant="caption" sx={{ color: '#4caf50', fontWeight: 600, fontSize: '0.65rem' }}>
ROI
</Typography>
</Box>
</Tooltip>
{/* Success Probability Circular Progress */}
<Tooltip title="Probability of achieving the defined content strategy goals" arrow>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0.5 }}>
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress
variant="determinate"
value={successValue}
size={50}
thickness={3}
sx={{
color: '#2196f3',
'& .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" sx={{ color: '#2196f3', fontWeight: 700, fontSize: '0.6rem' }}>
{successValue}%
</Typography>
</Box>
</Box>
<Typography variant="caption" sx={{ color: '#2196f3', fontWeight: 600, fontSize: '0.65rem' }}>
Success
</Typography>
</Box>
</Tooltip>
{/* Risk Level Circular Progress */}
<Tooltip title="Overall risk assessment for the content strategy implementation" arrow>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0.5 }}>
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress
variant="determinate"
value={strategyData.summary?.risk_level === 'Low' ? 25 :
strategyData.summary?.risk_level === 'Medium' ? 50 :
strategyData.summary?.risk_level === 'High' ? 75 : 60}
size={50}
thickness={3}
sx={{
color: riskColor,
'& .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" sx={{ color: riskColor, fontWeight: 700, fontSize: '0.6rem' }}>
{strategyData.summary?.risk_level === 'Low' ? 'L' :
strategyData.summary?.risk_level === 'Medium' ? 'M' :
strategyData.summary?.risk_level === 'High' ? 'H' : 'HM'}
</Typography>
</Box>
</Box>
<Typography variant="caption" sx={{ color: riskColor, fontWeight: 600, fontSize: '0.65rem' }}>
Risk
</Typography>
</Box>
</Tooltip>
{/* Timeline Circular Progress */}
<Tooltip title="Implementation timeline for the content strategy" arrow>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0.5 }}>
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress
variant="determinate"
value={timelineValue}
size={50}
thickness={3}
sx={{
color: '#667eea',
'& .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" sx={{ color: '#667eea', fontWeight: 700, fontSize: '0.6rem' }}>
{strategyData.summary?.implementation_timeline?.match(/\d+/)?.[0] || '6'}m
</Typography>
</Box>
</Box>
<Typography variant="caption" sx={{ color: '#667eea', fontWeight: 600, fontSize: '0.65rem' }}>
Timeline
</Typography>
</Box>
</Tooltip>
</Box>
</Grid>
</Grid>
{/* Strategy Status Section - Expanded to show data sources and analysis components */}
<Grid container spacing={2}>
<Grid item xs={12}>
<Box sx={{
background: 'rgba(255, 255, 255, 0.05)',
p: 2,
borderRadius: 2,
border: '1px solid rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)'
}}>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 1.5, color: '#667eea', fontSize: '0.9rem' }}>
Strategy Status & Data Sources
</Typography>
<Grid container spacing={2}>
{/* Status Info */}
<Grid item xs={12} md={4}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<TrendingUpIcon sx={{ color: '#4caf50', fontSize: 16 }} />
<Box>
<Typography variant="caption" sx={{ color: '#e0e0e0', fontWeight: 500, fontSize: '0.7rem' }}>
Status
</Typography>
<Typography variant="body2" sx={{ color: 'white', fontWeight: 600, fontSize: '0.75rem' }}>
{strategyConfirmed ? 'Confirmed' : 'Pending Review'}
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<InfoIcon sx={{ color: '#2196f3', fontSize: 16 }} />
<Box>
<Typography variant="caption" sx={{ color: '#e0e0e0', fontWeight: 500, fontSize: '0.7rem' }}>
User ID
</Typography>
<Typography variant="body2" sx={{ color: 'white', fontWeight: 600, fontSize: '0.75rem' }}>
{strategyData.strategy_metadata?.user_id}
</Typography>
</Box>
</Box>
</Box>
</Grid>
{/* Data Sources */}
<Grid item xs={12} md={8}>
<Box>
<Typography variant="caption" sx={{ color: '#e0e0e0', fontWeight: 500, fontSize: '0.7rem', mb: 1, display: 'block' }}>
Data Sources & User Inputs
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75 }}>
{dataSources.map((source, index) => (
<Chip
key={index}
icon={source.icon}
label={`${source.name} (${source.type})`}
size="small"
sx={{
background: 'linear-gradient(135deg, rgba(102, 126, 234, 0.3) 0%, rgba(102, 126, 234, 0.2) 100%)',
color: '#667eea',
border: '1px solid rgba(102, 126, 234, 0.4)',
fontWeight: 600,
fontSize: '0.65rem',
height: 24,
boxShadow: '0 2px 8px rgba(102, 126, 234, 0.2)',
'&:hover': {
background: 'linear-gradient(135deg, rgba(102, 126, 234, 0.4) 0%, rgba(102, 126, 234, 0.3) 100%)',
boxShadow: '0 4px 12px rgba(102, 126, 234, 0.3)',
transform: 'translateY(-1px)'
},
transition: 'all 0.2s ease'
}}
/>
))}
</Box>
</Box>
</Grid>
</Grid>
{/* Analysis Components Section - Moved inside the card */}
<Box sx={{ mt: 2, pt: 2, borderTop: '1px solid rgba(255, 255, 255, 0.1)' }}>
<Typography variant="caption" sx={{ color: '#e0e0e0', fontWeight: 500, fontSize: '0.7rem', mb: 1, display: 'block' }}>
Analysis Components
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75 }}>
{analysisComponents.map((component, index) => (
<Tooltip key={index} title={`${component.name} analysis completed`} arrow>
<Badge badgeContent={<CheckCircleIcon sx={{ fontSize: 10, color: '#4caf50' }} />} color="default">
<Chip
icon={component.icon}
label={component.name}
size="small"
sx={{
background: `linear-gradient(135deg, ${component.color}30 0%, ${component.color}20 100%)`,
color: component.color,
border: `1px solid ${component.color}50`,
fontWeight: 600,
fontSize: '0.65rem',
height: 24,
boxShadow: `0 2px 8px ${component.color}20`,
'&:hover': {
background: `linear-gradient(135deg, ${component.color}40 0%, ${component.color}30 100%)`,
boxShadow: `0 4px 12px ${component.color}30`,
transform: 'translateY(-1px)'
},
transition: 'all 0.2s ease'
}}
/>
</Badge>
</Tooltip>
))}
<Chip
icon={<StarIcon />}
label="Ready for Review"
size="small"
color="success"
sx={{
background: 'linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, rgba(76, 175, 80, 0.2) 100%)',
color: '#4caf50',
border: '1px solid rgba(76, 175, 80, 0.4)',
fontWeight: 600,
fontSize: '0.65rem',
height: 24,
boxShadow: '0 2px 8px rgba(76, 175, 80, 0.2)',
'&:hover': {
background: 'linear-gradient(135deg, rgba(76, 175, 80, 0.4) 0%, rgba(76, 175, 80, 0.3) 100%)',
boxShadow: '0 4px 12px rgba(76, 175, 80, 0.3)',
transform: 'translateY(-1px)'
},
transition: 'all 0.2s ease'
}}
/>
</Box>
</Box>
</Box>
</Grid>
</Grid>
{/* Next Steps Button - Area B */}
<Box sx={{ mt: 1.5, display: 'flex', justifyContent: 'center' }}>
<Tooltip
title={strategyData.summary?.next_step || "Review strategy and generate content calendar"}
arrow
open={showNextStepText}
onClose={() => setShowNextStepText(false)}
>
<Button
variant="contained"
onMouseEnter={() => setShowNextStepText(true)}
onMouseLeave={() => setShowNextStepText(false)}
sx={{
background: 'linear-gradient(135deg, #4caf50 0%, #66bb6a 50%, #81c784 100%)',
color: 'white',
fontWeight: 700,
fontSize: '0.8rem',
px: 3,
py: 1,
borderRadius: 3,
boxShadow: '0 4px 15px rgba(76, 175, 80, 0.4)',
border: '2px solid rgba(76, 175, 80, 0.3)',
'&:hover': {
background: 'linear-gradient(135deg, #66bb6a 0%, #81c784 50%, #a5d6a7 100%)',
boxShadow: '0 6px 20px rgba(76, 175, 80, 0.6)',
transform: 'translateY(-2px)'
},
transition: 'all 0.3s ease',
textTransform: 'none'
}}
startIcon={<ArrowForwardIcon />}
>
Next Step
</Button>
</Tooltip>
</Box>
</CardContent>
</Card>
</motion.div>
);
};
export default StrategyHeader;

View File

@@ -0,0 +1,33 @@
import React from 'react';
import { Box, Typography, Paper } from '@mui/material';
import { StrategyData } from '../types/strategy.types';
interface TestComponentProps {
strategyData: StrategyData | null;
}
const TestComponent: React.FC<TestComponentProps> = ({ strategyData }) => {
if (!strategyData) return null;
return (
<Paper sx={{ p: 2, mb: 2, background: 'rgba(76, 175, 80, 0.1)' }}>
<Typography variant="h6" color="success.main" gutterBottom>
Modular Structure Test
</Typography>
<Typography variant="body2">
Strategy Name: {strategyData.strategy_metadata?.strategy_name || strategyData.metadata?.strategy_name}
</Typography>
<Typography variant="body2">
ROI: {strategyData.summary?.estimated_roi}
</Typography>
<Typography variant="body2">
Success Probability: {strategyData.summary?.success_probability}
</Typography>
<Typography variant="body2" color="success.main">
Modular structure is working correctly!
</Typography>
</Paper>
);
};
export default TestComponent;

View File

@@ -0,0 +1,84 @@
import { useState } from 'react';
import { contentPlanningApi } from '../../../../../services/contentPlanningApi';
import { StrategyData } from '../types/strategy.types';
export const useStrategyActions = () => {
const [strategyConfirmed, setStrategyConfirmed] = useState(false);
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const handleConfirmStrategy = () => {
setShowConfirmDialog(true);
};
const confirmStrategy = async (strategyData: StrategyData | null) => {
try {
setStrategyConfirmed(true);
setShowConfirmDialog(false);
// Save confirmation status to backend
const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id;
if (userId) {
try {
// Update the strategy with confirmation status
await contentPlanningApi.updateEnhancedStrategy(
userId.toString(),
{ confirmed: true, confirmed_at: new Date().toISOString() }
);
console.log('Strategy confirmation saved to backend');
} catch (updateError) {
console.warn('Could not save confirmation to backend:', updateError);
// Don't fail the confirmation if backend update fails
}
}
console.log('Strategy confirmed! Ready to generate content calendar.');
} catch (error) {
console.error('Error confirming strategy:', error);
setStrategyConfirmed(false);
}
};
const handleGenerateContentCalendar = async (strategyData: StrategyData | null) => {
try {
const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id;
if (!userId) {
console.error('No strategy data available for calendar generation');
return;
}
// Generate content calendar based on confirmed strategy
const calendarRequest = {
user_id: userId,
strategy_id: userId, // Using user_id as strategy_id for now
calendar_type: 'comprehensive',
industry: strategyData.base_strategy?.industry || 'technology',
business_size: 'medium', // TODO: Get from strategy data
force_refresh: false
};
console.log('Generating content calendar with request:', calendarRequest);
// Call the calendar generation API
const calendarResponse = await contentPlanningApi.generateCalendar(calendarRequest);
console.log('Content calendar generated successfully:', calendarResponse);
// TODO: Navigate to calendar tab or show success message
// You could also store the calendar data in a global state
} catch (error) {
console.error('Error generating content calendar:', error);
// Show error message to user
throw new Error('Failed to generate content calendar. Please try again.');
}
};
return {
strategyConfirmed,
showConfirmDialog,
setShowConfirmDialog,
handleConfirmStrategy,
confirmStrategy,
handleGenerateContentCalendar
};
};

View File

@@ -0,0 +1,178 @@
import { useState, useEffect } from 'react';
import { contentPlanningApi } from '../../../../../services/contentPlanningApi';
import { StrategyData } from '../types/strategy.types';
import {
getUserId,
transformPollingStrategyData,
transformFullStructureData,
transformSwotToComprehensiveStructure,
hasFullStructure
} from '../utils/strategyTransformers';
export const useStrategyData = () => {
const [strategyData, setStrategyData] = useState<StrategyData | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const loadStrategyData = async (forceRefresh = false) => {
try {
setLoading(true);
setError(null);
const userId = getUserId();
// First, try to get the latest generated strategy from the polling system
try {
const latestStrategyResponse = await contentPlanningApi.getLatestGeneratedStrategy(userId);
if (latestStrategyResponse?.strategy) {
console.log('✅ Found latest generated strategy from polling system:', latestStrategyResponse.strategy);
const transformedStrategy = transformPollingStrategyData(latestStrategyResponse.strategy);
console.log('🔄 Transformed strategy data:', transformedStrategy);
setStrategyData(transformedStrategy);
setLoading(false);
return;
}
} catch (pollingError) {
console.log('No latest strategy found in polling system, checking database...', pollingError);
}
// If no strategy found in polling system, try to get from database
try {
const strategiesResponse = await contentPlanningApi.getEnhancedStrategies(userId);
// Handle the enhanced strategies response structure
const strategies = strategiesResponse?.data?.strategies || strategiesResponse?.strategies || [];
if (strategies && strategies.length > 0) {
// Get the most recent strategy (assuming it's sorted by creation date)
const latestStrategy = strategies[0];
// Check if this strategy has comprehensive AI-generated data
if (latestStrategy.comprehensive_ai_analysis) {
console.log('✅ Found comprehensive strategy in database:', latestStrategy);
console.log('📊 Comprehensive AI analysis structure:', latestStrategy.comprehensive_ai_analysis);
console.log('🔍 Available fields:', Object.keys(latestStrategy.comprehensive_ai_analysis));
// Check if this is the full 5-component structure or SWOT analysis
if (hasFullStructure(latestStrategy.comprehensive_ai_analysis)) {
// Transform the data to match frontend expectations (full 5-component structure)
const transformedStrategy = transformFullStructureData(latestStrategy);
console.log('🔄 Transformed enhanced strategy data (full structure):', transformedStrategy);
console.log('🎯 Final strategy data structure:', {
hasStrategicInsights: !!transformedStrategy.strategic_insights,
hasCompetitiveAnalysis: !!transformedStrategy.competitive_analysis,
hasPerformancePredictions: !!transformedStrategy.performance_predictions,
hasImplementationRoadmap: !!transformedStrategy.implementation_roadmap,
hasRiskAssessment: !!transformedStrategy.risk_assessment,
hasSummary: !!transformedStrategy.summary
});
setStrategyData(transformedStrategy);
setLoading(false);
return;
} else {
// This is SWOT analysis, create a comprehensive 5-component structure enhanced with SWOT data
console.log('🔄 Creating comprehensive 5-component structure from SWOT analysis');
const transformedStrategy = transformSwotToComprehensiveStructure(latestStrategy);
console.log('🔄 Created comprehensive 5-component structure from SWOT analysis:', transformedStrategy);
console.log('🎯 Final strategy data structure:', {
hasStrategicInsights: !!transformedStrategy.strategic_insights,
hasCompetitiveAnalysis: !!transformedStrategy.competitive_analysis,
hasPerformancePredictions: !!transformedStrategy.performance_predictions,
hasImplementationRoadmap: !!transformedStrategy.implementation_roadmap,
hasRiskAssessment: !!transformedStrategy.risk_assessment,
hasSummary: !!transformedStrategy.summary,
swotEnhanced: true
});
setStrategyData(transformedStrategy);
setLoading(false);
return;
}
} else {
console.log('⚠️ Strategy found but no comprehensive_ai_analysis field:', {
strategyId: latestStrategy.id,
strategyName: latestStrategy.name,
availableFields: Object.keys(latestStrategy)
});
}
}
} catch (dbError) {
console.log('No comprehensive strategies found in database, checking for recent generation...', dbError);
}
// If no comprehensive strategy found in database, check for recent generation tasks
try {
// Try to get the latest strategy generation result
const recentStrategies = await contentPlanningApi.getStrategies(userId);
// Handle the enhanced strategies response structure
const strategies = recentStrategies?.data?.strategies || recentStrategies?.strategies || [];
if (strategies && strategies.length > 0) {
// Find the most recent AI-generated strategy
const aiGeneratedStrategy = strategies.find(
(strategy: any) => strategy.comprehensive_ai_analysis
);
if (aiGeneratedStrategy && aiGeneratedStrategy.comprehensive_ai_analysis) {
console.log('✅ Found AI-generated strategy in recent strategies:', aiGeneratedStrategy);
// Transform the data to match frontend expectations
const transformedStrategy = transformPollingStrategyData(aiGeneratedStrategy.comprehensive_ai_analysis);
console.log('🔄 Transformed recent strategy data:', transformedStrategy);
setStrategyData(transformedStrategy);
setLoading(false);
return;
}
}
} catch (strategyError) {
console.log('No recent strategies found, checking for generation tasks...', strategyError);
}
// If no strategy data is available, show appropriate message
console.log('❌ No comprehensive strategy data found');
setStrategyData(null);
setError('No comprehensive strategy data available. Please generate a strategy first.');
setLoading(false);
} catch (err: any) {
console.error('Error loading strategy data:', err);
setError(err.message || 'Failed to load strategy data');
setStrategyData(null);
setLoading(false);
}
};
// Load data on mount and when component becomes visible
useEffect(() => {
// Always refresh data when component mounts to ensure we get the latest strategy
loadStrategyData(true);
// Also set up a listener for when the tab becomes visible (for better UX)
const handleVisibilityChange = () => {
if (!document.hidden) {
// Tab became visible, refresh data
loadStrategyData(true);
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, []);
return {
strategyData,
loading,
error,
loadStrategyData
};
};

View File

@@ -0,0 +1,22 @@
// Main Strategy Intelligence Tab
export { default as StrategyIntelligenceTab } from '../StrategyIntelligenceTab';
// Components
export { default as StrategyHeader } from './components/StrategyHeader';
export { default as StrategicInsightsCard } from './components/StrategicInsightsCard';
export { default as CompetitiveAnalysisCard } from './components/CompetitiveAnalysisCard';
export { default as PerformancePredictionsCard } from './components/PerformancePredictionsCard';
export { default as ImplementationRoadmapCard } from './components/ImplementationRoadmapCard';
export { default as RiskAssessmentCard } from './components/RiskAssessmentCard';
export { default as StrategyActions } from './components/StrategyActions';
export { default as ConfirmationDialog } from './components/ConfirmationDialog';
// Hooks
export { useStrategyData } from './hooks/useStrategyData';
export { useStrategyActions } from './hooks/useStrategyActions';
// Types
export * from './types/strategy.types';
// Utils
export * from './utils/strategyTransformers';

View File

@@ -0,0 +1,377 @@
import { Theme } from '@mui/material/styles';
// Color palette for analysis components
export const ANALYSIS_COLORS = {
primary: '#667eea',
secondary: '#764ba2',
accent: '#f093fb',
success: '#4caf50',
warning: '#ff9800',
error: '#f44336',
info: '#2196f3',
background: {
gradient: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #533483 100%)',
dark: 'rgba(0, 0, 0, 0.3)',
glass: 'rgba(255, 255, 255, 0.05)'
},
text: {
primary: 'white',
secondary: '#e0e0e0',
muted: '#b0b0b0'
},
border: {
primary: 'rgba(102, 126, 234, 0.3)',
secondary: 'rgba(255, 255, 255, 0.1)'
}
};
// Priority colors for chips
export const PRIORITY_COLORS = {
high: '#f44336',
medium: '#ff9800',
low: '#4caf50',
default: '#667eea'
};
// Impact colors for chips
export const IMPACT_COLORS = {
high: '#f44336',
medium: '#ff9800',
low: '#4caf50',
default: '#667eea'
};
// Main card container styles
export const getAnalysisCardStyles = () => ({
card: {
height: '100%',
borderRadius: 3,
background: ANALYSIS_COLORS.background.gradient,
color: ANALYSIS_COLORS.text.primary,
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(102, 126, 234, 0.3)',
border: `1px solid ${ANALYSIS_COLORS.border.primary}`,
position: 'relative' as const,
overflow: 'hidden',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%)',
pointerEvents: 'none'
},
'&:hover': {
boxShadow: '0 25px 70px rgba(102, 126, 234, 0.4)',
transform: 'translateY(-4px)'
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
},
cardContent: {
p: 2.5,
position: 'relative' as const,
zIndex: 1
}
});
// Header section styles
export const getHeaderStyles = () => ({
headerContainer: {
display: 'flex',
alignItems: 'center',
mb: 2.5
},
iconContainer: {
p: 1,
borderRadius: 2,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
mr: 1.5,
boxShadow: '0 4px 12px rgba(102, 126, 234, 0.3)'
},
titleContainer: {
flex: 1
},
title: {
fontWeight: 700,
background: 'linear-gradient(45deg, #667eea, #764ba2, #f093fb)',
backgroundSize: '200% 200%',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
animation: 'gradient 3s ease infinite',
'@keyframes gradient': {
'0%': { backgroundPosition: '0% 50%' },
'50%': { backgroundPosition: '100% 50%' },
'100%': { backgroundPosition: '0% 50%' }
}
}
});
// Market analysis header styles
export const getMarketAnalysisHeaderStyles = () => ({
marketAnalysisContainer: {
display: 'flex',
alignItems: 'center',
gap: 2,
p: 1.5,
background: ANALYSIS_COLORS.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_COLORS.border.secondary}`,
backdropFilter: 'blur(10px)'
},
circularProgress: {
color: ANALYSIS_COLORS.primary,
'& .MuiCircularProgress-circle': {
strokeLinecap: 'round',
filter: 'drop-shadow(0 2px 4px rgba(102, 126, 234, 0.3))'
}
},
progressText: {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
fontWeight: 700,
color: ANALYSIS_COLORS.primary,
fontSize: '0.7rem'
},
positionText: {
fontWeight: 600,
color: ANALYSIS_COLORS.text.primary,
display: 'block',
fontSize: '0.7rem'
},
positionLabel: {
color: ANALYSIS_COLORS.text.secondary,
fontSize: '0.6rem'
},
marketChip: {
background: 'rgba(0, 0, 0, 0.4)',
color: ANALYSIS_COLORS.primary,
fontWeight: 600,
fontSize: '0.6rem',
height: 20,
border: `1px solid rgba(102, 126, 234, 0.4)`,
'& .MuiChip-label': {
px: 1
}
}
});
// Section container styles
export const getSectionStyles = () => ({
sectionContainer: {
mb: 2.5,
p: 2,
background: ANALYSIS_COLORS.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_COLORS.border.secondary}`,
backdropFilter: 'blur(10px)'
},
sectionTitle: {
fontWeight: 600,
color: ANALYSIS_COLORS.text.primary,
fontSize: '0.85rem'
}
});
// Accordion styles
export const getAccordionStyles = () => ({
accordion: {
mb: 1,
'&:before': { display: 'none' },
background: ANALYSIS_COLORS.background.dark,
borderRadius: 2,
border: `1px solid ${ANALYSIS_COLORS.border.secondary}`,
backdropFilter: 'blur(10px)',
'&.Mui-expanded': {
margin: '8px 0'
}
},
accordionSummary: {
'& .MuiAccordionSummary-content': {
margin: '8px 0'
}
},
accordionTitle: {
fontWeight: 600,
color: ANALYSIS_COLORS.text.primary,
fontSize: '0.8rem'
},
accordionSubtitle: {
color: ANALYSIS_COLORS.text.secondary
},
expandIcon: {
color: ANALYSIS_COLORS.primary
}
});
// Enhanced chip styles
export const getEnhancedChipStyles = (color: string) => ({
chip: {
background: `linear-gradient(135deg, ${color}70 0%, ${color}60 100%)`,
color: color,
fontWeight: 700,
fontSize: '0.75rem',
height: 30,
border: `3px solid ${color}80`,
boxShadow: `0 8px 20px ${color}50, inset 0 3px 0 rgba(255, 255, 255, 0.2), inset 0 -2px 0 rgba(0, 0, 0, 0.4), 0 0 15px ${color}30`,
backdropFilter: 'blur(20px)',
transition: 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)',
textShadow: '0 2px 4px rgba(0, 0, 0, 0.5)',
position: 'relative' as const,
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: `linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(0, 0, 0, 0.1) 100%)`,
borderRadius: 'inherit',
pointerEvents: 'none'
},
'&:hover': {
transform: 'translateY(-4px) scale(1.05)',
boxShadow: `0 12px 30px ${color}60, inset 0 3px 0 rgba(255, 255, 255, 0.3), inset 0 -2px 0 rgba(0, 0, 0, 0.5), 0 0 25px ${color}40`,
background: `linear-gradient(135deg, ${color}80 0%, ${color}70 100%)`
},
'& .MuiChip-label': {
px: 2.5,
py: 1,
fontWeight: 700,
letterSpacing: '0.6px',
position: 'relative' as const,
zIndex: 1
},
'& .MuiChip-icon': {
color: color,
fontSize: '1.1rem',
filter: 'drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5))',
position: 'relative' as const,
zIndex: 1
}
}
});
// List item styles
export const getListItemStyles = () => ({
listItem: {
px: 0,
py: 1
},
listItemIcon: {
minWidth: 32
},
bulletPoint: {
width: 8,
height: 8,
borderRadius: '50%',
background: ANALYSIS_COLORS.primary,
opacity: 0.7
},
insightText: {
fontWeight: 500,
mb: 1,
color: ANALYSIS_COLORS.text.primary,
lineHeight: 1.5
},
reasoningText: {
color: ANALYSIS_COLORS.text.muted,
fontStyle: 'italic',
lineHeight: 1.4
}
});
// Fallback/empty state styles
export const getFallbackStyles = () => ({
fallbackContainer: {
textAlign: 'center',
py: 4
},
fallbackText: {
mb: 2,
color: ANALYSIS_COLORS.text.secondary
},
fallbackCaption: {
color: ANALYSIS_COLORS.text.muted
}
});
// Animation styles
export const getAnimationStyles = () => ({
iconAnimation: {
animate: { rotate: [0, 10, -10, 0] },
transition: { duration: 2, repeat: Infinity, ease: "easeInOut" as const }
},
cardAnimation: {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.5 },
whileHover: { y: -4 }
}
});
// Utility functions
export const getPriorityColor = (priority: string): string => {
switch (priority.toLowerCase()) {
case 'high':
return PRIORITY_COLORS.high;
case 'medium':
return PRIORITY_COLORS.medium;
case 'low':
return PRIORITY_COLORS.low;
default:
return PRIORITY_COLORS.default;
}
};
export const getImpactColor = (impact: string): string => {
switch (impact.toLowerCase()) {
case 'high':
return IMPACT_COLORS.high;
case 'medium':
return IMPACT_COLORS.medium;
case 'low':
return IMPACT_COLORS.low;
default:
return IMPACT_COLORS.default;
}
};
// Icon mapping for different insight types
export const getInsightIcon = (type: string) => {
switch (type.toLowerCase()) {
case 'market positioning':
return { icon: 'Business', color: ANALYSIS_COLORS.primary };
case 'content opportunity':
return { icon: 'Lightbulb', color: ANALYSIS_COLORS.success };
case 'growth potential':
return { icon: 'TrendingUp', color: ANALYSIS_COLORS.info };
case 'competitive advantage':
return { icon: 'Star', color: ANALYSIS_COLORS.warning };
case 'strategic recommendation':
return { icon: 'Psychology', color: ANALYSIS_COLORS.error };
default:
return { icon: 'Lightbulb', color: ANALYSIS_COLORS.primary };
}
};
// Complete style object for easy import
export const ANALYSIS_CARD_STYLES = {
colors: ANALYSIS_COLORS,
priorityColors: PRIORITY_COLORS,
impactColors: IMPACT_COLORS,
getAnalysisCardStyles,
getHeaderStyles,
getMarketAnalysisHeaderStyles,
getSectionStyles,
getAccordionStyles,
getEnhancedChipStyles,
getListItemStyles,
getFallbackStyles,
getAnimationStyles,
getPriorityColor,
getImpactColor,
getInsightIcon
};

View File

@@ -0,0 +1 @@
export * from './analysisCardStyles';

View File

@@ -0,0 +1,267 @@
export interface StrategyMetadata {
generated_at?: string;
generation_timestamp?: string;
user_id: number;
strategy_name: string;
generation_version?: string;
ai_model?: string;
personalization_level?: string;
ai_generated: boolean;
comprehensive: boolean;
content_calendar_ready: boolean;
}
export interface StrategicInsights {
market_positioning?: {
positioning_strength: number;
current_position: string;
swot_analysis?: {
strengths: string[];
opportunities: string[];
};
};
content_opportunities?: string[];
growth_potential?: {
market_size: string;
growth_rate: string;
key_drivers?: string[];
competitive_advantages?: string[];
};
swot_summary?: {
overall_score: number;
primary_strengths: string[];
key_opportunities: string[];
};
// Backend insights array structure
insights?: Array<{
type: string;
insight: string;
confidence_level: string;
estimated_impact: string;
implementation_time: string;
priority: string;
reasoning: string;
}>;
}
export interface CompetitiveAnalysis {
competitors?: Array<{
name: string;
market_position: string;
strengths: string[];
weaknesses: string[];
content_strategy?: string; // Added to match backend
competitive_response?: string;
}>;
market_gaps?: string[];
opportunities?: string[]; // Added to match backend
recommendations?: string[]; // Added to match backend
competitive_advantages?: {
primary: string[];
sustainable: string[];
development_areas: string[];
};
swot_competitive_insights?: {
leverage_strengths: string[];
address_weaknesses: string[];
capitalize_opportunities: string[];
mitigate_threats: string[];
};
}
export interface PerformancePredictions {
estimated_roi?: string;
key_metrics?: {
engagement_rate?: string;
conversion_rate?: string;
reach_growth?: string;
brand_awareness?: string;
market_share?: string;
};
timeline_projections?: {
[key: string]: string;
};
success_factors?: {
primary: string[];
secondary: string[];
risk_mitigation: string[];
};
swot_based_predictions?: {
strength_impact: string;
opportunity_impact: string;
weakness_mitigation: string;
threat_management: string;
};
// Nested prediction objects from backend
traffic_predictions?: {
monthly_traffic?: string;
growth_rate?: string;
traffic_growth?: string;
month_1?: string;
};
engagement_predictions?: {
engagement_rate?: string;
brand_awareness?: string;
time_on_page?: string;
month_3?: string;
success_factors?: string[];
};
conversion_predictions?: {
conversion_rate?: string;
lead_generation?: string;
month_6?: string;
risk_mitigation?: string[];
};
roi_predictions?: {
estimated_roi?: string;
market_share?: string;
cost_benefit?: string;
success_factors?: string[];
};
}
export interface ImplementationRoadmap {
total_duration?: string;
phases?: Array<{
phase: string;
duration: string;
tasks: string[]; // Changed from activities to match backend
milestones: string[]; // Changed from deliverables to match backend
resources: string[]; // Added to match backend
swot_focus?: string;
}>;
resource_allocation?: {
team_members?: string[]; // Changed from team_requirements to match backend
team_requirements?: string[]; // Added to match backend data
budget_allocation?: {
total_budget?: string;
content_creation?: string;
technology_tools?: string;
marketing_promotion?: string;
external_resources?: string;
};
swot_priorities?: {
high_priority: string[];
medium_priority: string[];
low_priority: string[];
};
};
success_metrics?: string[]; // Added to match backend
timeline?: {
start_date?: string;
end_date?: string;
key_milestones?: string[];
}; // Added to match backend
swot_integration?: {
strength_leverage: string[];
weakness_improvement: string[];
opportunity_capitalization: string[];
threat_mitigation: string[];
};
}
export interface RiskAssessment {
overall_risk_level?: string;
risks?: Array<{
risk: string;
probability: string;
impact: string;
mitigation?: string;
contingency?: string;
}>;
risk_categories?: {
market_risks?: Array<{
risk: string;
probability: string;
impact: string;
mitigation?: string;
}>;
operational_risks?: Array<{
risk: string;
probability: string;
impact: string;
mitigation?: string;
}>;
competitive_risks?: Array<{
risk: string;
probability: string;
impact: string;
mitigation?: string;
}>;
technical_risks?: Array<{
risk: string;
probability: string;
impact: string;
mitigation?: string;
}>;
financial_risks?: Array<{
risk: string;
probability: string;
impact: string;
mitigation?: string;
}>;
};
monitoring_framework?: {
escalation_procedures?: string[];
key_indicators?: string[];
monitoring_frequency?: string;
review_schedule?: string;
};
swot_risk_mapping?: {
strength_risks: string;
weakness_risks: string;
opportunity_risks: string;
threat_risks: string;
};
risk_mitigation_strategies?: {
based_on_strengths: string;
based_on_opportunities: string;
based_on_weaknesses: string;
based_on_threats: string;
};
mitigation_strategies?: string[];
}
export interface StrategySummary {
estimated_roi: string;
implementation_timeline: string;
risk_level: string;
success_probability: string;
next_step: string;
swot_highlights?: {
key_strengths: string[];
key_opportunities: string[];
primary_risks: string[];
};
}
export interface StrategyData {
strategy_metadata?: StrategyMetadata;
metadata?: StrategyMetadata;
base_strategy?: any;
strategic_insights?: StrategicInsights;
competitive_analysis?: CompetitiveAnalysis;
performance_predictions?: PerformancePredictions;
implementation_roadmap?: ImplementationRoadmap;
risk_assessment?: RiskAssessment;
summary?: StrategySummary;
}
export interface StrategyActionsProps {
strategyData: StrategyData | null;
strategyConfirmed: boolean;
onConfirmStrategy: () => void;
onGenerateContentCalendar: () => void;
onRefreshData: () => void;
}
export interface StrategyCardProps {
strategyData: StrategyData | null;
loading?: boolean;
}
export interface ConfirmationDialogProps {
open: boolean;
onClose: () => void;
onConfirm: () => void;
}

View File

@@ -0,0 +1,473 @@
import { StrategyData } from '../types/strategy.types';
// Helper function to get user ID from context or store
export const getUserId = (): number => {
// TODO: Replace with actual user context/store
// For now, return default user ID
return 1;
};
/**
* Transform polling system strategy data to frontend format
*/
export const transformPollingStrategyData = (strategyData: any): StrategyData => {
return {
...strategyData,
strategy_metadata: strategyData.metadata || strategyData.strategy_metadata,
// Add summary if not present
summary: strategyData.summary || {
estimated_roi: strategyData.performance_predictions?.estimated_roi || "15-25%",
implementation_timeline: strategyData.implementation_roadmap?.total_duration || "12 months",
risk_level: strategyData.risk_assessment?.overall_risk_level || "Medium",
success_probability: strategyData.performance_predictions?.success_probability || "85%",
next_step: "Review strategy and generate content calendar"
}
};
};
/**
* Transform full 5-component structure from database
*/
export const transformFullStructureData = (latestStrategy: any): StrategyData => {
const comprehensiveData = latestStrategy.comprehensive_ai_analysis;
return {
// Map metadata
strategy_metadata: comprehensiveData.metadata || comprehensiveData.strategy_metadata,
metadata: comprehensiveData.metadata || comprehensiveData.strategy_metadata,
// Transform Strategic Insights
strategic_insights: comprehensiveData.strategic_insights ? {
market_positioning: {
positioning_strength: 75, // Default value
current_position: "Emerging",
swot_analysis: {
strengths: [],
opportunities: []
}
},
content_opportunities: comprehensiveData.strategic_insights.insights?.filter((insight: any) =>
insight.type === 'Content Opportunity'
).map((insight: any) => insight.insight) || [],
growth_potential: {
market_size: "Growing",
growth_rate: "High",
key_drivers: comprehensiveData.strategic_insights.insights?.filter((insight: any) =>
insight.type === 'Growth Potential'
).map((insight: any) => insight.insight) || [],
competitive_advantages: []
},
swot_summary: {
overall_score: 75,
primary_strengths: comprehensiveData.strategic_insights.insights?.slice(0, 2) || [],
key_opportunities: comprehensiveData.strategic_insights.insights?.slice(2, 4) || []
},
// Map the insights array directly
insights: comprehensiveData.strategic_insights.insights || []
} : undefined,
// Transform Competitive Analysis
competitive_analysis: comprehensiveData.competitive_analysis ? {
competitors: comprehensiveData.competitive_analysis.competitors || [],
market_gaps: comprehensiveData.competitive_analysis.market_gaps || [],
opportunities: comprehensiveData.competitive_analysis.opportunities || [],
recommendations: comprehensiveData.competitive_analysis.recommendations || [],
competitive_advantages: {
primary: comprehensiveData.competitive_analysis.recommendations?.slice(0, 3) || [],
sustainable: comprehensiveData.competitive_analysis.recommendations?.slice(3, 5) || [],
development_areas: comprehensiveData.competitive_analysis.opportunities || []
},
swot_competitive_insights: {
leverage_strengths: comprehensiveData.competitive_analysis.recommendations?.slice(0, 2) || [],
address_weaknesses: comprehensiveData.competitive_analysis.recommendations?.slice(2, 4) || [],
capitalize_opportunities: comprehensiveData.competitive_analysis.opportunities?.slice(0, 2) || [],
mitigate_threats: comprehensiveData.competitive_analysis.recommendations?.slice(4, 6) || []
}
} : undefined,
// Transform Performance Predictions
performance_predictions: comprehensiveData.performance_predictions ? {
estimated_roi: comprehensiveData.performance_predictions.roi_predictions?.estimated_roi || "15-25%",
key_metrics: {
engagement_rate: comprehensiveData.performance_predictions.engagement_predictions?.engagement_rate || "8-12%",
conversion_rate: comprehensiveData.performance_predictions.conversion_predictions?.conversion_rate || "3-5%",
reach_growth: comprehensiveData.performance_predictions.traffic_predictions?.traffic_growth || "40-60%",
brand_awareness: comprehensiveData.performance_predictions.engagement_predictions?.brand_awareness || "25-35%",
market_share: comprehensiveData.performance_predictions.roi_predictions?.market_share || "5-8%"
},
timeline_projections: {
"month_1": comprehensiveData.performance_predictions.traffic_predictions?.month_1 || "Initial setup and content creation",
"month_3": comprehensiveData.performance_predictions.engagement_predictions?.month_3 || "Content optimization and audience growth",
"month_6": comprehensiveData.performance_predictions.conversion_predictions?.month_6 || "Full strategy implementation and scaling"
},
success_factors: {
primary: comprehensiveData.performance_predictions.roi_predictions?.success_factors?.slice(0, 3) || [],
secondary: comprehensiveData.performance_predictions.engagement_predictions?.success_factors?.slice(0, 2) || [],
risk_mitigation: comprehensiveData.performance_predictions.conversion_predictions?.risk_mitigation?.slice(0, 2) || []
},
swot_based_predictions: {
strength_impact: "High positive impact from identified strengths",
opportunity_impact: "Significant growth potential from market opportunities",
weakness_mitigation: "Addressing weaknesses through strategic content planning",
threat_management: "Proactive threat management through diversified approach"
}
} : undefined,
// Transform Implementation Roadmap
implementation_roadmap: comprehensiveData.implementation_roadmap ? {
total_duration: comprehensiveData.implementation_roadmap.total_duration || "6 months",
phases: comprehensiveData.implementation_roadmap.phases || [],
success_metrics: comprehensiveData.implementation_roadmap.success_metrics || [],
timeline: comprehensiveData.implementation_roadmap.timeline || {
start_date: "2024-09-01",
end_date: "2025-02-28",
key_milestones: []
},
resource_allocation: {
team_members: comprehensiveData.implementation_roadmap.resource_allocation?.team_members ||
comprehensiveData.implementation_roadmap.resource_allocation?.team_requirements ||
["Content Strategist", "SEO Specialist", "Content Writer", "Editor"],
team_requirements: comprehensiveData.implementation_roadmap.resource_allocation?.team_requirements ||
comprehensiveData.implementation_roadmap.resource_allocation?.team_members ||
["Content Strategist", "SEO Specialist", "Content Writer", "Editor"],
budget_allocation: comprehensiveData.implementation_roadmap.resource_allocation?.budget_allocation || {
total_budget: "$60,000",
content_creation: "$30,000",
technology_tools: "$5,000",
marketing_promotion: "$20,000",
external_resources: "$5,000"
},
swot_priorities: {
high_priority: comprehensiveData.implementation_roadmap.success_metrics?.slice(0, 3) || [],
medium_priority: comprehensiveData.implementation_roadmap.success_metrics?.slice(3, 6) || [],
low_priority: comprehensiveData.implementation_roadmap.success_metrics?.slice(6, 9) || []
}
}
} : undefined,
// Transform Risk Assessment
risk_assessment: comprehensiveData.risk_assessment ? {
overall_risk_level: comprehensiveData.risk_assessment.overall_risk_level || "Medium",
risks: comprehensiveData.risk_assessment.risks || [],
risk_categories: comprehensiveData.risk_assessment.risk_categories || {},
monitoring_framework: comprehensiveData.risk_assessment.monitoring_framework || {
escalation_procedures: [],
key_indicators: [],
monitoring_frequency: "Weekly for key performance metrics, Monthly for strategic content review, Quarterly for comprehensive analysis",
review_schedule: "Monthly performance review meetings to discuss analytics and tactical adjustments. Quarterly strategic comprehensive review"
},
swot_risk_mapping: {
strength_risks: "Leverage strengths to mitigate risks",
weakness_risks: "Address weaknesses through strategic planning",
opportunity_risks: "Capitalize on opportunities while managing risks",
threat_risks: "Proactive threat management and contingency planning"
},
risk_mitigation_strategies: {
based_on_strengths: comprehensiveData.risk_assessment.mitigation_strategies?.[0] || "Leverage identified strengths",
based_on_opportunities: comprehensiveData.risk_assessment.mitigation_strategies?.[1] || "Capitalize on market opportunities",
based_on_weaknesses: comprehensiveData.risk_assessment.mitigation_strategies?.[2] || "Address identified weaknesses",
based_on_threats: comprehensiveData.risk_assessment.mitigation_strategies?.[3] || "Proactive threat management"
},
mitigation_strategies: comprehensiveData.risk_assessment.mitigation_strategies || []
} : undefined,
// Add summary
summary: comprehensiveData.summary || {
estimated_roi: comprehensiveData.performance_predictions?.roi_predictions?.estimated_roi || "15-25%",
implementation_timeline: comprehensiveData.implementation_roadmap?.total_duration || "6 months",
risk_level: comprehensiveData.risk_assessment?.overall_risk_level || "Medium",
success_probability: "85%",
next_step: "Review strategy and generate content calendar"
}
};
};
/**
* Transform SWOT analysis into comprehensive 5-component structure
*/
export const transformSwotToComprehensiveStructure = (latestStrategy: any): StrategyData => {
const swotData = latestStrategy.comprehensive_ai_analysis;
return {
strategy_metadata: {
user_id: latestStrategy.user_id,
strategy_name: latestStrategy.name,
ai_generated: true,
comprehensive: true,
content_calendar_ready: false,
generation_timestamp: latestStrategy.created_at
},
// Enhanced Strategic Insights with SWOT data
strategic_insights: {
market_positioning: {
positioning_strength: swotData.overall_score || 75,
current_position: "Emerging",
swot_analysis: {
strengths: swotData.strengths || [],
opportunities: swotData.opportunities || []
}
},
content_opportunities: [
...(swotData.opportunities || []),
"Leverage identified market gaps",
"Focus on unique value propositions",
"Build thought leadership content"
],
growth_potential: {
market_size: "Growing",
growth_rate: "High",
key_drivers: swotData.opportunities || [],
competitive_advantages: swotData.strengths || []
},
swot_summary: {
overall_score: swotData.overall_score || 75,
primary_strengths: (swotData.strengths || []).slice(0, 3),
key_opportunities: (swotData.opportunities || []).slice(0, 3)
}
},
// Enhanced Competitive Analysis with SWOT data
competitive_analysis: {
competitors: [
{
name: "Direct Competitors",
market_position: "Established",
strengths: swotData.strengths || [],
weaknesses: swotData.weaknesses || [],
competitive_response: "Focus on differentiation"
},
{
name: "Emerging Competitors",
market_position: "Growing",
strengths: [],
weaknesses: swotData.weaknesses || [],
competitive_response: "Establish market leadership"
}
],
market_gaps: [
...(swotData.opportunities || []),
"Content personalization opportunities",
"Niche market segments",
"Innovation in content delivery"
],
competitive_advantages: {
primary: swotData.strengths || [],
sustainable: swotData.opportunities || [],
development_areas: swotData.weaknesses || []
},
swot_competitive_insights: {
leverage_strengths: swotData.strengths || [],
address_weaknesses: swotData.weaknesses || [],
capitalize_opportunities: swotData.opportunities || [],
mitigate_threats: swotData.threats || []
}
},
// Enhanced Performance Predictions with SWOT context
performance_predictions: {
estimated_roi: "20-30%",
key_metrics: {
engagement_rate: "5-8%",
conversion_rate: "2-4%",
reach_growth: "40-60%",
brand_awareness: "25-35%",
market_share: "3-5%"
},
timeline_projections: {
"3_months": "Initial traction and brand awareness leveraging identified strengths",
"6_months": "Established presence and engagement addressing market opportunities",
"12_months": "Market leadership and growth capitalizing on competitive advantages"
},
success_factors: {
primary: swotData.strengths || [],
secondary: swotData.opportunities || [],
risk_mitigation: swotData.threats || []
},
swot_based_predictions: {
strength_impact: "High performance in areas of identified strengths",
opportunity_impact: "Growth potential through market opportunities",
weakness_mitigation: "Improved performance by addressing weaknesses",
threat_management: "Risk-adjusted projections considering market threats"
}
},
// Enhanced Implementation Roadmap with SWOT considerations
implementation_roadmap: {
total_duration: "12 months",
phases: [
{
phase: "Foundation (Months 1-3)",
duration: "3 months",
tasks: [
"Brand positioning leveraging identified strengths",
"Content strategy development addressing market opportunities",
"Weakness assessment and improvement planning"
],
milestones: ["Brand guidelines", "Content calendar", "SWOT action plan"],
resources: ["Content Strategist", "Brand Manager", "SWOT Analyst"],
swot_focus: "Strengths and Opportunities"
},
{
phase: "Growth (Months 4-8)",
duration: "5 months",
tasks: [
"Content execution based on competitive advantages",
"Community building addressing market gaps",
"Threat mitigation strategies implementation"
],
milestones: ["Content library", "Engaged audience", "Risk management framework"],
resources: ["Content Writer", "Community Manager", "Risk Analyst"],
swot_focus: "Opportunities and Threats"
},
{
phase: "Scale (Months 9-12)",
duration: "4 months",
tasks: [
"Market expansion capitalizing on strengths",
"Performance optimization addressing weaknesses",
"Sustainable competitive advantage development"
],
milestones: ["Market leadership", "Optimized strategy", "Long-term competitive position"],
resources: ["Growth Manager", "Performance Analyst", "Strategy Consultant"],
swot_focus: "Strengths and Weaknesses"
}
],
resource_allocation: {
team_members: ["Content Strategist", "SEO Specialist", "Content Writer", "Editor", "Marketing Manager"],
budget_allocation: {
total_budget: "$60,000",
content_creation: "$30,000",
technology_tools: "$5,000",
marketing_promotion: "$20,000",
external_resources: "$5,000"
},
swot_priorities: {
high_priority: swotData.opportunities || [],
medium_priority: swotData.strengths || [],
low_priority: swotData.weaknesses || []
}
},
swot_integration: {
strength_leverage: swotData.strengths || [],
weakness_improvement: swotData.weaknesses || [],
opportunity_capitalization: swotData.opportunities || [],
threat_mitigation: swotData.threats || []
}
},
// Enhanced Risk Assessment with SWOT threats
risk_assessment: {
overall_risk_level: "Medium",
risks: [
...(swotData.threats?.map((threat: string) => ({
risk: threat,
probability: "Medium",
impact: "High",
mitigation: "Strategic planning and monitoring"
})) || []),
{
risk: "Market saturation",
probability: "Medium",
impact: "Medium",
mitigation: "Innovation and differentiation"
}
],
risk_categories: {
market_risks: [
...(swotData.threats?.map((threat: string) => ({
risk: threat,
probability: "Medium",
impact: "High",
mitigation: "Strategic planning and monitoring"
})) || []),
{
risk: "Market saturation",
probability: "Medium",
impact: "Medium",
mitigation: "Innovation and differentiation"
}
],
operational_risks: [
{
risk: "Resource constraints",
probability: "Medium",
impact: "Medium",
mitigation: "Efficient resource allocation"
},
{
risk: "Weakness areas",
probability: "High",
impact: "Medium",
mitigation: "Targeted improvement programs"
}
],
competitive_risks: [
{
risk: "Market competition",
probability: "High",
impact: "Medium",
mitigation: "Leverage competitive advantages"
},
{
risk: "Strength erosion",
probability: "Medium",
impact: "High",
mitigation: "Continuous improvement and innovation"
}
]
},
swot_risk_mapping: {
strength_risks: "Risk of over-reliance on current strengths",
weakness_risks: "Risk of weakness exploitation by competitors",
opportunity_risks: "Risk of missing market opportunities",
threat_risks: "Risk of threat materialization"
},
risk_mitigation_strategies: {
based_on_strengths: "Leverage strengths to mitigate threats",
based_on_opportunities: "Use opportunities to address weaknesses",
based_on_weaknesses: "Develop improvement plans for weak areas",
based_on_threats: "Create contingency plans for identified threats"
},
mitigation_strategies: swotData.mitigation_strategies || []
},
// Enhanced summary with SWOT context
summary: {
estimated_roi: "20-30%",
implementation_timeline: "12 months",
risk_level: "Medium",
success_probability: `${swotData.overall_score || 75}%`,
next_step: "Review strategy and generate content calendar",
swot_highlights: {
key_strengths: (swotData.strengths || []).slice(0, 2),
key_opportunities: (swotData.opportunities || []).slice(0, 2),
primary_risks: (swotData.threats || []).slice(0, 2)
}
}
};
};
/**
* Check if strategy data has full 5-component structure
*/
export const hasFullStructure = (comprehensiveAnalysis: any): boolean => {
return !!(comprehensiveAnalysis.strategic_insights ||
comprehensiveAnalysis.competitive_analysis ||
comprehensiveAnalysis.performance_predictions);
};
/**
* Get strategy name from metadata
*/
export const getStrategyName = (strategyData: StrategyData | null): string => {
return strategyData?.strategy_metadata?.strategy_name ||
strategyData?.metadata?.strategy_name ||
'AI-Generated Strategy';
};
/**
* Get strategy generation date
*/
export const getStrategyGenerationDate = (strategyData: StrategyData | null): string => {
const timestamp = strategyData?.strategy_metadata?.generated_at ||
strategyData?.strategy_metadata?.generation_timestamp ||
strategyData?.metadata?.generated_at ||
strategyData?.metadata?.generation_timestamp || '';
return new Date(timestamp).toLocaleDateString();
};

View File

@@ -0,0 +1,103 @@
import React from 'react';
import { Box, CircularProgress, Alert, Typography, Grid } from '@mui/material';
import { useStrategyData } from './StrategyIntelligence/hooks/useStrategyData';
import { useStrategyActions } from './StrategyIntelligence/hooks/useStrategyActions';
import StrategyHeader from './StrategyIntelligence/components/StrategyHeader';
import StrategicInsightsCard from './StrategyIntelligence/components/StrategicInsightsCard';
import CompetitiveAnalysisCard from './StrategyIntelligence/components/CompetitiveAnalysisCard';
import PerformancePredictionsCard from './StrategyIntelligence/components/PerformancePredictionsCard';
import ImplementationRoadmapCard from './StrategyIntelligence/components/ImplementationRoadmapCard';
import RiskAssessmentCard from './StrategyIntelligence/components/RiskAssessmentCard';
import StrategyActions from './StrategyIntelligence/components/StrategyActions';
import ConfirmationDialog from './StrategyIntelligence/components/ConfirmationDialog';
const StrategyIntelligenceTab: React.FC = () => {
const { strategyData, loading, error, loadStrategyData } = useStrategyData();
const {
strategyConfirmed,
showConfirmDialog,
setShowConfirmDialog,
handleConfirmStrategy,
confirmStrategy,
handleGenerateContentCalendar
} = useStrategyActions();
const handleConfirmStrategyClick = () => {
handleConfirmStrategy();
};
const handleConfirmStrategyAction = async () => {
await confirmStrategy(strategyData);
};
const handleGenerateContentCalendarAction = async () => {
try {
await handleGenerateContentCalendar(strategyData);
} catch (error) {
console.error('Error generating content calendar:', error);
}
};
if (loading) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: 400 }}>
<CircularProgress />
</Box>
);
}
if (error) {
return (
<Alert severity="error" sx={{ m: 2 }}>
{error}
</Alert>
);
}
if (!strategyData) {
return (
<Box sx={{ textAlign: 'center', p: 4 }}>
<Typography variant="h6" color="text.secondary" gutterBottom>
No Strategy Data Available
</Typography>
<Typography variant="body2" color="text.secondary">
Generate a comprehensive strategy first to view strategic intelligence.
</Typography>
</Box>
);
}
return (
<Box sx={{ p: 3 }}>
{/* Header Section */}
<StrategyHeader strategyData={strategyData} strategyConfirmed={strategyConfirmed} />
{/* Strategy Components Grid */}
<Grid container spacing={2}>
<StrategicInsightsCard strategyData={strategyData} />
<CompetitiveAnalysisCard strategyData={strategyData} />
<PerformancePredictionsCard strategyData={strategyData} />
<ImplementationRoadmapCard strategyData={strategyData} />
<RiskAssessmentCard strategyData={strategyData} />
</Grid>
{/* Action Buttons */}
<StrategyActions
strategyData={strategyData}
strategyConfirmed={strategyConfirmed}
onConfirmStrategy={handleConfirmStrategyClick}
onGenerateContentCalendar={handleGenerateContentCalendarAction}
onRefreshData={loadStrategyData}
/>
{/* Confirmation Dialog */}
<ConfirmationDialog
open={showConfirmDialog}
onClose={() => setShowConfirmDialog(false)}
onConfirm={handleConfirmStrategyAction}
/>
</Box>
);
};
export default StrategyIntelligenceTab;

View File

@@ -56,6 +56,7 @@ import {
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
import { contentPlanningApi } from '../../../services/contentPlanningApi';
import ContentStrategyBuilder from '../components/ContentStrategyBuilder';
import StrategyIntelligenceTab from '../components/StrategyIntelligenceTab';
interface TabPanelProps {
children?: React.ReactNode;
@@ -461,110 +462,7 @@ const ContentStrategyTab: React.FC = () => {
{/* Strategic Intelligence Tab */}
<TabPanel value={tabValue} index={1}>
{dataLoading.strategicIntelligence ? (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
<CircularProgress />
</Box>
) : strategicIntelligence && strategicIntelligence.market_positioning ? (
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Market Positioning
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<CircularProgress
variant="determinate"
value={strategicIntelligence.market_positioning.score || 0}
size={60}
color="primary"
/>
<Typography variant="h4" sx={{ ml: 2 }}>
{strategicIntelligence.market_positioning.score || 0}/100
</Typography>
</Box>
<Typography variant="subtitle2" gutterBottom>
Strengths:
</Typography>
<List dense>
{(strategicIntelligence.market_positioning.strengths || []).map((strength: string, index: number) => (
<ListItem key={index}>
<ListItemIcon>
<CheckCircleIcon color="success" />
</ListItemIcon>
<ListItemText primary={strength} />
</ListItem>
))}
</List>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Competitive Advantages
</Typography>
{(strategicIntelligence.competitive_advantages || []).map((advantage: any, index: number) => (
<Box key={index} sx={{ mb: 2 }}>
<Typography variant="subtitle1">
{advantage.advantage}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mt: 1 }}>
<Chip
label={advantage.impact}
color={advantage.impact === 'High' ? 'success' : 'primary'}
size="small"
/>
<Chip
label={advantage.implementation}
variant="outlined"
size="small"
/>
</Box>
</Box>
))}
</CardContent>
</Card>
</Grid>
<Grid item xs={12}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Strategic Risks
</Typography>
{(strategicIntelligence.strategic_risks || []).map((risk: any, index: number) => (
<Box key={index} sx={{ mb: 2 }}>
<Typography variant="subtitle1">
{risk.risk}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mt: 1 }}>
<Chip
label={`Probability: ${risk.probability}`}
color={risk.probability === 'High' ? 'error' : 'warning'}
size="small"
/>
<Chip
label={`Impact: ${risk.impact}`}
color={risk.impact === 'High' ? 'error' : 'warning'}
size="small"
/>
</Box>
</Box>
))}
</CardContent>
</Card>
</Grid>
</Grid>
) : (
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', p: 3 }}>
No strategic intelligence data available
</Typography>
)}
<StrategyIntelligenceTab />
</TabPanel>
{/* Keyword Research Tab */}