ALwrity version 0.5.5
This commit is contained in:
@@ -67,11 +67,15 @@ import EnterpriseDatapointsModal from './EnterpriseDatapointsModal';
|
||||
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 { useModalManagement } from './ContentStrategyBuilder/hooks/useModalManagement';
|
||||
import { useAIRefresh } from './ContentStrategyBuilder/hooks/useAIRefresh';
|
||||
import { useEventHandlers } from './ContentStrategyBuilder/hooks/useEventHandlers';
|
||||
import { useStrategyCreation } from './ContentStrategyBuilder/hooks/useStrategyCreation';
|
||||
|
||||
// Import extracted utilities
|
||||
import { getCategoryIcon, getCategoryColor, getCategoryName, getCategoryStatus } from './ContentStrategyBuilder/utils/categoryHelpers';
|
||||
import { getEducationalContent } from './ContentStrategyBuilder/utils/educationalContent';
|
||||
import { setupCSSAnimations, cleanupCSSAnimations } from './ContentStrategyBuilder/utils/cssAnimations';
|
||||
|
||||
// Import extracted components
|
||||
import CategoryList from './ContentStrategyBuilder/components/CategoryList';
|
||||
@@ -135,48 +139,79 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
personalizationData
|
||||
} = useEnhancedStrategyStore();
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState<string | null>(null);
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(null);
|
||||
const [showEducationalInfo, setShowEducationalInfo] = useState<string | null>(null);
|
||||
const [showAIRecommendations, setShowAIRecommendations] = useState(false);
|
||||
const [showDataSourceTransparency, setShowDataSourceTransparency] = useState(false);
|
||||
const [refreshMessage, setRefreshMessage] = useState<string | null>(null);
|
||||
const [refreshProgress, setRefreshProgress] = useState<number>(0);
|
||||
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
|
||||
const [refreshError, setRefreshError] = useState<string | null>(null);
|
||||
const [showEducationalModal, setShowEducationalModal] = useState(false);
|
||||
const [localEducationalContent, setLocalEducationalContent] = useState<any>(null);
|
||||
const [showEnterpriseModal, setShowEnterpriseModal] = useState(false);
|
||||
|
||||
// Persist enterprise modal state across hot reloads
|
||||
useEffect(() => {
|
||||
const savedModalState = sessionStorage.getItem('showEnterpriseModal');
|
||||
if (savedModalState === 'true') {
|
||||
console.log('🎯 Restoring enterprise modal state from sessionStorage');
|
||||
setShowEnterpriseModal(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save modal state to sessionStorage when it changes
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem('showEnterpriseModal', showEnterpriseModal.toString());
|
||||
}, [showEnterpriseModal]);
|
||||
|
||||
// Cleanup sessionStorage on component unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Only clear if we're not in the middle of showing the modal
|
||||
if (!showEnterpriseModal) {
|
||||
sessionStorage.removeItem('showEnterpriseModal');
|
||||
}
|
||||
};
|
||||
}, [showEnterpriseModal]);
|
||||
const [localGenerationProgress, setLocalGenerationProgress] = useState<number>(0);
|
||||
const [showAIRecModal, setShowAIRecModal] = useState(false);
|
||||
|
||||
// Ref to track if we've already set the default category
|
||||
const hasSetDefaultCategory = useRef(false);
|
||||
|
||||
// Use extracted hooks
|
||||
const {
|
||||
showTooltip,
|
||||
setShowTooltip,
|
||||
activeCategory,
|
||||
setActiveCategory,
|
||||
showEducationalInfo,
|
||||
setShowEducationalInfo,
|
||||
handleReviewCategory,
|
||||
handleShowEducationalInfo
|
||||
} = useEventHandlers();
|
||||
|
||||
// Use strategy creation hook first
|
||||
const { originalHandleCreateStrategy, handleSaveStrategy } = useStrategyCreation({
|
||||
formData,
|
||||
error,
|
||||
currentStrategy,
|
||||
setAIGenerating,
|
||||
setError,
|
||||
setCurrentStrategy,
|
||||
setSaving,
|
||||
setGenerationProgress: setStoreGenerationProgress,
|
||||
setEducationalContent: setStoreEducationalContent,
|
||||
setShowEducationalModal: () => {}, // Temporary placeholder
|
||||
validateAllFields,
|
||||
getCompletionStats,
|
||||
generateAIRecommendations: (strategyId: string) => generateAIRecommendations(strategyId),
|
||||
createEnhancedStrategy,
|
||||
contentPlanningApi
|
||||
});
|
||||
|
||||
const {
|
||||
showEducationalModal,
|
||||
setShowEducationalModal,
|
||||
showEnterpriseModal,
|
||||
setShowEnterpriseModal,
|
||||
handleProceedWithCurrentStrategy,
|
||||
handleAddEnterpriseDatapoints
|
||||
} = useModalManagement({
|
||||
aiGenerating,
|
||||
originalHandleCreateStrategy
|
||||
});
|
||||
|
||||
const {
|
||||
refreshMessage,
|
||||
setRefreshMessage,
|
||||
refreshProgress,
|
||||
setRefreshProgress,
|
||||
isRefreshing,
|
||||
setIsRefreshing,
|
||||
refreshError,
|
||||
setRefreshError,
|
||||
handleAIRefresh
|
||||
} = useAIRefresh({
|
||||
setTransparencyModalOpen,
|
||||
setIsGenerating,
|
||||
setStoreGenerationProgress,
|
||||
setCurrentPhase,
|
||||
clearTransparencyMessages,
|
||||
addTransparencyMessage,
|
||||
setAIGenerating,
|
||||
setError
|
||||
});
|
||||
|
||||
const completionStats = getCompletionStats();
|
||||
const completionPercentage = calculateCompletionPercentage();
|
||||
|
||||
@@ -205,24 +240,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
completionStats
|
||||
});
|
||||
|
||||
// Use ActionButtons business logic hook
|
||||
const { handleCreateStrategy: originalHandleCreateStrategy, handleSaveStrategy } = useActionButtonsBusinessLogic({
|
||||
formData,
|
||||
error,
|
||||
currentStrategy,
|
||||
setAIGenerating,
|
||||
setError,
|
||||
setCurrentStrategy,
|
||||
setSaving,
|
||||
setGenerationProgress: setStoreGenerationProgress,
|
||||
setEducationalContent: setStoreEducationalContent,
|
||||
setShowEducationalModal,
|
||||
validateAllFields,
|
||||
getCompletionStats,
|
||||
generateAIRecommendations,
|
||||
createEnhancedStrategy,
|
||||
contentPlanningApi
|
||||
});
|
||||
|
||||
|
||||
// Enhanced handleCreateStrategy to show enterprise modal
|
||||
const handleCreateStrategy = () => {
|
||||
@@ -264,51 +282,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Handle proceed with current strategy (30 fields)
|
||||
const handleProceedWithCurrentStrategy = async () => {
|
||||
console.log('🎯 User clicked "Proceed with Current Strategy"');
|
||||
setShowEnterpriseModal(false);
|
||||
sessionStorage.removeItem('showEnterpriseModal'); // Clear sessionStorage
|
||||
|
||||
// Add a small delay to ensure modal closes properly before showing educational modal
|
||||
setTimeout(async () => {
|
||||
console.log('🎯 Calling original handleCreateStrategy after enterprise modal closes');
|
||||
try {
|
||||
// Ensure we're not already generating
|
||||
if (!aiGenerating) {
|
||||
console.log('🎯 Starting strategy generation...');
|
||||
await originalHandleCreateStrategy();
|
||||
} else {
|
||||
console.log('🎯 Already generating, skipping duplicate call');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('🎯 Error in handleProceedWithCurrentStrategy:', error);
|
||||
}
|
||||
}, 300); // Increased delay to ensure modal closes completely
|
||||
};
|
||||
|
||||
// Handle add enterprise datapoints (coming soon)
|
||||
const handleAddEnterpriseDatapoints = async () => {
|
||||
console.log('🎯 User clicked "Add Enterprise Datapoints"');
|
||||
setShowEnterpriseModal(false);
|
||||
sessionStorage.removeItem('showEnterpriseModal'); // Clear sessionStorage
|
||||
|
||||
// For now, just proceed with current strategy
|
||||
// In Phase 2, this will enable enterprise datapoints
|
||||
setTimeout(async () => {
|
||||
console.log('🎯 Calling original handleCreateStrategy for enterprise datapoints');
|
||||
try {
|
||||
// Ensure we're not already generating
|
||||
if (!aiGenerating) {
|
||||
await originalHandleCreateStrategy();
|
||||
} else {
|
||||
console.log('🎯 Already generating, skipping duplicate call');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('🎯 Error in handleAddEnterpriseDatapoints:', error);
|
||||
}
|
||||
}, 200); // Increased delay to ensure modal closes completely
|
||||
};
|
||||
|
||||
// Auto-populate from onboarding on first load
|
||||
useEffect(() => {
|
||||
@@ -375,37 +349,13 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
|
||||
// Add CSS keyframes for pulse animation
|
||||
useEffect(() => {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
const style = setupCSSAnimations();
|
||||
return () => {
|
||||
if (document.head.contains(style)) {
|
||||
document.head.removeChild(style);
|
||||
}
|
||||
cleanupCSSAnimations(style);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleReviewCategory = (categoryId: string) => {
|
||||
setActiveCategory(activeCategory === categoryId ? null : categoryId);
|
||||
};
|
||||
|
||||
const handleShowEducationalInfo = (categoryId: string) => {
|
||||
setShowEducationalInfo(showEducationalInfo === categoryId ? null : categoryId);
|
||||
};
|
||||
|
||||
// Wrapper for the hook function to maintain the same interface
|
||||
const handleConfirmCategoryReviewWrapper = () => {
|
||||
@@ -447,208 +397,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
onShowAIRecommendations={() => setShowAIRecommendations(true)}
|
||||
onShowDataSourceTransparency={() => setShowDataSourceTransparency(true)}
|
||||
onRefreshData={() => autoPopulateFromOnboarding()}
|
||||
onRefreshAI={async () => {
|
||||
try {
|
||||
// 🚀 POLLING-BASED AI REFRESH (No SSE)
|
||||
// We switched from SSE to polling for better reliability
|
||||
// This approach uses direct HTTP calls with visual feedback
|
||||
|
||||
// Open transparency modal and initialize transparency state
|
||||
console.log('🎯 Opening transparency modal...');
|
||||
setTransparencyModalOpen(true);
|
||||
setIsGenerating(true);
|
||||
setStoreGenerationProgress(0);
|
||||
setCurrentPhase('autofill_initialization');
|
||||
clearTransparencyMessages();
|
||||
addTransparencyMessage('Starting strategy inputs generation process...');
|
||||
console.log('🎯 Modal state set, transparency initialized');
|
||||
|
||||
setAIGenerating(true);
|
||||
setIsRefreshing(true);
|
||||
setRefreshError(null);
|
||||
setRefreshMessage('Initializing AI refresh…');
|
||||
setRefreshProgress(5);
|
||||
|
||||
// Start transparency message polling for visual feedback
|
||||
const transparencyMessages = [
|
||||
{ type: 'autofill_initialization', message: 'Starting strategy inputs generation process...', progress: 5 },
|
||||
{ type: 'autofill_data_collection', message: 'Collecting and analyzing data sources...', progress: 15 },
|
||||
{ type: 'autofill_data_quality', message: 'Assessing data quality and completeness...', progress: 25 },
|
||||
{ type: 'autofill_context_analysis', message: 'Analyzing business context and strategic framework...', progress: 35 },
|
||||
{ type: 'autofill_strategy_generation', message: 'Generating strategic insights and recommendations...', progress: 45 },
|
||||
{ type: 'autofill_field_generation', message: 'Generating individual strategy input fields...', progress: 55 },
|
||||
{ type: 'autofill_quality_validation', message: 'Validating generated strategy inputs...', progress: 65 },
|
||||
{ type: 'autofill_alignment_check', message: 'Checking strategy alignment and consistency...', progress: 75 },
|
||||
{ type: 'autofill_final_review', message: 'Performing final review and optimization...', progress: 85 },
|
||||
{ type: 'autofill_complete', message: 'Strategy inputs generation completed successfully...', progress: 95 }
|
||||
];
|
||||
|
||||
let messageIndex = 0;
|
||||
const transparencyInterval = setInterval(() => {
|
||||
if (messageIndex < transparencyMessages.length) {
|
||||
const message = transparencyMessages[messageIndex];
|
||||
console.log('🎯 Raw Polling Message:', {
|
||||
type: message.type,
|
||||
message: message.message,
|
||||
progress: message.progress,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
setCurrentPhase(message.type);
|
||||
addTransparencyMessage(message.message);
|
||||
setStoreGenerationProgress(message.progress);
|
||||
setRefreshProgress(message.progress);
|
||||
messageIndex++;
|
||||
} else {
|
||||
clearInterval(transparencyInterval);
|
||||
}
|
||||
}, 2000); // Send a message every 2 seconds for better UX
|
||||
|
||||
// Call the non-streaming refresh endpoint (Polling-based approach)
|
||||
console.log('🎯 Calling AI refresh endpoint (Polling-based)...');
|
||||
const response = await contentPlanningApi.refreshAutofill(1, true, true);
|
||||
console.log('🎯 Raw Polling Response:', {
|
||||
success: !!response,
|
||||
hasData: !!response?.data,
|
||||
responseStructure: {
|
||||
hasDataProperty: !!response?.data?.data,
|
||||
hasFieldsDirect: !!response?.data?.fields,
|
||||
hasFieldsInData: !!response?.data?.data?.fields
|
||||
},
|
||||
fieldsCount: Object.keys(response?.data?.data?.fields || response?.data?.fields || {}).length,
|
||||
sourcesCount: Object.keys(response?.data?.data?.sources || response?.data?.sources || {}).length,
|
||||
meta: response?.data?.data?.meta || response?.data?.meta || {},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Clear the transparency interval since we got the response
|
||||
clearInterval(transparencyInterval);
|
||||
|
||||
// Process the response
|
||||
if (response && response.data) {
|
||||
// The API response is wrapped in ResponseBuilder format:
|
||||
// { status: "success", message: "...", data: { fields: {...}, sources: {...}, meta: {...} } }
|
||||
// So we need to access payload.data.fields, not payload.fields
|
||||
const payload = response.data;
|
||||
const fields = payload.data?.fields || payload.fields || {};
|
||||
const sources = payload.data?.sources || payload.sources || {};
|
||||
const inputDataPoints = payload.data?.input_data_points || payload.input_data_points || {};
|
||||
const meta = payload.data?.meta || payload.meta || {};
|
||||
|
||||
console.log('🎯 AI Refresh Result - Payload:', payload);
|
||||
console.log('🎯 AI Refresh Result - Fields:', fields);
|
||||
console.log('🎯 AI Refresh Result - Meta:', meta);
|
||||
console.log('🎯 Fields structure check:', {
|
||||
fieldsType: typeof fields,
|
||||
fieldsKeys: Object.keys(fields),
|
||||
sampleField: fields[Object.keys(fields)[0]],
|
||||
hasValueProperty: fields[Object.keys(fields)[0]]?.hasOwnProperty('value')
|
||||
});
|
||||
|
||||
// 🚨 CRITICAL: Check if AI generation failed
|
||||
if (meta.error || !meta.ai_used || meta.ai_overrides_count === 0) {
|
||||
console.error('❌ AI generation failed:', meta.error || 'No AI data generated');
|
||||
setError(`AI generation failed: ${meta.error || 'No real AI data was generated. Please try again.'}`);
|
||||
setTransparencyModalOpen(false);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('AI generation failed. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 🚨 CRITICAL: Validate data source
|
||||
if (meta.data_source === 'ai_generation_failed' || meta.data_source === 'ai_generation_error' || meta.data_source === 'ai_disabled') {
|
||||
console.error('❌ Invalid data source:', meta.data_source);
|
||||
setError(`AI generation failed: ${meta.error || 'Invalid data source. Please try again.'}`);
|
||||
setTransparencyModalOpen(false);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('AI generation failed. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ AI generation successful - processing real AI data');
|
||||
|
||||
const fieldValues: Record<string, any> = {};
|
||||
const confidenceScores: Record<string, number> = {};
|
||||
|
||||
Object.keys(fields).forEach((fieldId) => {
|
||||
const fieldData = fields[fieldId];
|
||||
console.log(`🎯 Processing field ${fieldId}:`, fieldData);
|
||||
|
||||
if (fieldData && typeof fieldData === 'object' && 'value' in fieldData) {
|
||||
fieldValues[fieldId] = fieldData.value;
|
||||
console.log(`✅ Field ${fieldId} value extracted:`, fieldData.value);
|
||||
|
||||
// Extract confidence score if available
|
||||
if (fieldData.confidence) {
|
||||
confidenceScores[fieldId] = fieldData.confidence;
|
||||
console.log(`🎯 Field ${fieldId} confidence: ${fieldData.confidence}`);
|
||||
}
|
||||
|
||||
// Extract personalization data if available
|
||||
if (fieldData.personalization_data) {
|
||||
console.log(`🎯 Field ${fieldId} personalization:`, fieldData.personalization_data);
|
||||
}
|
||||
} else {
|
||||
console.warn(`⚠️ Field ${fieldId} has invalid structure:`, fieldData);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('🎯 Processed field values:', Object.keys(fieldValues));
|
||||
console.log('🎯 Confidence scores:', confidenceScores);
|
||||
console.log('🎯 Field values details:', fieldValues);
|
||||
|
||||
// Update the store with the new data
|
||||
useEnhancedStrategyStore.setState((state) => {
|
||||
const newState = {
|
||||
autoPopulatedFields: { ...state.autoPopulatedFields, ...fieldValues },
|
||||
dataSources: { ...state.dataSources, ...sources },
|
||||
inputDataPoints: { ...state.inputDataPoints, ...inputDataPoints },
|
||||
confidenceScores: { ...state.confidenceScores, ...confidenceScores },
|
||||
formData: { ...state.formData, ...fieldValues }
|
||||
};
|
||||
console.log('🎯 Updated store state:', newState);
|
||||
console.log('🎯 Field values being added:', fieldValues);
|
||||
console.log('🎯 Confidence scores being added:', confidenceScores);
|
||||
console.log('🎯 Store autoPopulatedFields count:', Object.keys(newState.autoPopulatedFields).length);
|
||||
return newState;
|
||||
});
|
||||
|
||||
// Add final completion message
|
||||
addTransparencyMessage(`✅ AI generation completed successfully! Generated ${Object.keys(fieldValues).length} real AI values.`);
|
||||
setStoreGenerationProgress(100);
|
||||
setRefreshProgress(100);
|
||||
setCurrentPhase('Complete');
|
||||
setRefreshMessage(`AI refresh completed! Generated ${Object.keys(fieldValues).length} fields.`);
|
||||
|
||||
// Don't close modal automatically - let user close it manually
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
console.log('🎯 Polling-based AI refresh completed successfully!', {
|
||||
fieldsGenerated: Object.keys(fieldValues).length,
|
||||
confidenceScoresCount: Object.keys(confidenceScores).length,
|
||||
dataSourcesCount: Object.keys(sources).length,
|
||||
approach: 'Polling (No SSE)',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} else {
|
||||
throw new Error('Invalid response from AI refresh endpoint');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('AI refresh error', e);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('AI refresh failed. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
setError(`AI refresh failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
||||
}
|
||||
}}
|
||||
onRefreshAI={handleAIRefresh}
|
||||
refreshMessage={refreshMessage}
|
||||
refreshProgress={refreshProgress}
|
||||
isRefreshing={isRefreshing}
|
||||
@@ -757,7 +506,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
saving={saving}
|
||||
reviewProgressPercentage={reviewProgressPercentage}
|
||||
onCreateStrategy={handleCreateStrategy}
|
||||
onSaveStrategy={handleSaveStrategy}
|
||||
onSaveStrategy={() => handleSaveStrategy()}
|
||||
/>
|
||||
|
||||
{/* AI Recommendations Modal */}
|
||||
@@ -785,7 +534,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
<EducationalModal
|
||||
open={showEducationalModal}
|
||||
onClose={() => setShowEducationalModal(false)}
|
||||
educationalContent={storeEducationalContent}
|
||||
educationalContent={storeEducationalContent}
|
||||
generationProgress={storeGenerationProgress}
|
||||
onReviewStrategy={() => {
|
||||
console.log('🎯 User clicked "Next: Review Strategy and Create Calendar"');
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
import { useState } from 'react';
|
||||
import { contentPlanningApi } from '../../../../../services/contentPlanningApi';
|
||||
import { useEnhancedStrategyStore } from '../../../../../stores/enhancedStrategyStore';
|
||||
|
||||
interface UseAIRefreshProps {
|
||||
setTransparencyModalOpen: (open: boolean) => void;
|
||||
setIsGenerating: (generating: boolean) => void;
|
||||
setStoreGenerationProgress: (progress: number) => void;
|
||||
setCurrentPhase: (phase: string) => void;
|
||||
clearTransparencyMessages: () => void;
|
||||
addTransparencyMessage: (message: string) => void;
|
||||
setAIGenerating: (generating: boolean) => void;
|
||||
setError: (error: string | null) => void;
|
||||
}
|
||||
|
||||
export const useAIRefresh = ({
|
||||
setTransparencyModalOpen,
|
||||
setIsGenerating,
|
||||
setStoreGenerationProgress,
|
||||
setCurrentPhase,
|
||||
clearTransparencyMessages,
|
||||
addTransparencyMessage,
|
||||
setAIGenerating,
|
||||
setError
|
||||
}: UseAIRefreshProps) => {
|
||||
const [refreshMessage, setRefreshMessage] = useState<string | null>(null);
|
||||
const [refreshProgress, setRefreshProgress] = useState<number>(0);
|
||||
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
|
||||
const [refreshError, setRefreshError] = useState<string | null>(null);
|
||||
|
||||
const handleAIRefresh = async () => {
|
||||
try {
|
||||
// 🚀 POLLING-BASED AI REFRESH (No SSE)
|
||||
// We switched from SSE to polling for better reliability
|
||||
// This approach uses direct HTTP calls with visual feedback
|
||||
|
||||
// Open transparency modal and initialize transparency state
|
||||
console.log('🎯 Opening transparency modal...');
|
||||
setTransparencyModalOpen(true);
|
||||
setIsGenerating(true);
|
||||
setStoreGenerationProgress(0);
|
||||
setCurrentPhase('autofill_initialization');
|
||||
clearTransparencyMessages();
|
||||
addTransparencyMessage('Starting strategy inputs generation process...');
|
||||
console.log('🎯 Modal state set, transparency initialized');
|
||||
|
||||
setAIGenerating(true);
|
||||
setIsRefreshing(true);
|
||||
setRefreshError(null);
|
||||
setRefreshMessage('Initializing AI refresh…');
|
||||
setRefreshProgress(5);
|
||||
|
||||
// Start transparency message polling for visual feedback
|
||||
const transparencyMessages = [
|
||||
{ type: 'autofill_initialization', message: 'Starting strategy inputs generation process...', progress: 5 },
|
||||
{ type: 'autofill_data_collection', message: 'Collecting and analyzing data sources...', progress: 15 },
|
||||
{ type: 'autofill_data_quality', message: 'Assessing data quality and completeness...', progress: 25 },
|
||||
{ type: 'autofill_context_analysis', message: 'Analyzing business context and strategic framework...', progress: 35 },
|
||||
{ type: 'autofill_strategy_generation', message: 'Generating strategic insights and recommendations...', progress: 45 },
|
||||
{ type: 'autofill_field_generation', message: 'Generating individual strategy input fields...', progress: 55 },
|
||||
{ type: 'autofill_quality_validation', message: 'Validating generated strategy inputs...', progress: 65 },
|
||||
{ type: 'autofill_alignment_check', message: 'Checking strategy alignment and consistency...', progress: 75 },
|
||||
{ type: 'autofill_final_review', message: 'Performing final review and optimization...', progress: 85 },
|
||||
{ type: 'autofill_complete', message: 'Strategy inputs generation completed successfully...', progress: 95 }
|
||||
];
|
||||
|
||||
let messageIndex = 0;
|
||||
const transparencyInterval = setInterval(() => {
|
||||
if (messageIndex < transparencyMessages.length) {
|
||||
const message = transparencyMessages[messageIndex];
|
||||
console.log('🎯 Raw Polling Message:', {
|
||||
type: message.type,
|
||||
message: message.message,
|
||||
progress: message.progress,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
setCurrentPhase(message.type);
|
||||
addTransparencyMessage(message.message);
|
||||
setStoreGenerationProgress(message.progress);
|
||||
setRefreshProgress(message.progress);
|
||||
messageIndex++;
|
||||
} else {
|
||||
clearInterval(transparencyInterval);
|
||||
}
|
||||
}, 2000); // Send a message every 2 seconds for better UX
|
||||
|
||||
// Call the non-streaming refresh endpoint (Polling-based approach)
|
||||
console.log('🎯 Calling AI refresh endpoint (Polling-based)...');
|
||||
const response = await contentPlanningApi.refreshAutofill(1, true, true);
|
||||
console.log('🎯 Raw Polling Response:', {
|
||||
success: !!response,
|
||||
hasData: !!response?.fields,
|
||||
responseStructure: {
|
||||
hasFieldsProperty: !!response?.fields,
|
||||
hasSourcesProperty: !!response?.sources,
|
||||
hasMetaProperty: !!response?.meta
|
||||
},
|
||||
fieldsCount: Object.keys(response?.fields || {}).length,
|
||||
sourcesCount: Object.keys(response?.sources || {}).length,
|
||||
meta: response?.meta || {},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Clear the transparency interval since we got the response
|
||||
clearInterval(transparencyInterval);
|
||||
|
||||
// Process the response
|
||||
// The API method already returns the extracted data, not the full response
|
||||
if (response) {
|
||||
// Debug the actual response structure
|
||||
console.log('🎯 Raw response structure:', {
|
||||
responseType: typeof response,
|
||||
responseKeys: Object.keys(response),
|
||||
hasFieldsProperty: response?.hasOwnProperty('fields'),
|
||||
hasSourcesProperty: response?.hasOwnProperty('sources'),
|
||||
hasMetaProperty: response?.hasOwnProperty('meta')
|
||||
});
|
||||
|
||||
// Debug the actual response data
|
||||
console.log('🎯 Raw response:', response);
|
||||
console.log('🎯 Raw response.fields:', response?.fields);
|
||||
console.log('🎯 Raw response.sources:', response?.sources);
|
||||
console.log('🎯 Raw response.meta:', response?.meta);
|
||||
|
||||
// The API method already returns the extracted payload from ResponseBuilder
|
||||
// So response is already the payload with fields, sources, meta, etc.
|
||||
const payload = response;
|
||||
|
||||
// Debug the payload structure
|
||||
console.log('🎯 Payload structure:', {
|
||||
payloadType: typeof payload,
|
||||
payloadKeys: Object.keys(payload),
|
||||
hasFieldsProperty: payload?.hasOwnProperty('fields'),
|
||||
hasSourcesProperty: payload?.hasOwnProperty('sources'),
|
||||
hasMetaProperty: payload?.hasOwnProperty('meta'),
|
||||
fieldsKeys: payload?.fields ? Object.keys(payload.fields) : 'no fields'
|
||||
});
|
||||
|
||||
const fields = payload.fields || {};
|
||||
const sources = payload.sources || {};
|
||||
const inputDataPoints = payload.input_data_points || {};
|
||||
const meta = payload.meta || {};
|
||||
|
||||
// Debug the extracted data
|
||||
console.log('🎯 Extracted fields:', fields);
|
||||
console.log('🎯 Extracted sources:', sources);
|
||||
console.log('🎯 Extracted inputDataPoints:', inputDataPoints);
|
||||
console.log('🎯 Extracted meta:', meta);
|
||||
console.log('🎯 Fields count:', Object.keys(fields).length);
|
||||
console.log('🎯 Sources count:', Object.keys(sources).length);
|
||||
console.log('🎯 InputDataPoints count:', Object.keys(inputDataPoints).length);
|
||||
|
||||
console.log('🎯 AI Refresh Result - Payload:', payload);
|
||||
console.log('🎯 AI Refresh Result - Fields:', fields);
|
||||
console.log('🎯 AI Refresh Result - Meta:', meta);
|
||||
console.log('🎯 Fields structure check:', {
|
||||
fieldsType: typeof fields,
|
||||
fieldsKeys: Object.keys(fields),
|
||||
sampleField: fields[Object.keys(fields)[0]],
|
||||
hasValueProperty: fields[Object.keys(fields)[0]]?.hasOwnProperty('value')
|
||||
});
|
||||
|
||||
// 🚨 CRITICAL: Check if AI generation failed
|
||||
if (meta.error || !meta.ai_used) {
|
||||
console.error('❌ AI generation failed:', meta.error || 'AI not used');
|
||||
setError(`AI generation failed: ${meta.error || 'AI was not used for generation. Please try again.'}`);
|
||||
setTransparencyModalOpen(false);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('AI generation failed. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have any fields generated (more lenient validation)
|
||||
const fieldsCount = Object.keys(fields).length;
|
||||
if (fieldsCount === 0) {
|
||||
console.error('❌ No fields generated');
|
||||
setError('No fields were generated. Please try again.');
|
||||
setTransparencyModalOpen(false);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('No fields generated. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`✅ AI generation successful - generated ${fieldsCount} fields, AI overrides: ${meta.ai_overrides_count || 0}`);
|
||||
|
||||
// 🚨 CRITICAL: Validate data source (only check for explicit failure states)
|
||||
if (meta.data_source === 'ai_generation_failed' || meta.data_source === 'ai_generation_error') {
|
||||
console.error('❌ Invalid data source:', meta.data_source);
|
||||
setError(`AI generation failed: ${meta.error || 'Invalid data source. Please try again.'}`);
|
||||
setTransparencyModalOpen(false);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('AI generation failed. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ AI generation successful - processing real AI data');
|
||||
|
||||
const fieldValues: Record<string, any> = {};
|
||||
const confidenceScores: Record<string, number> = {};
|
||||
|
||||
Object.keys(fields).forEach((fieldId) => {
|
||||
const fieldData = fields[fieldId];
|
||||
console.log(`🎯 Processing field ${fieldId}:`, fieldData);
|
||||
|
||||
if (fieldData && typeof fieldData === 'object' && 'value' in fieldData) {
|
||||
fieldValues[fieldId] = fieldData.value;
|
||||
console.log(`✅ Field ${fieldId} value extracted:`, fieldData.value);
|
||||
|
||||
// Extract confidence score if available
|
||||
if (fieldData.confidence) {
|
||||
confidenceScores[fieldId] = fieldData.confidence;
|
||||
console.log(`🎯 Field ${fieldId} confidence: ${fieldData.confidence}`);
|
||||
}
|
||||
|
||||
// Extract personalization data if available
|
||||
if (fieldData.personalization_data) {
|
||||
console.log(`🎯 Field ${fieldId} personalization:`, fieldData.personalization_data);
|
||||
}
|
||||
} else {
|
||||
console.warn(`⚠️ Field ${fieldId} has invalid structure:`, fieldData);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('🎯 Processed field values:', Object.keys(fieldValues));
|
||||
console.log('🎯 Confidence scores:', confidenceScores);
|
||||
console.log('🎯 Field values details:', fieldValues);
|
||||
|
||||
// Update the store with the new data
|
||||
useEnhancedStrategyStore.setState((state) => {
|
||||
const newState = {
|
||||
autoPopulatedFields: { ...state.autoPopulatedFields, ...fieldValues },
|
||||
dataSources: { ...state.dataSources, ...sources },
|
||||
inputDataPoints: { ...state.inputDataPoints, ...inputDataPoints },
|
||||
confidenceScores: { ...state.confidenceScores, ...confidenceScores },
|
||||
formData: { ...state.formData, ...fieldValues }
|
||||
};
|
||||
console.log('🎯 Updated store state:', newState);
|
||||
console.log('🎯 Field values being added:', fieldValues);
|
||||
console.log('🎯 Confidence scores being added:', confidenceScores);
|
||||
console.log('🎯 Store autoPopulatedFields count:', Object.keys(newState.autoPopulatedFields).length);
|
||||
return newState;
|
||||
});
|
||||
|
||||
// Add final completion message
|
||||
addTransparencyMessage(`✅ AI generation completed successfully! Generated ${Object.keys(fieldValues).length} real AI values.`);
|
||||
setStoreGenerationProgress(100);
|
||||
setRefreshProgress(100);
|
||||
setCurrentPhase('Complete');
|
||||
setRefreshMessage(`AI refresh completed! Generated ${Object.keys(fieldValues).length} fields.`);
|
||||
|
||||
// Ensure the educational modal shows the completion state
|
||||
setTimeout(() => {
|
||||
setStoreGenerationProgress(100);
|
||||
setRefreshProgress(100);
|
||||
}, 100);
|
||||
|
||||
// Reset refresh state
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
console.log('🎯 Polling-based AI refresh completed successfully!', {
|
||||
fieldsGenerated: Object.keys(fieldValues).length,
|
||||
confidenceScoresCount: Object.keys(confidenceScores).length,
|
||||
dataSourcesCount: Object.keys(sources).length,
|
||||
approach: 'Polling (No SSE)',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} else {
|
||||
throw new Error('Invalid response from AI refresh endpoint');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('AI refresh error', e);
|
||||
setAIGenerating(false);
|
||||
setIsRefreshing(false);
|
||||
setIsGenerating(false);
|
||||
setRefreshError('AI refresh failed. Please try again.');
|
||||
setRefreshMessage('Refresh failed.');
|
||||
setError(`AI refresh failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
refreshMessage,
|
||||
setRefreshMessage,
|
||||
refreshProgress,
|
||||
setRefreshProgress,
|
||||
isRefreshing,
|
||||
setIsRefreshing,
|
||||
refreshError,
|
||||
setRefreshError,
|
||||
handleAIRefresh
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export const useEventHandlers = () => {
|
||||
const [showTooltip, setShowTooltip] = useState<string | null>(null);
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(null);
|
||||
const [showEducationalInfo, setShowEducationalInfo] = useState<string | null>(null);
|
||||
|
||||
const handleReviewCategory = (categoryId: string) => {
|
||||
setActiveCategory(activeCategory === categoryId ? null : categoryId);
|
||||
};
|
||||
|
||||
const handleShowEducationalInfo = (categoryId: string) => {
|
||||
setShowEducationalInfo(showEducationalInfo === categoryId ? null : categoryId);
|
||||
};
|
||||
|
||||
return {
|
||||
showTooltip,
|
||||
setShowTooltip,
|
||||
activeCategory,
|
||||
setActiveCategory,
|
||||
showEducationalInfo,
|
||||
setShowEducationalInfo,
|
||||
handleReviewCategory,
|
||||
handleShowEducationalInfo
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface UseModalManagementProps {
|
||||
aiGenerating: boolean;
|
||||
originalHandleCreateStrategy: (() => Promise<void>) | null;
|
||||
}
|
||||
|
||||
export const useModalManagement = ({ aiGenerating, originalHandleCreateStrategy }: UseModalManagementProps) => {
|
||||
const [showEducationalModal, setShowEducationalModal] = useState(false);
|
||||
const [showEnterpriseModal, setShowEnterpriseModal] = useState(false);
|
||||
|
||||
// Persist enterprise modal state across hot reloads
|
||||
useEffect(() => {
|
||||
const savedModalState = sessionStorage.getItem('showEnterpriseModal');
|
||||
if (savedModalState === 'true') {
|
||||
console.log('🎯 Restoring enterprise modal state from sessionStorage');
|
||||
setShowEnterpriseModal(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save modal state to sessionStorage when it changes
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem('showEnterpriseModal', showEnterpriseModal.toString());
|
||||
}, [showEnterpriseModal]);
|
||||
|
||||
// Cleanup sessionStorage on component unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Only clear if we're not in the middle of showing the modal
|
||||
if (!showEnterpriseModal) {
|
||||
sessionStorage.removeItem('showEnterpriseModal');
|
||||
}
|
||||
};
|
||||
}, [showEnterpriseModal]);
|
||||
|
||||
// Monitor enterprise modal state for debugging
|
||||
useEffect(() => {
|
||||
console.log('🎯 Enterprise modal state changed - showEnterpriseModal:', showEnterpriseModal);
|
||||
|
||||
// If modal was unexpectedly closed, log it
|
||||
if (!showEnterpriseModal && aiGenerating) {
|
||||
console.warn('🎯 WARNING: Enterprise modal closed while AI is generating');
|
||||
}
|
||||
|
||||
// Only warn about unexpected closure if it's not due to hot reload
|
||||
if (!showEnterpriseModal && !aiGenerating) {
|
||||
const savedModalState = sessionStorage.getItem('showEnterpriseModal');
|
||||
if (savedModalState !== 'true') {
|
||||
console.warn('🎯 WARNING: Enterprise modal closed unexpectedly (not due to hot reload)');
|
||||
}
|
||||
}
|
||||
}, [showEnterpriseModal, aiGenerating]);
|
||||
|
||||
// Handle proceed with current strategy (30 fields)
|
||||
const handleProceedWithCurrentStrategy = async () => {
|
||||
console.log('🎯 User clicked "Proceed with Current Strategy"');
|
||||
setShowEnterpriseModal(false);
|
||||
sessionStorage.removeItem('showEnterpriseModal'); // Clear sessionStorage
|
||||
|
||||
// Add a small delay to ensure modal closes properly before showing educational modal
|
||||
setTimeout(async () => {
|
||||
console.log('🎯 Calling original handleCreateStrategy after enterprise modal closes');
|
||||
try {
|
||||
// Ensure we're not already generating
|
||||
if (!aiGenerating && originalHandleCreateStrategy) {
|
||||
console.log('🎯 Starting strategy generation...');
|
||||
await originalHandleCreateStrategy();
|
||||
} else {
|
||||
console.log('🎯 Already generating, skipping duplicate call');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('🎯 Error in handleProceedWithCurrentStrategy:', error);
|
||||
}
|
||||
}, 300); // Increased delay to ensure modal closes completely
|
||||
};
|
||||
|
||||
// Handle add enterprise datapoints (coming soon)
|
||||
const handleAddEnterpriseDatapoints = async () => {
|
||||
console.log('🎯 User clicked "Add Enterprise Datapoints"');
|
||||
setShowEnterpriseModal(false);
|
||||
sessionStorage.removeItem('showEnterpriseModal'); // Clear sessionStorage
|
||||
|
||||
// For now, just proceed with current strategy
|
||||
// In Phase 2, this will enable enterprise datapoints
|
||||
setTimeout(async () => {
|
||||
console.log('🎯 Calling original handleCreateStrategy for enterprise datapoints');
|
||||
try {
|
||||
// Ensure we're not already generating
|
||||
if (!aiGenerating && originalHandleCreateStrategy) {
|
||||
await originalHandleCreateStrategy();
|
||||
} else {
|
||||
console.log('🎯 Already generating, skipping duplicate call');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('🎯 Error in handleAddEnterpriseDatapoints:', error);
|
||||
}
|
||||
}, 200); // Increased delay to ensure modal closes completely
|
||||
};
|
||||
|
||||
return {
|
||||
showEducationalModal,
|
||||
setShowEducationalModal,
|
||||
showEnterpriseModal,
|
||||
setShowEnterpriseModal,
|
||||
handleProceedWithCurrentStrategy,
|
||||
handleAddEnterpriseDatapoints
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useActionButtonsBusinessLogic } from '../components/ActionButtons';
|
||||
|
||||
interface UseStrategyCreationProps {
|
||||
formData: 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: (data: any) => Promise<any>;
|
||||
contentPlanningApi: any;
|
||||
}
|
||||
|
||||
export const useStrategyCreation = ({
|
||||
formData,
|
||||
error,
|
||||
currentStrategy,
|
||||
setAIGenerating,
|
||||
setError,
|
||||
setCurrentStrategy,
|
||||
setSaving,
|
||||
setGenerationProgress,
|
||||
setEducationalContent,
|
||||
setShowEducationalModal,
|
||||
validateAllFields,
|
||||
getCompletionStats,
|
||||
generateAIRecommendations,
|
||||
createEnhancedStrategy,
|
||||
contentPlanningApi
|
||||
}: UseStrategyCreationProps) => {
|
||||
// Use ActionButtons business logic hook
|
||||
const { handleCreateStrategy: originalHandleCreateStrategy, handleSaveStrategy } = useActionButtonsBusinessLogic({
|
||||
formData,
|
||||
error,
|
||||
currentStrategy,
|
||||
setAIGenerating,
|
||||
setError,
|
||||
setCurrentStrategy,
|
||||
setSaving,
|
||||
setGenerationProgress,
|
||||
setEducationalContent,
|
||||
setShowEducationalModal,
|
||||
validateAllFields,
|
||||
getCompletionStats,
|
||||
generateAIRecommendations,
|
||||
createEnhancedStrategy,
|
||||
contentPlanningApi
|
||||
});
|
||||
|
||||
return {
|
||||
originalHandleCreateStrategy: () => originalHandleCreateStrategy(),
|
||||
handleSaveStrategy: () => handleSaveStrategy()
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
export const setupCSSAnimations = () => {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
return style;
|
||||
};
|
||||
|
||||
export const cleanupCSSAnimations = (style: HTMLStyleElement) => {
|
||||
if (document.head.contains(style)) {
|
||||
document.head.removeChild(style);
|
||||
}
|
||||
};
|
||||
@@ -373,6 +373,259 @@ const CompetitiveAnalysisCard: React.FC<CompetitiveAnalysisCardProps> = ({ strat
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Competitive Advantages */}
|
||||
{strategyData.competitive_analysis.competitive_advantages && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Competitive Advantages
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
|
||||
{/* Development Areas */}
|
||||
{strategyData.competitive_analysis.competitive_advantages.development_areas &&
|
||||
strategyData.competitive_analysis.competitive_advantages.development_areas.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.info, fontWeight: 600, mb: 1 }}>
|
||||
Development Areas ({strategyData.competitive_analysis.competitive_advantages.development_areas.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.competitive_advantages.development_areas.map((area: 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={area}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Primary Advantages */}
|
||||
{strategyData.competitive_analysis.competitive_advantages.primary &&
|
||||
strategyData.competitive_analysis.competitive_advantages.primary.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.primary, fontWeight: 600, mb: 1 }}>
|
||||
Primary Advantages ({strategyData.competitive_analysis.competitive_advantages.primary.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.competitive_advantages.primary.map((advantage: 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.primary,
|
||||
opacity: 0.7
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={advantage}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Sustainable Advantages */}
|
||||
{strategyData.competitive_analysis.competitive_advantages.sustainable &&
|
||||
strategyData.competitive_analysis.competitive_advantages.sustainable.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
|
||||
Sustainable Advantages ({strategyData.competitive_analysis.competitive_advantages.sustainable.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.competitive_advantages.sustainable.map((advantage: 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={advantage}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* SWOT Competitive Insights */}
|
||||
{strategyData.competitive_analysis.swot_competitive_insights && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
SWOT Competitive Insights
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
|
||||
{/* Leverage Strengths */}
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.leverage_strengths &&
|
||||
strategyData.competitive_analysis.swot_competitive_insights.leverage_strengths.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
|
||||
Leverage Strengths ({strategyData.competitive_analysis.swot_competitive_insights.leverage_strengths.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.leverage_strengths.map((strength: 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={strength}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Address Weaknesses */}
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.address_weaknesses &&
|
||||
strategyData.competitive_analysis.swot_competitive_insights.address_weaknesses.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.warning, fontWeight: 600, mb: 1 }}>
|
||||
Address Weaknesses ({strategyData.competitive_analysis.swot_competitive_insights.address_weaknesses.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.address_weaknesses.map((weakness: 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={weakness}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Capitalize Opportunities */}
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.capitalize_opportunities &&
|
||||
strategyData.competitive_analysis.swot_competitive_insights.capitalize_opportunities.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.info, fontWeight: 600, mb: 1 }}>
|
||||
Capitalize Opportunities ({strategyData.competitive_analysis.swot_competitive_insights.capitalize_opportunities.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.capitalize_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.info,
|
||||
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>
|
||||
)}
|
||||
|
||||
{/* Mitigate Threats */}
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.mitigate_threats &&
|
||||
strategyData.competitive_analysis.swot_competitive_insights.mitigate_threats.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.error, fontWeight: 600, mb: 1 }}>
|
||||
Mitigate Threats ({strategyData.competitive_analysis.swot_competitive_insights.mitigate_threats.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.competitive_analysis.swot_competitive_insights.mitigate_threats.map((threat: 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.error,
|
||||
opacity: 0.7
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={threat}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
@@ -97,27 +97,28 @@ const ImplementationRoadmapCard: React.FC<ImplementationRoadmapCardProps> = ({ s
|
||||
color: 'white',
|
||||
fontSize: '1.2rem',
|
||||
fontWeight: 600,
|
||||
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.info}30`
|
||||
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.info}30`,
|
||||
flexShrink: 0
|
||||
}}>
|
||||
{strategyData.implementation_roadmap.phases?.length || 0}
|
||||
{strategyData.implementation_roadmap.timeline}
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, fontWeight: 600 }}>
|
||||
Project Phases
|
||||
Implementation Roadmap
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary }}>
|
||||
{strategyData.implementation_roadmap.total_duration || '6 months'} implementation
|
||||
{strategyData.implementation_roadmap.timeline} implementation timeline
|
||||
</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`}
|
||||
label={`${strategyData.implementation_roadmap.phases?.length || 0} Phases`}
|
||||
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`}
|
||||
label={`${strategyData.implementation_roadmap.milestones?.length || 0} Milestones`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
/>
|
||||
@@ -125,26 +126,40 @@ const ImplementationRoadmapCard: React.FC<ImplementationRoadmapCardProps> = ({ s
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Phases Preview */}
|
||||
{/* Timeline Preview */}
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 600 }}>
|
||||
Implementation Phases
|
||||
Project Timeline
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
|
||||
{strategyData.implementation_roadmap.phases?.slice(0, 4).map((phase: any, index: number) => (
|
||||
<Chip
|
||||
label={`Duration: ${strategyData.implementation_roadmap.timeline}`}
|
||||
size="small"
|
||||
icon={<TimelineIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
/>
|
||||
{strategyData.implementation_roadmap.phases && strategyData.implementation_roadmap.phases.length > 0 && (
|
||||
<Chip
|
||||
key={index}
|
||||
label={`Phase ${index + 1}`}
|
||||
label={`${strategyData.implementation_roadmap.phases.length} Phases`}
|
||||
size="small"
|
||||
icon={<TimelineIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
|
||||
/>
|
||||
))}
|
||||
{(strategyData.implementation_roadmap.phases?.length || 0) > 4 && (
|
||||
)}
|
||||
{strategyData.implementation_roadmap.milestones && strategyData.implementation_roadmap.milestones.length > 0 && (
|
||||
<Chip
|
||||
label={`+${(strategyData.implementation_roadmap.phases?.length || 0) - 4} more`}
|
||||
label={`${strategyData.implementation_roadmap.milestones.length} Milestones`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
|
||||
icon={<CheckCircleIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
|
||||
/>
|
||||
)}
|
||||
{strategyData.implementation_roadmap.resource_requirements && strategyData.implementation_roadmap.resource_requirements.length > 0 && (
|
||||
<Chip
|
||||
label={`${strategyData.implementation_roadmap.resource_requirements.length} Resources`}
|
||||
size="small"
|
||||
icon={<GroupIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
@@ -156,60 +171,63 @@ const ImplementationRoadmapCard: React.FC<ImplementationRoadmapCardProps> = ({ s
|
||||
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 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.timeline}
|
||||
</Typography>
|
||||
</Box>
|
||||
{strategyData.implementation_roadmap.timeline && (
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary, mb: 1 }}>
|
||||
Timeline: {strategyData.implementation_roadmap.timeline}
|
||||
</Typography>
|
||||
)}
|
||||
{strategyData.implementation_roadmap.timeline_object?.start_date && (
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary, mb: 1 }}>
|
||||
Start Date: {strategyData.implementation_roadmap.timeline_object.start_date}
|
||||
</Typography>
|
||||
)}
|
||||
{strategyData.implementation_roadmap.timeline_object?.end_date && (
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.secondary, mb: 1 }}>
|
||||
End Date: {strategyData.implementation_roadmap.timeline_object.end_date}
|
||||
</Typography>
|
||||
)}
|
||||
{strategyData.implementation_roadmap.timeline_object?.key_milestones && strategyData.implementation_roadmap.timeline_object.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_object.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 && (
|
||||
@@ -242,10 +260,10 @@ const ImplementationRoadmapCard: React.FC<ImplementationRoadmapCardProps> = ({ s
|
||||
</Box>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body2" sx={accordionStyles.accordionTitle}>
|
||||
{phase.name || `Phase ${index + 1}`}
|
||||
{phase.phase || `Phase ${index + 1}`}
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={accordionStyles.accordionSubtitle}>
|
||||
{phase.duration || '2 months'} • {phase.tasks?.length || 0} tasks • {phase.milestones?.length || 0} milestones
|
||||
{phase.duration} • {phase.tasks?.length || 0} tasks • {phase.milestones?.length || 0} milestones
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -357,6 +375,102 @@ const ImplementationRoadmapCard: React.FC<ImplementationRoadmapCardProps> = ({ s
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Milestones */}
|
||||
{strategyData.implementation_roadmap.milestones && strategyData.implementation_roadmap.milestones.length > 0 && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Project Milestones ({strategyData.implementation_roadmap.milestones.length})
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{strategyData.implementation_roadmap.milestones.map((milestone: 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={milestone}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Resource Requirements */}
|
||||
{strategyData.implementation_roadmap.resource_requirements && strategyData.implementation_roadmap.resource_requirements.length > 0 && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Resource Requirements ({strategyData.implementation_roadmap.resource_requirements.length})
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{strategyData.implementation_roadmap.resource_requirements.map((requirement: 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={requirement}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Critical Path */}
|
||||
{strategyData.implementation_roadmap.critical_path && strategyData.implementation_roadmap.critical_path.length > 0 && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Critical Path ({strategyData.implementation_roadmap.critical_path.length})
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{strategyData.implementation_roadmap.critical_path.map((path: 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.error,
|
||||
opacity: 0.7
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={path}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Resource Allocation */}
|
||||
{strategyData.implementation_roadmap.resource_allocation && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
|
||||
@@ -98,7 +98,7 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.success}30`,
|
||||
flexShrink: 0
|
||||
}}>
|
||||
{strategyData.performance_predictions.roi_predictions?.estimated_roi || '25%'}
|
||||
{strategyData.performance_predictions.estimated_roi || '25%'}
|
||||
</Box>
|
||||
<Box sx={{
|
||||
minWidth: 0,
|
||||
@@ -113,7 +113,7 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
mb: 0.5,
|
||||
wordBreak: 'break-word'
|
||||
}}>
|
||||
ROI Predictions
|
||||
Performance Predictions
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
|
||||
@@ -121,7 +121,7 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
lineHeight: 1.2,
|
||||
wordBreak: 'break-word'
|
||||
}}>
|
||||
Expected return on investment
|
||||
Expected ROI and success metrics
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -133,12 +133,12 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<Chip
|
||||
label={`${(strategyData.performance_predictions as any)?.success_probability || '85%'} Success`}
|
||||
label={`${strategyData.performance_predictions.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'}`}
|
||||
label="12 months"
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
|
||||
/>
|
||||
@@ -154,7 +154,7 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
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.
|
||||
ROI of {strategyData.performance_predictions.estimated_roi || '20-30%'} is achievable with {strategyData.performance_predictions.success_probability || '85%'} success probability.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -175,24 +175,30 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
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}
|
||||
/>
|
||||
{strategyData.performance_predictions.traffic_growth && (
|
||||
<Chip
|
||||
label={`${strategyData.performance_predictions.traffic_growth.month_12 || '100%'} Traffic`}
|
||||
size="small"
|
||||
icon={<TrendingUpIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
/>
|
||||
)}
|
||||
{strategyData.performance_predictions.engagement_metrics && (
|
||||
<Chip
|
||||
label={`${strategyData.performance_predictions.engagement_metrics.time_on_page || '3-5 min'}`}
|
||||
size="small"
|
||||
icon={<AssessmentIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
|
||||
/>
|
||||
)}
|
||||
{strategyData.performance_predictions.conversion_predictions && (
|
||||
<Chip
|
||||
label={`${strategyData.performance_predictions.conversion_predictions.lead_generation || '5-8%'} Leads`}
|
||||
size="small"
|
||||
icon={<ShowChartIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.accent).chip}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -214,7 +220,7 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
lineHeight: 1.5,
|
||||
wordBreak: 'break-word'
|
||||
}}>
|
||||
Estimated ROI: {strategyData.performance_predictions.roi_predictions?.estimated_roi || '25%'}
|
||||
Estimated ROI: {strategyData.performance_predictions.estimated_roi || '20-30%'}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
|
||||
@@ -222,268 +228,298 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
lineHeight: 1.5,
|
||||
wordBreak: 'break-word'
|
||||
}}>
|
||||
Success Probability: {(strategyData.performance_predictions as any)?.success_probability || '85%'}
|
||||
Success Probability: {strategyData.performance_predictions.success_probability || '85%'}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
|
||||
fontSize: '0.85rem',
|
||||
lineHeight: 1.5,
|
||||
wordBreak: 'break-word'
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Traffic Growth */}
|
||||
{strategyData.performance_predictions.traffic_growth && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Traffic Growth Projections
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<Box sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: { xs: '1fr', sm: 'repeat(auto-fit, minmax(150px, 1fr))' },
|
||||
gap: 2
|
||||
}}>
|
||||
Implementation Timeline: {(strategyData.performance_predictions as any)?.implementation_timeline || '6 months'}
|
||||
</Typography>
|
||||
{strategyData.performance_predictions.traffic_growth.month_3 && (
|
||||
<Box sx={{
|
||||
p: 2,
|
||||
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.success}`,
|
||||
borderRadius: 2,
|
||||
background: `rgba(76, 175, 80, 0.1)`,
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
<Typography variant="body2" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.success,
|
||||
fontWeight: 600,
|
||||
mb: 1,
|
||||
fontSize: '0.8rem'
|
||||
}}>
|
||||
Month 3
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.primary,
|
||||
fontSize: '1.2rem',
|
||||
fontWeight: 700
|
||||
}}>
|
||||
{strategyData.performance_predictions.traffic_growth.month_3}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{strategyData.performance_predictions.traffic_growth.month_6 && (
|
||||
<Box sx={{
|
||||
p: 2,
|
||||
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.primary}`,
|
||||
borderRadius: 2,
|
||||
background: `rgba(63, 81, 181, 0.1)`,
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
<Typography variant="body2" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.primary,
|
||||
fontWeight: 600,
|
||||
mb: 1,
|
||||
fontSize: '0.8rem'
|
||||
}}>
|
||||
Month 6
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.primary,
|
||||
fontSize: '1.2rem',
|
||||
fontWeight: 700
|
||||
}}>
|
||||
{strategyData.performance_predictions.traffic_growth.month_6}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{strategyData.performance_predictions.traffic_growth.month_12 && (
|
||||
<Box sx={{
|
||||
p: 2,
|
||||
border: `1px solid ${ANALYSIS_CARD_STYLES.colors.accent}`,
|
||||
borderRadius: 2,
|
||||
background: `rgba(240, 147, 251, 0.1)`,
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
<Typography variant="body2" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.accent,
|
||||
fontWeight: 600,
|
||||
mb: 1,
|
||||
fontSize: '0.8rem'
|
||||
}}>
|
||||
Month 12
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.primary,
|
||||
fontSize: '1.2rem',
|
||||
fontWeight: 700
|
||||
}}>
|
||||
{strategyData.performance_predictions.traffic_growth.month_12}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</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>
|
||||
)}
|
||||
{/* Engagement Metrics */}
|
||||
{strategyData.performance_predictions.engagement_metrics && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Engagement Metrics
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{strategyData.performance_predictions.engagement_metrics.bounce_rate && (
|
||||
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
|
||||
<ListItemIcon sx={listItemStyles.listItemIcon}>
|
||||
<Box sx={{
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: ANALYSIS_CARD_STYLES.colors.warning,
|
||||
opacity: 0.7
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Bounce Rate"
|
||||
secondary={strategyData.performance_predictions.engagement_metrics.bounce_rate}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { color: ANALYSIS_CARD_STYLES.colors.warning, fontWeight: 600, mb: 0.5 }
|
||||
}}
|
||||
secondaryTypographyProps={{
|
||||
variant: 'caption',
|
||||
fontSize: '0.75rem',
|
||||
sx: { color: ANALYSIS_CARD_STYLES.colors.text.secondary, lineHeight: 1.4 }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
{strategyData.performance_predictions.engagement_metrics.time_on_page && (
|
||||
<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="Time on Page"
|
||||
secondary={strategyData.performance_predictions.engagement_metrics.time_on_page}
|
||||
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>
|
||||
)}
|
||||
{strategyData.performance_predictions.engagement_metrics.social_shares && (
|
||||
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
|
||||
<ListItemIcon sx={listItemStyles.listItemIcon}>
|
||||
<Box sx={{
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: ANALYSIS_CARD_STYLES.colors.info,
|
||||
opacity: 0.7
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Social Shares"
|
||||
secondary={strategyData.performance_predictions.engagement_metrics.social_shares}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { color: ANALYSIS_CARD_STYLES.colors.info, 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>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Conversion Predictions */}
|
||||
{strategyData.performance_predictions.conversion_predictions && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Conversion Predictions
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{strategyData.performance_predictions.conversion_predictions.content_downloads && (
|
||||
<ListItem sx={{ ...listItemStyles.listItem, py: 1.5 }}>
|
||||
<ListItemIcon sx={listItemStyles.listItemIcon}>
|
||||
<Box sx={{
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: ANALYSIS_CARD_STYLES.colors.primary,
|
||||
opacity: 0.7
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Content Downloads"
|
||||
secondary={strategyData.performance_predictions.conversion_predictions.content_downloads}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { color: ANALYSIS_CARD_STYLES.colors.primary, fontWeight: 600, mb: 0.5 }
|
||||
}}
|
||||
secondaryTypographyProps={{
|
||||
variant: 'caption',
|
||||
fontSize: '0.75rem',
|
||||
sx: { color: ANALYSIS_CARD_STYLES.colors.text.secondary, lineHeight: 1.4 }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
{strategyData.performance_predictions.conversion_predictions.email_signups && (
|
||||
<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="Email Signups"
|
||||
secondary={strategyData.performance_predictions.conversion_predictions.email_signups}
|
||||
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>
|
||||
)}
|
||||
{strategyData.performance_predictions.conversion_predictions.lead_generation && (
|
||||
<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="Lead Generation"
|
||||
secondary={strategyData.performance_predictions.conversion_predictions.lead_generation}
|
||||
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>
|
||||
)}
|
||||
</List>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Divider sx={{ my: 2, opacity: 0.2, borderColor: ANALYSIS_CARD_STYLES.colors.border.secondary }} />
|
||||
|
||||
{/* Detailed Predictions */}
|
||||
{/* Success Factors */}
|
||||
<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
|
||||
Success Factors
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
@@ -492,28 +528,28 @@ const PerformancePredictionsCard: React.FC<PerformancePredictionsCardProps> = ({
|
||||
fontSize: '0.8rem',
|
||||
lineHeight: 1.5
|
||||
}}>
|
||||
• Month 1-2: Initial setup and foundation building
|
||||
• High success probability of {strategyData.performance_predictions.success_probability || '85%'}
|
||||
</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
|
||||
• Expected ROI of {strategyData.performance_predictions.estimated_roi || '20-30%'}
|
||||
</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
|
||||
• Traffic growth from {strategyData.performance_predictions.traffic_growth?.month_3 || '25%'} to {strategyData.performance_predictions.traffic_growth?.month_12 || '100%'}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{
|
||||
color: ANALYSIS_CARD_STYLES.colors.text.secondary,
|
||||
fontSize: '0.8rem',
|
||||
lineHeight: 1.5
|
||||
}}>
|
||||
• Ongoing: Continuous monitoring and improvement
|
||||
• Lead generation improvement of {strategyData.performance_predictions.conversion_predictions?.lead_generation || '5-8%'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -278,13 +278,13 @@ const RiskAssessmentCard: React.FC<RiskAssessmentCardProps> = ({ strategyData })
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{strategyData.risk_assessment.mitigation_strategies.map((strategy: string, index: number) => (
|
||||
{strategyData.risk_assessment.mitigation_strategies.map((strategy: any, 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}
|
||||
primary={typeof strategy === 'string' ? strategy : strategy.mitigation || strategy.risk || 'Mitigation strategy'}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
@@ -305,31 +305,36 @@ const RiskAssessmentCard: React.FC<RiskAssessmentCardProps> = ({ strategyData })
|
||||
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 }} />
|
||||
{Object.entries(strategyData.risk_assessment.risk_categories).map(([category, risks]: [string, any]) => {
|
||||
// Skip empty arrays or null values
|
||||
if (!risks || !Array.isArray(risks) || risks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<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}>
|
||||
{risks.length} risks
|
||||
</Typography>
|
||||
</Box>
|
||||
</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) ? (
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ pt: 0 }}>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<List dense>
|
||||
{risks.map((risk: string, index: number) => (
|
||||
{risks.map((risk: any, index: number) => (
|
||||
<ListItem key={index} sx={listItemStyles.listItem}>
|
||||
<ListItemIcon sx={listItemStyles.listItemIcon}>
|
||||
<Box sx={{
|
||||
@@ -341,7 +346,7 @@ const RiskAssessmentCard: React.FC<RiskAssessmentCardProps> = ({ strategyData })
|
||||
}} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={risk}
|
||||
primary={typeof risk === 'string' ? risk : risk.risk || 'Risk'}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
@@ -351,35 +356,38 @@ const RiskAssessmentCard: React.FC<RiskAssessmentCardProps> = ({ strategyData })
|
||||
</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>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Monitoring Framework */}
|
||||
{strategyData.risk_assessment.monitoring_framework && (
|
||||
{strategyData.risk_assessment.monitoring_framework && Object.keys(strategyData.risk_assessment.monitoring_framework).length > 0 && (
|
||||
<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>
|
||||
))}
|
||||
{Object.entries(strategyData.risk_assessment.monitoring_framework).map(([key, value]: [string, any]) => {
|
||||
// Skip empty values
|
||||
if (!value || (Array.isArray(value) && value.length === 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<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>
|
||||
)}
|
||||
|
||||
@@ -20,15 +20,13 @@ import {
|
||||
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
|
||||
getListItemStyles
|
||||
} from '../styles';
|
||||
import ProgressiveCard from './ProgressiveCard';
|
||||
|
||||
@@ -41,10 +39,16 @@ const StrategicInsightsCard: React.FC<StrategicInsightsCardProps> = ({ strategyD
|
||||
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);
|
||||
console.log('🔍 StrategicInsightsCard - market_positioning:', strategyData?.strategic_insights?.market_positioning);
|
||||
console.log('🔍 StrategicInsightsCard - swot_analysis:', strategyData?.strategic_insights?.market_positioning?.swot_analysis);
|
||||
console.log('🔍 StrategicInsightsCard - strengths:', strategyData?.strategic_insights?.market_positioning?.swot_analysis?.strengths);
|
||||
console.log('🔍 StrategicInsightsCard - opportunities:', strategyData?.strategic_insights?.market_positioning?.swot_analysis?.opportunities);
|
||||
console.log('🔍 StrategicInsightsCard - content_opportunities:', strategyData?.strategic_insights?.content_opportunities);
|
||||
console.log('🔍 StrategicInsightsCard - growth_potential:', strategyData?.strategic_insights?.growth_potential);
|
||||
console.log('🔍 StrategicInsightsCard - swot_summary:', strategyData?.strategic_insights?.swot_summary);
|
||||
|
||||
if (!strategyData?.strategic_insights) {
|
||||
return (
|
||||
@@ -105,25 +109,27 @@ const StrategicInsightsCard: React.FC<StrategicInsightsCardProps> = ({ strategyD
|
||||
fontWeight: 600,
|
||||
boxShadow: `0 4px 12px ${ANALYSIS_CARD_STYLES.colors.success}30`
|
||||
}}>
|
||||
85%
|
||||
{strategyData.strategic_insights.market_positioning?.positioning_strength ||
|
||||
strategyData.strategic_insights.swot_summary?.overall_score ||
|
||||
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
|
||||
{strategyData.strategic_insights.market_positioning?.current_position || 'Strong'} market positioning identified
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<Chip
|
||||
label="High Growth"
|
||||
label={strategyData.strategic_insights.growth_potential?.growth_rate || "High Growth"}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
|
||||
/>
|
||||
<Chip
|
||||
label="6 months"
|
||||
label={strategyData.strategic_insights.growth_potential?.market_size || "Growing Market"}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
|
||||
/>
|
||||
@@ -137,22 +143,52 @@ const StrategicInsightsCard: React.FC<StrategicInsightsCardProps> = ({ strategyD
|
||||
Key Insights Preview
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
|
||||
{strategyData.strategic_insights.insights?.slice(0, 3).map((insight: any, index: number) => (
|
||||
{/* Show content opportunities as key insights */}
|
||||
{strategyData.strategic_insights.content_opportunities?.slice(0, 2).map((opportunity: string, index: number) => (
|
||||
<Chip
|
||||
key={index}
|
||||
label={insight.type}
|
||||
key={`content-${index}`}
|
||||
label={`Opportunity ${index + 1}`}
|
||||
size="small"
|
||||
icon={getInsightIcon(insight.type)}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
icon={<LightbulbIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
|
||||
/>
|
||||
))}
|
||||
{(strategyData.strategic_insights.insights?.length || 0) > 3 && (
|
||||
{/* Show growth drivers as key insights */}
|
||||
{strategyData.strategic_insights.growth_potential?.key_drivers?.slice(0, 1).map((driver: string, index: number) => (
|
||||
<Chip
|
||||
label={`+${(strategyData.strategic_insights.insights?.length || 0) - 3} more`}
|
||||
key={`driver-${index}`}
|
||||
label="Growth Driver"
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip}
|
||||
icon={<TrendingUpIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip}
|
||||
/>
|
||||
)}
|
||||
))}
|
||||
{/* Show SWOT insights */}
|
||||
{(() => {
|
||||
const strengthsLength = strategyData.strategic_insights.market_positioning?.swot_analysis?.strengths?.length || 0;
|
||||
const opportunitiesLength = strategyData.strategic_insights.market_positioning?.swot_analysis?.opportunities?.length || 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
{strengthsLength > 0 && (
|
||||
<Chip
|
||||
label={`${strengthsLength} Strengths`}
|
||||
size="small"
|
||||
icon={<AnalyticsIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
/>
|
||||
)}
|
||||
{opportunitiesLength > 0 && (
|
||||
<Chip
|
||||
label={`${opportunitiesLength} Opportunities`}
|
||||
size="small"
|
||||
icon={<PsychologyIcon />}
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -161,6 +197,244 @@ const StrategicInsightsCard: React.FC<StrategicInsightsCardProps> = ({ strategyD
|
||||
// Detailed content - shown on expansion
|
||||
const detailedContent = (
|
||||
<Box>
|
||||
{/* Market Positioning */}
|
||||
{strategyData.strategic_insights.market_positioning && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Market Positioning
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<Box sx={{ display: 'flex', gap: 2, mb: 2 }}>
|
||||
<Chip
|
||||
label={`Position: ${strategyData.strategic_insights.market_positioning.current_position}`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
/>
|
||||
<Chip
|
||||
label={`Strength: ${strategyData.strategic_insights.market_positioning.positioning_strength}%`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* SWOT Analysis */}
|
||||
{strategyData.strategic_insights.market_positioning.swot_analysis && (
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 500 }}>
|
||||
SWOT Analysis
|
||||
</Typography>
|
||||
|
||||
{/* Strengths */}
|
||||
{strategyData.strategic_insights.market_positioning.swot_analysis.strengths &&
|
||||
strategyData.strategic_insights.market_positioning.swot_analysis.strengths.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, display: 'block', mb: 1 }}>
|
||||
Strengths ({strategyData.strategic_insights.market_positioning.swot_analysis.strengths.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.strategic_insights.market_positioning.swot_analysis.strengths.map((strength: 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={strength}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Opportunities */}
|
||||
{strategyData.strategic_insights.market_positioning.swot_analysis.opportunities &&
|
||||
strategyData.strategic_insights.market_positioning.swot_analysis.opportunities.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="caption" sx={{ color: ANALYSIS_CARD_STYLES.colors.info, fontWeight: 600, display: 'block', mb: 1 }}>
|
||||
Opportunities ({strategyData.strategic_insights.market_positioning.swot_analysis.opportunities.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.strategic_insights.market_positioning.swot_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.info,
|
||||
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>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Growth Potential */}
|
||||
{strategyData.strategic_insights.growth_potential && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
Growth Potential
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<Box sx={{ display: 'flex', gap: 2, mb: 2 }}>
|
||||
<Chip
|
||||
label={`Market: ${strategyData.strategic_insights.growth_potential.market_size}`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip}
|
||||
/>
|
||||
<Chip
|
||||
label={`Growth: ${strategyData.strategic_insights.growth_potential.growth_rate}`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Key Drivers */}
|
||||
{strategyData.strategic_insights.growth_potential.key_drivers &&
|
||||
strategyData.strategic_insights.growth_potential.key_drivers.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 1, fontWeight: 500 }}>
|
||||
Key Growth Drivers ({strategyData.strategic_insights.growth_potential.key_drivers.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.strategic_insights.growth_potential.key_drivers.map((driver: 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={driver}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* SWOT Summary */}
|
||||
{strategyData.strategic_insights.swot_summary && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ color: ANALYSIS_CARD_STYLES.colors.text.primary, mb: 2, fontWeight: 600 }}>
|
||||
SWOT Summary
|
||||
</Typography>
|
||||
<Box sx={sectionStyles.sectionContainer}>
|
||||
<Box sx={{ display: 'flex', gap: 2, mb: 2 }}>
|
||||
<Chip
|
||||
label={`Overall Score: ${strategyData.strategic_insights.swot_summary.overall_score}%`}
|
||||
size="small"
|
||||
sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Primary Strengths */}
|
||||
{strategyData.strategic_insights.swot_summary.primary_strengths &&
|
||||
strategyData.strategic_insights.swot_summary.primary_strengths.length > 0 && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.success, fontWeight: 600, mb: 1 }}>
|
||||
Primary Strengths ({strategyData.strategic_insights.swot_summary.primary_strengths.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.strategic_insights.swot_summary.primary_strengths.map((strength: 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={strength}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontSize: '0.875rem',
|
||||
sx: { lineHeight: 1.4, color: ANALYSIS_CARD_STYLES.colors.text.primary }
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Key Opportunities */}
|
||||
{strategyData.strategic_insights.swot_summary.key_opportunities &&
|
||||
strategyData.strategic_insights.swot_summary.key_opportunities.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ color: ANALYSIS_CARD_STYLES.colors.info, fontWeight: 600, mb: 1 }}>
|
||||
Key Opportunities ({strategyData.strategic_insights.swot_summary.key_opportunities.length})
|
||||
</Typography>
|
||||
<List dense>
|
||||
{strategyData.strategic_insights.swot_summary.key_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.info,
|
||||
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>
|
||||
)}
|
||||
|
||||
{/* Strategic Insights by Type */}
|
||||
{strategyData.strategic_insights.insights && strategyData.strategic_insights.insights.length > 0 && (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
|
||||
@@ -25,15 +25,23 @@ export const useStrategyData = () => {
|
||||
try {
|
||||
const latestStrategyResponse = await contentPlanningApi.getLatestGeneratedStrategy(userId);
|
||||
|
||||
if (latestStrategyResponse?.strategy) {
|
||||
console.log('✅ Found latest generated strategy from polling system:', latestStrategyResponse.strategy);
|
||||
console.log('🔍 Latest strategy response from API:', latestStrategyResponse);
|
||||
console.log('🔍 Response type:', typeof latestStrategyResponse);
|
||||
console.log('🔍 Response keys:', Object.keys(latestStrategyResponse || {}));
|
||||
|
||||
if (latestStrategyResponse && latestStrategyResponse.strategic_insights) {
|
||||
// If the response itself is the strategy data (after API extraction)
|
||||
console.log('✅ Found latest generated strategy (direct response):', latestStrategyResponse);
|
||||
console.log('🔍 Direct response keys:', Object.keys(latestStrategyResponse));
|
||||
|
||||
const transformedStrategy = transformPollingStrategyData(latestStrategyResponse.strategy);
|
||||
const transformedStrategy = transformPollingStrategyData(latestStrategyResponse);
|
||||
|
||||
console.log('🔄 Transformed strategy data:', transformedStrategy);
|
||||
setStrategyData(transformedStrategy);
|
||||
setLoading(false);
|
||||
return;
|
||||
} else {
|
||||
console.log('❌ No strategy data found in response');
|
||||
}
|
||||
} catch (pollingError) {
|
||||
console.log('No latest strategy found in polling system, checking database...', pollingError);
|
||||
|
||||
@@ -71,6 +71,22 @@ export interface CompetitiveAnalysis {
|
||||
|
||||
export interface PerformancePredictions {
|
||||
estimated_roi?: string;
|
||||
success_probability?: string;
|
||||
traffic_growth?: {
|
||||
month_3?: string;
|
||||
month_6?: string;
|
||||
month_12?: string;
|
||||
};
|
||||
engagement_metrics?: {
|
||||
bounce_rate?: string;
|
||||
social_shares?: string;
|
||||
time_on_page?: string;
|
||||
};
|
||||
conversion_predictions?: {
|
||||
content_downloads?: string;
|
||||
email_signups?: string;
|
||||
lead_generation?: string;
|
||||
};
|
||||
key_metrics?: {
|
||||
engagement_rate?: string;
|
||||
conversion_rate?: string;
|
||||
@@ -92,7 +108,7 @@ export interface PerformancePredictions {
|
||||
weakness_mitigation: string;
|
||||
threat_management: string;
|
||||
};
|
||||
// Nested prediction objects from backend
|
||||
// Legacy nested prediction objects (keeping for backward compatibility)
|
||||
traffic_predictions?: {
|
||||
monthly_traffic?: string;
|
||||
growth_rate?: string;
|
||||
@@ -106,7 +122,7 @@ export interface PerformancePredictions {
|
||||
month_3?: string;
|
||||
success_factors?: string[];
|
||||
};
|
||||
conversion_predictions?: {
|
||||
conversion_predictions_legacy?: {
|
||||
conversion_rate?: string;
|
||||
lead_generation?: string;
|
||||
month_6?: string;
|
||||
@@ -122,6 +138,7 @@ export interface PerformancePredictions {
|
||||
|
||||
export interface ImplementationRoadmap {
|
||||
total_duration?: string;
|
||||
timeline?: string;
|
||||
phases?: Array<{
|
||||
phase: string;
|
||||
duration: string;
|
||||
@@ -130,6 +147,10 @@ export interface ImplementationRoadmap {
|
||||
resources: string[]; // Added to match backend
|
||||
swot_focus?: string;
|
||||
}>;
|
||||
milestones?: string[]; // Added to match backend data
|
||||
resource_requirements?: string[]; // Added to match backend data
|
||||
critical_path?: string[]; // Added to match backend data
|
||||
success_metrics?: string[]; // Added to match backend
|
||||
resource_allocation?: {
|
||||
team_members?: string[]; // Changed from team_requirements to match backend
|
||||
team_requirements?: string[]; // Added to match backend data
|
||||
@@ -146,8 +167,7 @@ export interface ImplementationRoadmap {
|
||||
low_priority: string[];
|
||||
};
|
||||
};
|
||||
success_metrics?: string[]; // Added to match backend
|
||||
timeline?: {
|
||||
timeline_object?: {
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
key_milestones?: string[];
|
||||
|
||||
@@ -11,18 +11,175 @@ export const getUserId = (): number => {
|
||||
* Transform polling system strategy data to frontend format
|
||||
*/
|
||||
export const transformPollingStrategyData = (strategyData: any): StrategyData => {
|
||||
return {
|
||||
...strategyData,
|
||||
console.log('🔄 Transforming polling strategy data:', strategyData);
|
||||
console.log('🔄 Strategy data type:', typeof strategyData);
|
||||
console.log('🔄 Strategy data keys:', Object.keys(strategyData || {}));
|
||||
|
||||
// Extract the actual strategy components from the backend structure
|
||||
const strategicInsights = strategyData.strategic_insights;
|
||||
const competitiveAnalysis = strategyData.competitive_analysis;
|
||||
const performancePredictions = strategyData.performance_predictions;
|
||||
const implementationRoadmap = strategyData.implementation_roadmap;
|
||||
const riskAssessment = strategyData.risk_assessment;
|
||||
|
||||
console.log('📊 Extracted components:', {
|
||||
hasStrategicInsights: !!strategicInsights,
|
||||
hasCompetitiveAnalysis: !!competitiveAnalysis,
|
||||
hasPerformancePredictions: !!performancePredictions,
|
||||
hasImplementationRoadmap: !!implementationRoadmap,
|
||||
hasRiskAssessment: !!riskAssessment
|
||||
});
|
||||
|
||||
console.log('🔍 Strategic Insights Raw Data:', strategicInsights);
|
||||
console.log('🔍 Competitive Analysis Raw Data:', competitiveAnalysis);
|
||||
console.log('🔍 Performance Predictions Raw Data:', performancePredictions);
|
||||
console.log('🔍 Implementation Roadmap Raw Data:', implementationRoadmap);
|
||||
console.log('🔍 Risk Assessment Raw Data:', riskAssessment);
|
||||
|
||||
const transformedData = {
|
||||
// Map metadata
|
||||
strategy_metadata: strategyData.metadata || strategyData.strategy_metadata,
|
||||
// Add summary if not present
|
||||
metadata: strategyData.metadata || strategyData.strategy_metadata,
|
||||
|
||||
// Transform Strategic Insights - map the actual backend structure
|
||||
strategic_insights: strategicInsights ? {
|
||||
market_positioning: {
|
||||
positioning_strength: strategicInsights.market_positioning?.positioning_strength || 75,
|
||||
current_position: strategicInsights.market_positioning?.current_position || "Emerging",
|
||||
swot_analysis: {
|
||||
strengths: strategicInsights.market_positioning?.swot_analysis?.strengths || [],
|
||||
opportunities: strategicInsights.market_positioning?.swot_analysis?.opportunities || []
|
||||
}
|
||||
},
|
||||
content_opportunities: strategicInsights.content_opportunities || [],
|
||||
growth_potential: {
|
||||
market_size: strategicInsights.growth_potential?.market_size || "Growing",
|
||||
growth_rate: strategicInsights.growth_potential?.growth_rate || "High",
|
||||
key_drivers: strategicInsights.growth_potential?.key_drivers || [],
|
||||
competitive_advantages: strategicInsights.growth_potential?.competitive_advantages || []
|
||||
},
|
||||
swot_summary: {
|
||||
overall_score: strategicInsights.swot_summary?.overall_score || 75,
|
||||
primary_strengths: strategicInsights.swot_summary?.primary_strengths || [],
|
||||
key_opportunities: strategicInsights.swot_summary?.key_opportunities || []
|
||||
},
|
||||
// Add insights array if it exists in the backend data
|
||||
insights: strategicInsights.insights || []
|
||||
} : undefined,
|
||||
|
||||
// Transform Competitive Analysis - map the actual backend structure
|
||||
competitive_analysis: competitiveAnalysis ? {
|
||||
competitors: competitiveAnalysis.competitors || [],
|
||||
market_gaps: competitiveAnalysis.market_gaps || [],
|
||||
opportunities: competitiveAnalysis.opportunities || [],
|
||||
recommendations: competitiveAnalysis.recommendations || [],
|
||||
competitive_advantages: {
|
||||
primary: competitiveAnalysis.competitive_advantages?.primary || [],
|
||||
sustainable: competitiveAnalysis.competitive_advantages?.sustainable || [],
|
||||
development_areas: competitiveAnalysis.competitive_advantages?.development_areas || []
|
||||
},
|
||||
swot_competitive_insights: {
|
||||
leverage_strengths: competitiveAnalysis.swot_competitive_insights?.leverage_strengths || [],
|
||||
address_weaknesses: competitiveAnalysis.swot_competitive_insights?.address_weaknesses || [],
|
||||
capitalize_opportunities: competitiveAnalysis.swot_competitive_insights?.capitalize_opportunities || [],
|
||||
mitigate_threats: competitiveAnalysis.swot_competitive_insights?.mitigate_threats || []
|
||||
}
|
||||
} : undefined,
|
||||
|
||||
// Transform Performance Predictions - map the actual backend structure
|
||||
performance_predictions: performancePredictions ? {
|
||||
estimated_roi: performancePredictions.estimated_roi || "15-25%",
|
||||
key_metrics: {
|
||||
engagement_rate: performancePredictions.engagement_metrics?.time_on_page || "3-5 minutes",
|
||||
conversion_rate: performancePredictions.conversion_predictions?.lead_generation || "5-8%",
|
||||
reach_growth: performancePredictions.traffic_growth?.month_12 || "100%",
|
||||
brand_awareness: performancePredictions.engagement_metrics?.social_shares || "15-25 per post",
|
||||
market_share: performancePredictions.success_probability || "85%"
|
||||
},
|
||||
timeline_projections: {
|
||||
"month_1": "Initial setup and content creation",
|
||||
"month_3": performancePredictions.traffic_growth?.month_3 || "25% growth",
|
||||
"month_6": performancePredictions.traffic_growth?.month_6 || "50% growth",
|
||||
"month_12": performancePredictions.traffic_growth?.month_12 || "100% growth"
|
||||
},
|
||||
success_factors: {
|
||||
primary: performancePredictions.conversion_predictions ? [
|
||||
`Lead generation: ${performancePredictions.conversion_predictions.lead_generation}`,
|
||||
`Email signups: ${performancePredictions.conversion_predictions.email_signups}`,
|
||||
`Content downloads: ${performancePredictions.conversion_predictions.content_downloads}`
|
||||
] : [],
|
||||
secondary: performancePredictions.engagement_metrics ? [
|
||||
`Time on page: ${performancePredictions.engagement_metrics.time_on_page}`,
|
||||
`Bounce rate: ${performancePredictions.engagement_metrics.bounce_rate}`
|
||||
] : [],
|
||||
risk_mitigation: performancePredictions.success_probability ? [
|
||||
`Success probability: ${performancePredictions.success_probability}`
|
||||
] : []
|
||||
},
|
||||
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 - map the actual backend structure
|
||||
implementation_roadmap: implementationRoadmap ? {
|
||||
timeline: implementationRoadmap.timeline || "12 months",
|
||||
phases: implementationRoadmap.phases || [],
|
||||
milestones: implementationRoadmap.milestones || [],
|
||||
resource_requirements: implementationRoadmap.resource_requirements || [],
|
||||
critical_path: implementationRoadmap.critical_path || [],
|
||||
success_metrics: implementationRoadmap.success_metrics || [],
|
||||
timeline_object: {
|
||||
start_date: "2024-09-01",
|
||||
end_date: "2025-08-31",
|
||||
key_milestones: implementationRoadmap.milestones || []
|
||||
},
|
||||
resource_allocation: {
|
||||
team_members: implementationRoadmap.resource_requirements || [],
|
||||
team_requirements: implementationRoadmap.resource_requirements || [],
|
||||
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: implementationRoadmap.success_metrics?.slice(0, 3) || [],
|
||||
medium_priority: implementationRoadmap.success_metrics?.slice(3, 6) || [],
|
||||
low_priority: implementationRoadmap.success_metrics?.slice(6, 9) || []
|
||||
}
|
||||
}
|
||||
} : undefined,
|
||||
|
||||
// Transform Risk Assessment - map the actual backend structure
|
||||
risk_assessment: riskAssessment ? {
|
||||
overall_risk_level: riskAssessment.overall_risk_level || "Medium",
|
||||
risks: riskAssessment.risks || [],
|
||||
risk_categories: {
|
||||
market_risks: riskAssessment.risk_categories?.market_risks || [],
|
||||
operational_risks: riskAssessment.risk_categories?.operational_risks || [],
|
||||
competitive_risks: riskAssessment.risk_categories?.competitive_risks || [],
|
||||
technical_risks: riskAssessment.risk_categories?.technical_risks || [],
|
||||
financial_risks: riskAssessment.risk_categories?.financial_risks || []
|
||||
}
|
||||
} : undefined,
|
||||
|
||||
// Add summary
|
||||
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%",
|
||||
estimated_roi: performancePredictions?.estimated_roi || "15-25%",
|
||||
implementation_timeline: implementationRoadmap?.timeline || "12 months",
|
||||
risk_level: riskAssessment?.overall_risk_level || "Medium",
|
||||
success_probability: performancePredictions?.success_probability || "85%",
|
||||
next_step: "Review strategy and generate content calendar"
|
||||
}
|
||||
};
|
||||
|
||||
console.log('✅ Transformed Polling Strategy Data:', transformedData);
|
||||
return transformedData;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -115,21 +272,22 @@ export const transformFullStructureData = (latestStrategy: any): StrategyData =>
|
||||
|
||||
// Transform Implementation Roadmap
|
||||
implementation_roadmap: comprehensiveData.implementation_roadmap ? {
|
||||
total_duration: comprehensiveData.implementation_roadmap.total_duration || "6 months",
|
||||
timeline: comprehensiveData.implementation_roadmap.timeline || "6 months",
|
||||
phases: comprehensiveData.implementation_roadmap.phases || [],
|
||||
milestones: comprehensiveData.implementation_roadmap.milestones || [],
|
||||
resource_requirements: comprehensiveData.implementation_roadmap.resource_requirements || [],
|
||||
critical_path: comprehensiveData.implementation_roadmap.critical_path || [],
|
||||
success_metrics: comprehensiveData.implementation_roadmap.success_metrics || [],
|
||||
timeline: comprehensiveData.implementation_roadmap.timeline || {
|
||||
timeline_object: comprehensiveData.implementation_roadmap.timeline_object || {
|
||||
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"],
|
||||
comprehensiveData.implementation_roadmap.resource_allocation?.team_requirements || [],
|
||||
team_requirements: comprehensiveData.implementation_roadmap.resource_allocation?.team_requirements ||
|
||||
comprehensiveData.implementation_roadmap.resource_allocation?.team_members ||
|
||||
["Content Strategist", "SEO Specialist", "Content Writer", "Editor"],
|
||||
comprehensiveData.implementation_roadmap.resource_allocation?.team_members || [],
|
||||
budget_allocation: comprehensiveData.implementation_roadmap.resource_allocation?.budget_allocation || {
|
||||
total_budget: "$60,000",
|
||||
content_creation: "$30,000",
|
||||
@@ -290,7 +448,7 @@ export const transformSwotToComprehensiveStructure = (latestStrategy: any): Stra
|
||||
},
|
||||
// Enhanced Implementation Roadmap with SWOT considerations
|
||||
implementation_roadmap: {
|
||||
total_duration: "12 months",
|
||||
timeline: "12 months",
|
||||
phases: [
|
||||
{
|
||||
phase: "Foundation (Months 1-3)",
|
||||
@@ -329,8 +487,36 @@ export const transformSwotToComprehensiveStructure = (latestStrategy: any): Stra
|
||||
swot_focus: "Strengths and Weaknesses"
|
||||
}
|
||||
],
|
||||
milestones: [
|
||||
"Brand guidelines", "Content calendar", "SWOT action plan",
|
||||
"Content library", "Engaged audience", "Risk management framework",
|
||||
"Market leadership", "Optimized strategy", "Long-term competitive position"
|
||||
],
|
||||
resource_requirements: [
|
||||
"Content Strategist", "SEO Specialist", "Content Writer", "Editor", "Marketing Manager"
|
||||
],
|
||||
critical_path: [
|
||||
"Brand positioning leveraging identified strengths",
|
||||
"Content execution based on competitive advantages",
|
||||
"Market expansion capitalizing on strengths"
|
||||
],
|
||||
success_metrics: [
|
||||
"Brand guidelines", "Content calendar", "SWOT action plan",
|
||||
"Content library", "Engaged audience", "Risk management framework",
|
||||
"Market leadership", "Optimized strategy", "Long-term competitive position"
|
||||
],
|
||||
timeline_object: {
|
||||
start_date: "2024-01-01",
|
||||
end_date: "2024-12-31",
|
||||
key_milestones: [
|
||||
"Brand guidelines", "Content calendar", "SWOT action plan",
|
||||
"Content library", "Engaged audience", "Risk management framework",
|
||||
"Market leadership", "Optimized strategy", "Long-term competitive position"
|
||||
]
|
||||
},
|
||||
resource_allocation: {
|
||||
team_members: ["Content Strategist", "SEO Specialist", "Content Writer", "Editor", "Marketing Manager"],
|
||||
team_requirements: ["Content Strategist", "SEO Specialist", "Content Writer", "Editor", "Marketing Manager"],
|
||||
budget_allocation: {
|
||||
total_budget: "$60,000",
|
||||
content_creation: "$30,000",
|
||||
@@ -343,12 +529,6 @@ export const transformSwotToComprehensiveStructure = (latestStrategy: any): Stra
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user