ALwrity version 0.5.5

This commit is contained in:
ajaysi
2025-08-20 20:22:56 +05:30
parent 74e22b421a
commit 3f2f4d7b8c
43 changed files with 2938 additions and 690 deletions

View File

@@ -151,6 +151,7 @@ interface ContentPlanningStore {
// State
strategies: ContentStrategy[];
currentStrategy: ContentStrategy | null;
latestGeneratedStrategy: any | null; // Cache for the latest generated strategy
calendarEvents: CalendarEvent[];
gapAnalyses: ContentGapAnalysis[];
aiRecommendations: AIRecommendation[];
@@ -182,6 +183,7 @@ interface ContentPlanningStore {
updateStrategy: (id: string, updates: Partial<ContentStrategy>) => Promise<void>;
deleteStrategy: (id: string) => Promise<void>;
setCurrentStrategy: (strategy: ContentStrategy | null) => void;
setLatestGeneratedStrategy: (strategy: any | null) => void;
// Calendar actions
createEvent: (event: Omit<CalendarEvent, 'id' | 'created_at' | 'updated_at'>) => Promise<void>;
@@ -269,6 +271,7 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
// Initial state
strategies: [],
currentStrategy: null,
latestGeneratedStrategy: null,
calendarEvents: [],
gapAnalyses: [],
aiRecommendations: [],
@@ -345,6 +348,18 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
},
setCurrentStrategy: (strategy) => set({ currentStrategy: strategy }),
setLatestGeneratedStrategy: (strategy) => {
console.log('🔧 Store: Setting latest generated strategy:', {
strategyId: strategy?.id || strategy?.strategy_id,
strategyName: strategy?.name || strategy?.strategy_name,
hasStrategicInsights: !!strategy?.strategic_insights,
hasCompetitiveAnalysis: !!strategy?.competitive_analysis,
hasPerformancePredictions: !!strategy?.performance_predictions,
hasImplementationRoadmap: !!strategy?.implementation_roadmap,
hasRiskAssessment: !!strategy?.risk_assessment
});
set({ latestGeneratedStrategy: strategy });
},
// Calendar actions
createEvent: async (event) => {
@@ -476,24 +491,17 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
loadStrategies: async () => {
set({ loading: true, error: null });
try {
console.log('🔍 Loading strategies from API...');
const strategies = await contentPlanningApi.getStrategiesSafe();
console.log('🔍 API response for strategies:', strategies);
console.log('🔍 Strategies type:', typeof strategies);
console.log('🔍 Is Array:', Array.isArray(strategies));
if (Array.isArray(strategies)) {
console.log('✅ Strategies loaded successfully (direct array):', strategies.length);
set({ strategies, loading: false });
} else if (strategies && strategies.strategies && Array.isArray(strategies.strategies)) {
console.log('✅ Strategies found in response.strategies:', strategies.strategies.length);
set({ strategies: strategies.strategies, loading: false });
} else {
console.log('❌ No strategies found in response');
set({ strategies: [], loading: false });
}
} catch (error: any) {
console.error('Error loading strategies:', error);
console.error('Error loading strategies:', error);
set({ error: error.message || 'Failed to load strategies', loading: false });
}
},
@@ -521,7 +529,7 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
loadAIInsights: async () => {
set({ loading: true, error: null });
try {
const response = await contentPlanningApi.getAIAnalyticsSafe();
const response = await contentPlanningApi.getAIAnalyticsWithRetry();
// Validate response structure
if (!response || typeof response !== 'object') {
@@ -559,6 +567,21 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
set({ aiInsights: transformedInsights, loading: false });
} catch (error: any) {
console.error('Error loading AI insights:', error);
// Check if it's a rate limit error
if (error.message?.includes('rate limit') || error.message?.includes('429')) {
console.warn('⚠️ Rate limit hit for AI insights, using empty data');
set({ aiInsights: [], loading: false });
return;
}
// Check if it's a network error
if (error.message?.includes('network') || error.message?.includes('connection')) {
console.warn('⚠️ Network error for AI insights, using empty data');
set({ aiInsights: [], loading: false });
return;
}
set({ error: error.message || 'Failed to load AI insights', loading: false, aiInsights: [] });
}
},
@@ -566,7 +589,7 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
loadAIRecommendations: async () => {
set({ loading: true, error: null });
try {
const response = await contentPlanningApi.getAIAnalyticsSafe();
const response = await contentPlanningApi.getAIAnalyticsWithRetry();
// Validate response structure
if (!response || typeof response !== 'object') {
@@ -593,6 +616,21 @@ export const useContentPlanningStore = create<ContentPlanningStore>((set, get) =
set({ aiRecommendations: transformedRecommendations, loading: false });
} catch (error: any) {
console.error('Error loading AI recommendations:', error);
// Check if it's a rate limit error
if (error.message?.includes('rate limit') || error.message?.includes('429')) {
console.warn('⚠️ Rate limit hit for AI recommendations, using empty data');
set({ aiRecommendations: [], loading: false });
return;
}
// Check if it's a network error
if (error.message?.includes('network') || error.message?.includes('connection')) {
console.warn('⚠️ Network error for AI recommendations, using empty data');
set({ aiRecommendations: [], loading: false });
return;
}
set({ error: error.message || 'Failed to load AI recommendations', loading: false, aiRecommendations: [] });
}
},

View File

@@ -4,6 +4,78 @@ import { contentPlanningApi } from '../services/contentPlanningApi';
// Global flag to prevent multiple simultaneous auto-population calls
let isAutoPopulating = false;
// Helper function to detect generic placeholder values
const isGenericPlaceholder = (fieldId: string, value: any): boolean => {
if (!value) return false;
const stringValue = String(value).toLowerCase();
// Common generic placeholder patterns
const genericPatterns = [
'example.com',
'your website',
'your business',
'your industry',
'your target audience',
'your content',
'your goals',
'your objectives',
'your metrics',
'your budget',
'your team',
'your timeline',
'your competitors',
'your market',
'your brand',
'your voice',
'your tone',
'your strategy',
'your plan',
'your approach',
'your focus',
'your niche',
'your sector',
'your domain',
'your company',
'your organization',
'your brand',
'your product',
'your service',
'your offering',
'your solution',
'your value proposition',
'your unique selling point',
'your competitive advantage',
'your market position',
'your customer base',
'your user base',
'your audience',
'your customers',
'your clients',
'your users',
'your visitors',
'your readers',
'your followers',
'your subscribers',
'your leads',
'your prospects',
'your potential customers',
'your target market',
'your target segment',
'your target demographic',
'your target group',
'your target population',
'your target users',
'your target readers',
'your target audience',
'your target customers',
'your target clients'
];
// Check if the value contains any generic placeholder patterns
return genericPatterns.some(pattern => stringValue.includes(pattern));
};
// Enhanced Strategy Types
export interface EnhancedStrategy {
id: string;
@@ -638,7 +710,6 @@ export const useStrategyBuilderStore = create<StrategyBuilderStore>((set, get) =
if (forceRefresh) {
try {
await contentPlanningApi.clearEnhancedCache(1);
console.log('♻️ Cleared enhanced strategy cache for fresh onboarding data');
} catch (e) {
console.warn('Cache clear failed (non-blocking):', e);
}
@@ -646,16 +717,55 @@ export const useStrategyBuilderStore = create<StrategyBuilderStore>((set, get) =
// Fetch onboarding data to auto-populate fields
const response = await contentPlanningApi.getOnboardingData();
console.log('📡 Backend response:', response);
// Enhanced logging for autofill data
console.log('📊 Autofill Response Structure:', {
hasResponse: !!response,
responseKeys: response ? Object.keys(response) : [],
fieldsCount: response?.fields ? Object.keys(response.fields).length : 0,
sourcesCount: response?.sources ? Object.keys(response.sources).length : 0,
inputDataPointsCount: response?.input_data_points ? Object.keys(response.input_data_points).length : 0,
hasMeta: !!response?.meta
});
// Validate response structure
if (!response) {
throw new Error('Invalid response structure from backend');
}
// Extract field values and sources from the new backend format
const fields = response.data?.fields || {};
const sources = response.data?.sources || {};
const inputDataPoints = response.data?.input_data_points || {};
const fields = response.fields || {};
const sources = response.sources || {};
const inputDataPoints = response.input_data_points || {};
console.log('📋 Extracted fields:', fields);
console.log('🔗 Data sources:', sources);
console.log('📝 Input data points:', inputDataPoints);
// Log detailed field information
console.log('🎯 Autofill Field Details:', {
totalFields: Object.keys(fields).length,
fieldIds: Object.keys(fields),
sampleFieldData: Object.keys(fields).slice(0, 3).map(id => ({
id,
hasValue: !!fields[id]?.value,
hasPersonalization: !!fields[id]?.personalization_data,
hasConfidence: !!fields[id]?.confidence_score,
valueType: typeof fields[id]?.value
}))
});
// Validate AI generation success
const meta = response.meta || {};
console.log('🤖 AI Generation Meta:', {
aiUsed: meta.ai_used,
aiOverridesCount: meta.ai_overrides_count,
error: meta.error,
processingTime: meta.processing_time
});
if (meta.ai_used === false || meta.ai_overrides_count === 0) {
console.log('❌ AI generation failed - no real AI values produced');
throw new Error(meta.error || 'AI generation failed to produce strategy fields. Please try again.');
}
console.log('✅ AI generation successful:', Object.keys(fields).length, 'fields');
// Transform the fields object to extract values for formData
const fieldValues: Record<string, any> = {};
@@ -663,61 +773,77 @@ export const useStrategyBuilderStore = create<StrategyBuilderStore>((set, get) =
const personalizationData: Record<string, any> = {};
const confidenceScores: Record<string, number> = {};
// Check if fields is empty and provide fallback
if (Object.keys(fields).length === 0) {
console.log('⚠️ No fields found in onboarding data, using default values');
// Check if fields is empty - no fallback to mock data
if (Object.keys(fields).length === 0) {
console.log(' No fields found in onboarding data - AI generation may have failed');
console.log('🚫 No fallback to mock data - user must retry or provide manual input');
// Set error state instead of mock data
set({
loading: false,
error: 'AI generation failed to produce strategy fields. Please try again or provide manual input.',
autoPopulatedFields: {},
personalizationData: {},
dataSources: {},
inputDataPoints: {}
});
return;
}
// Process actual fields from backend
let processedFields = 0;
let skippedFields = 0;
let fieldsWithPersonalization = 0;
let fieldsWithConfidence = 0;
// Set default values for strategy builder
const defaultFields: Record<string, any> = {
industry: 'Technology',
business_objectives: 'Increase brand awareness and drive sales',
target_metrics: { traffic: 10000, conversion_rate: 2.5 },
content_budget: 5000,
team_size: 3,
content_preferences: ['Blog posts', 'Social media', 'Email marketing'],
preferred_formats: ['Blog posts', 'Whitepapers', 'Videos'],
content_mix: { blog_posts: 40, whitepapers: 20, videos: 15, social_media: 25 }
};
Object.keys(defaultFields).forEach(fieldId => {
fieldValues[fieldId] = defaultFields[fieldId];
autoPopulatedFields[fieldId] = defaultFields[fieldId];
confidenceScores[fieldId] = 0.7; // Medium confidence for defaults
console.log(`✅ Set default value for ${fieldId}:`, defaultFields[fieldId]);
});
} else {
// Process actual fields from backend
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;
autoPopulatedFields[fieldId] = fieldData.value;
// Validate that the value is not a generic placeholder
const value = fieldData.value;
const isGenericPlaceholderValue = isGenericPlaceholder(fieldId, value);
if (isGenericPlaceholderValue) {
console.log(`⚠️ Skipping ${fieldId} - contains generic placeholder value: "${value}"`);
skippedFields++;
return; // Skip this field
}
fieldValues[fieldId] = value;
autoPopulatedFields[fieldId] = value;
processedFields++;
// Extract personalization data if available
if (fieldData.personalization_data) {
personalizationData[fieldId] = fieldData.personalization_data;
console.log(`🎯 Personalization data for ${fieldId}:`, fieldData.personalization_data);
fieldsWithPersonalization++;
}
// Extract confidence score if available
if (fieldData.confidence_score) {
confidenceScores[fieldId] = fieldData.confidence_score;
console.log(`💯 Confidence score for ${fieldId}:`, fieldData.confidence_score);
fieldsWithConfidence++;
}
console.log(`✅ Auto-populated ${fieldId}:`, fieldData.value);
} else {
console.log(`❌ Skipping ${fieldId} - invalid data structure`);
console.log(`❌ Skipping ${fieldId} - invalid data structure:`, fieldData);
skippedFields++;
}
});
}
// Log field processing summary
console.log('📈 Field Processing Summary:', {
totalFields: Object.keys(fields).length,
processedFields,
skippedFields,
fieldsWithPersonalization,
fieldsWithConfidence,
averageConfidence: fieldsWithConfidence > 0
? (Object.values(confidenceScores).filter((score): score is number => typeof score === 'number').reduce((a, b) => a + b, 0) / fieldsWithConfidence).toFixed(1)
: 'N/A'
});
console.log('📝 Final field values:', fieldValues);
console.log('🔄 Final auto-populated fields:', autoPopulatedFields);
console.log('🎯 Personalization data:', personalizationData);
console.log('💯 Confidence scores:', confidenceScores);
console.log('✅ Auto-population completed:', Object.keys(fieldValues).length, 'fields');
set((state) => ({
autoPopulatedFields,
@@ -728,85 +854,100 @@ export const useStrategyBuilderStore = create<StrategyBuilderStore>((set, get) =
formData: { ...state.formData, ...fieldValues }
}));
console.log('✅ Auto-population completed successfully');
} catch (error: any) {
console.error('❌ Auto-population error:', error);
const errorMessage = error.message || 'Failed to auto-populate from onboarding';
set({
error: errorMessage,
loading: false
// Log final state summary
console.log('🎉 Auto-population State Summary:', {
autoPopulatedFieldsCount: Object.keys(autoPopulatedFields).length,
dataSourcesCount: Object.keys(sources).length,
inputDataPointsCount: Object.keys(inputDataPoints).length,
personalizationDataCount: Object.keys(personalizationData).length,
confidenceScoresCount: Object.keys(confidenceScores).length,
formDataUpdated: Object.keys(fieldValues).length,
timestamp: new Date().toISOString()
});
// If it's a rate limit error, set a flag to prevent further attempts
if (errorMessage.includes('Too many requests') || errorMessage.includes('No response from server')) {
set({ autoPopulationBlocked: true });
}
} finally {
set({ loading: false });
isAutoPopulating = false; // Reset global flag
}
},
updateAutoPopulatedField: (fieldId, value, source) => {
set((state) => ({
autoPopulatedFields: { ...state.autoPopulatedFields, [fieldId]: value },
dataSources: { ...state.dataSources, [fieldId]: source }
}));
},
overrideAutoPopulatedField: (fieldId, value) => {
set((state) => ({
formData: { ...state.formData, [fieldId]: value },
autoPopulatedFields: { ...state.autoPopulatedFields, [fieldId]: value }
}));
},
// UI Actions
setLoading: (loading) => set({ loading }),
setError: (error) => set({ error }),
setSaving: (saving) => set({ saving }),
// Completion Tracking
calculateCompletionPercentage: () => {
const formData = get().formData;
const requiredFields = STRATEGIC_INPUT_FIELDS.filter(field => field.required);
const filledRequiredFields = requiredFields.filter(field => {
const value = formData[field.id];
return value && (Array.isArray(value) ? value.length > 0 : true);
});
return requiredFields.length > 0 ? (filledRequiredFields.length / requiredFields.length) * 100 : 0;
},
getCompletionStats: () => {
const formData = get().formData;
const totalFields = STRATEGIC_INPUT_FIELDS.length;
const filledFields = STRATEGIC_INPUT_FIELDS.filter(field => {
const value = formData[field.id];
return value && (Array.isArray(value) ? value.length > 0 : true);
}).length;
const completionPercentage = totalFields > 0 ? (filledFields / totalFields) * 100 : 0;
// Calculate completion by category
const categoryCompletion: Record<string, number> = {};
const categories = Array.from(new Set(STRATEGIC_INPUT_FIELDS.map(field => field.category)));
categories.forEach(category => {
const categoryFields = STRATEGIC_INPUT_FIELDS.filter(field => field.category === category);
const filledCategoryFields = categoryFields.filter(field => {
const value = formData[field.id];
return value && (Array.isArray(value) ? value.length > 0 : true);
}).length;
categoryCompletion[category] = categoryFields.length > 0 ? (filledCategoryFields / categoryFields.length) * 100 : 0;
});
return {
total_fields: totalFields,
filled_fields: filledFields,
completion_percentage: completionPercentage,
category_completion: categoryCompletion
};
}
}));
console.log('✅ Auto-population completed successfully');
// Store the autofill completion time
sessionStorage.setItem('lastAutofillTime', new Date().toISOString());
} catch (error: any) {
console.error('❌ Auto-population error:', error);
const errorMessage = error.message || 'Failed to auto-populate from onboarding';
set({
error: errorMessage,
loading: false
});
// If it's a rate limit error, set a flag to prevent further attempts
if (errorMessage.includes('Too many requests') || errorMessage.includes('No response from server')) {
set({ autoPopulationBlocked: true });
}
} finally {
set({ loading: false });
isAutoPopulating = false; // Reset global flag
}
},
updateAutoPopulatedField: (fieldId: string, value: any, source: string) => {
set((state) => ({
autoPopulatedFields: { ...state.autoPopulatedFields, [fieldId]: value },
dataSources: { ...state.dataSources, [fieldId]: source }
}));
},
overrideAutoPopulatedField: (fieldId: string, value: any) => {
set((state) => ({
formData: { ...state.formData, [fieldId]: value },
autoPopulatedFields: { ...state.autoPopulatedFields, [fieldId]: value }
}));
},
// UI Actions
setLoading: (loading: boolean) => set({ loading }),
setError: (error: string | null) => set({ error }),
setSaving: (saving: boolean) => set({ saving }),
// Completion Tracking
calculateCompletionPercentage: () => {
const formData = get().formData;
const requiredFields = STRATEGIC_INPUT_FIELDS.filter(field => field.required);
const filledRequiredFields = requiredFields.filter(field => {
const value = formData[field.id];
return value && (Array.isArray(value) ? value.length > 0 : true);
});
return requiredFields.length > 0 ? (filledRequiredFields.length / requiredFields.length) * 100 : 0;
},
getCompletionStats: () => {
const formData = get().formData;
const totalFields = STRATEGIC_INPUT_FIELDS.length;
const filledFields = STRATEGIC_INPUT_FIELDS.filter(field => {
const value = formData[field.id];
return value && (Array.isArray(value) ? value.length > 0 : true);
}).length;
const completionPercentage = totalFields > 0 ? (filledFields / totalFields) * 100 : 0;
// Calculate completion by category
const categoryCompletion: Record<string, number> = {};
const categories = Array.from(new Set(STRATEGIC_INPUT_FIELDS.map(field => field.category)));
categories.forEach(category => {
const categoryFields = STRATEGIC_INPUT_FIELDS.filter(field => field.category === category);
const filledCategoryFields = categoryFields.filter(field => {
const value = formData[field.id];
return value && (Array.isArray(value) ? value.length > 0 : true);
}).length;
categoryCompletion[category] = categoryFields.length > 0 ? (filledCategoryFields / categoryFields.length) * 100 : 0;
});
return {
total_fields: totalFields,
filled_fields: filledFields,
completion_percentage: completionPercentage,
category_completion: categoryCompletion
};
}
}));

View File

@@ -171,7 +171,24 @@ export const useStrategyReviewStore = create<ReviewState>()(
// Start review process
startReviewProcess: () => {
set({ reviewProcessStarted: true });
console.log('🔧 Starting review process - resetting all reviews first');
// Reset all reviews when starting a new review process
const { components } = get();
const resetComponents = components.map(comp => ({
...comp,
status: 'not_reviewed' as ReviewStatus,
reviewedAt: undefined,
reviewedBy: undefined,
notes: undefined
}));
set({
reviewProcessStarted: true,
components: resetComponents,
reviewProgress: 0
});
console.log('🔧 Review process started with reset components');
},
// Update review progress
@@ -181,7 +198,6 @@ export const useStrategyReviewStore = create<ReviewState>()(
const totalCount = components.length;
const progress = totalCount > 0 ? (reviewedCount / totalCount) * 100 : 0;
console.log('🔧 Updating progress:', { reviewedCount, totalCount, progress, components: components.map(c => ({ id: c.id, status: c.status })) });
set({ reviewProgress: progress });
},