ALwrity version 0.5.5

This commit is contained in:
ajaysi
2025-08-15 23:02:18 +05:30
parent 6bfa9f0fce
commit 234eefb4bc
27 changed files with 2806 additions and 1417 deletions

View File

@@ -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"');

View File

@@ -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
};
};

View File

@@ -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
};
};

View File

@@ -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
};
};

View File

@@ -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()
};
};

View File

@@ -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);
}
};

View File

@@ -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>
);

View File

@@ -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 }}>

View File

@@ -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>

View File

@@ -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>
)}

View File

@@ -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 }}>

View File

@@ -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);

View File

@@ -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[];

View File

@@ -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

View File

@@ -191,83 +191,83 @@ class ContentPlanningAPI {
// Content Strategy APIs
async createStrategy(strategy: ContentStrategyCreate) {
const response = await apiClient.post(`${this.baseURL}/strategies/`, strategy);
return response.data;
return response.data?.data || response.data;
}
async getStrategies(userId?: number) {
const params = userId ? { user_id: userId } : {};
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies`, { params });
return response.data;
return response.data?.data || response.data;
}
async getStrategy(id: string) {
const response = await apiClient.get(`${this.baseURL}/strategies/${id}`);
return response.data;
return response.data?.data || response.data;
}
async updateStrategy(id: string, updates: ContentStrategyUpdate) {
const response = await apiClient.put(`${this.baseURL}/strategies/${id}`, updates);
return response.data;
return response.data?.data || response.data;
}
async deleteStrategy(id: string) {
const response = await apiClient.delete(`${this.baseURL}/strategies/${id}`);
return response.data;
return response.data?.data || response.data;
}
// Calendar Event APIs
async createEvent(event: CalendarEventCreate) {
const response = await apiClient.post(`${this.baseURL}/calendar-events/`, event);
return response.data;
return response.data?.data || response.data;
}
async getEvents(userId?: number, filters?: any) {
const params = { ...filters };
if (userId) params.user_id = userId;
const response = await apiClient.get(`${this.baseURL}/calendar-events/`, { params });
return response.data;
return response.data?.data || response.data;
}
async getEvent(id: string) {
const response = await apiClient.get(`${this.baseURL}/calendar-events/${id}`);
return response.data;
return response.data?.data || response.data;
}
async updateEvent(id: string, updates: CalendarEventUpdate) {
const response = await apiClient.put(`${this.baseURL}/calendar-events/${id}`, updates);
return response.data;
return response.data?.data || response.data;
}
async deleteEvent(id: string) {
const response = await apiClient.delete(`${this.baseURL}/calendar-events/${id}`);
return response.data;
return response.data?.data || response.data;
}
// Gap Analysis APIs
async createGapAnalysis(analysis: GapAnalysisCreate) {
const response = await apiClient.post(`${this.baseURL}/gap-analysis/`, analysis);
return response.data;
return response.data?.data || response.data;
}
async getGapAnalyses(userId?: number) {
const params = userId ? { user_id: userId } : {};
const response = await apiClient.get(`${this.baseURL}/gap-analysis/`, { params });
return response.data;
return response.data?.data || response.data;
}
async getGapAnalysis(id: string) {
const response = await apiClient.get(`${this.baseURL}/gap-analysis/${id}`);
return response.data;
return response.data?.data || response.data;
}
async updateGapAnalysis(id: string, updates: GapAnalysisUpdate) {
const response = await apiClient.put(`${this.baseURL}/gap-analysis/${id}`, updates);
return response.data;
return response.data?.data || response.data;
}
async deleteGapAnalysis(id: string) {
const response = await apiClient.delete(`${this.baseURL}/gap-analysis/${id}`);
return response.data;
return response.data?.data || response.data;
}
// AI-Powered Gap Analysis - Using AI client for longer timeout
@@ -544,14 +544,14 @@ class ContentPlanningAPI {
const params: any = {};
if (userId) params.user_id = userId;
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies`, { params });
return response.data;
return response.data?.data || response.data;
});
}
async getEnhancedStrategy(strategyId: string): Promise<any> {
return this.handleRequest(async () => {
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies/${strategyId}`);
return response.data;
return response.data?.data || response.data;
});
}
@@ -565,21 +565,21 @@ class ContentPlanningAPI {
async getEnhancedStrategyCompletion(strategyId: string): Promise<any> {
return this.handleRequest(async () => {
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies/${strategyId}/completion`);
return response.data;
return response.data?.data || response.data;
});
}
async getEnhancedStrategyTooltips(): Promise<any> {
return this.handleRequest(async () => {
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies/tooltips`);
return response.data;
return response.data?.data || response.data;
});
}
async getEnhancedStrategyDisclosureSteps(): Promise<any> {
return this.handleRequest(async () => {
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies/disclosure-steps`);
return response.data;
return response.data?.data || response.data;
});
}
@@ -588,7 +588,7 @@ class ContentPlanningAPI {
const params: any = {};
if (userId) params.user_id = userId;
const response = await apiClient.post(`${this.baseURL}/enhanced-strategies/cache/clear`, null, { params });
return response.data;
return response.data?.data || response.data;
}
// Non-streaming autofill refresh method
@@ -596,7 +596,29 @@ class ContentPlanningAPI {
const params: any = { use_ai: useAI, ai_only: aiOnly };
if (userId) params.user_id = userId;
const response = await apiClient.post(`${this.baseURL}/enhanced-strategies/autofill/refresh`, null, { params });
return response.data;
// Debug the API response
console.log('🎯 API refreshAutofill response:', {
responseType: typeof response,
responseKeys: Object.keys(response),
dataType: typeof response.data,
dataKeys: response.data ? Object.keys(response.data) : 'no data',
hasDataProperty: response.data?.hasOwnProperty('data'),
hasFieldsProperty: response.data?.hasOwnProperty('fields'),
dataDataKeys: response.data?.data ? Object.keys(response.data.data) : 'no data.data'
});
// The backend returns ResponseBuilder format: { status, message, data, status_code, timestamp }
// We need to return the actual payload from response.data.data
const result = response.data?.data || response.data;
console.log('🎯 API refreshAutofill returning:', {
resultType: typeof result,
resultKeys: Object.keys(result),
hasFields: result?.hasOwnProperty('fields'),
fieldsCount: result?.fields ? Object.keys(result.fields).length : 0
});
return result;
}
// Enhanced Strategy CRUD Operations
@@ -619,14 +641,14 @@ class ContentPlanningAPI {
return this.handleRequest(async () => {
const params = userId ? { user_id: userId } : {};
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies/onboarding-data`, { params });
return response.data;
return response.data?.data || response.data;
});
}
async getOnboardingIntegration(strategyId: string): Promise<any> {
return this.handleRequest(async () => {
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies/${strategyId}/onboarding-integration`);
return response.data;
return response.data?.data || response.data;
});
}
@@ -692,7 +714,18 @@ class ContentPlanningAPI {
return this.handleRequest(async () => {
const params = userId ? { user_id: userId } : {};
const response = await apiClient.get(`${this.baseURL}/content-strategy/ai-generation/latest-strategy`, { params });
return response.data;
console.log('🔍 getLatestGeneratedStrategy response:', response.data);
console.log('🔍 Response structure:', {
hasData: !!response.data,
dataKeys: Object.keys(response.data || {}),
hasStrategy: !!response.data?.data?.strategy,
strategyKeys: response.data?.data?.strategy ? Object.keys(response.data.data.strategy) : []
});
// Return the strategy data from the nested response structure
const result = response.data?.data?.strategy;
console.log('🔍 Returning result:', result);
console.log('🔍 Result keys:', Object.keys(result || {}));
return result;
});
}