ALwrity + Wix + Wordpress + GSC + Bug Fixes

This commit is contained in:
ajaysi
2025-10-10 13:08:09 +05:30
parent af4c8afb5b
commit 11f164ae21
80 changed files with 125 additions and 678 deletions

View File

@@ -1428,7 +1428,7 @@
"title": "Personalization", "title": "Personalization",
"description": "Set up personalization features", "description": "Set up personalization features",
"status": "completed", "status": "completed",
"completed_at": "2025-10-09T13:05:12.886271", "completed_at": "2025-10-10T08:44:52.292617",
"data": { "data": {
"corePersona": { "corePersona": {
"analysis_notes": "This persona is generated based on general best practices for professional and engaging content, as no specific website, content, audience, or brand data was provided. The details are inferred to create a functional, albeit generic, persona. For a truly precise and actionable persona, comprehensive input data across all specified categories (website analysis, content insights, audience intelligence, brand voice, technical metrics, competitive analysis, content strategy, research preferences) is essential. The current persona serves as a foundational template that would be significantly refined and customized with actual data.", "analysis_notes": "This persona is generated based on general best practices for professional and engaging content, as no specific website, content, audience, or brand data was provided. The details are inferred to create a functional, albeit generic, persona. For a truly precise and actionable persona, comprehensive input data across all specified categories (website analysis, content insights, audience intelligence, brand voice, technical metrics, competitive analysis, content strategy, research preferences) is essential. The current persona serves as a foundational template that would be significantly refined and customized with actual data.",
@@ -2677,7 +2677,7 @@
], ],
"current_step": 5, "current_step": 5,
"started_at": "2025-09-29T17:22:14.375002", "started_at": "2025-09-29T17:22:14.375002",
"last_updated": "2025-10-09T13:05:12.888068", "last_updated": "2025-10-10T08:44:52.293336",
"is_completed": false, "is_completed": false,
"completed_at": null "completed_at": null
} }

View File

@@ -52,14 +52,14 @@ export const ResearchPollingHandler: React.FC<ResearchPollingHandlerProps> = ({
} else { } else {
polling.stopPolling(); polling.stopPolling();
} }
}, [taskId]); }, [taskId, polling]);
// Cleanup on unmount // Cleanup on unmount
useEffect(() => { useEffect(() => {
return () => { return () => {
polling.stopPolling(); polling.stopPolling();
}; };
}, []); }, [polling]);
console.log('ResearchPollingHandler render:', { console.log('ResearchPollingHandler render:', {
taskId, taskId,

View File

@@ -213,7 +213,7 @@ export const RewriteFeedbackForm: React.FC<RewriteFeedbackFormProps> = ({
onRewriteStarted, onRewriteStarted,
onRewriteTriggered onRewriteTriggered
}) => { }) => {
const [isCollectingFeedback, setIsCollectingFeedback] = useState(false); // Note: isCollectingFeedback state removed as it was unused
// Rewrite Blog Action with HITL // Rewrite Blog Action with HITL
useCopilotActionTyped({ useCopilotActionTyped({
@@ -303,7 +303,6 @@ export const RewriteFeedbackForm: React.FC<RewriteFeedbackFormProps> = ({
if (result.success && result.taskId) { if (result.success && result.taskId) {
onRewriteStarted?.(result.taskId); onRewriteStarted?.(result.taskId);
setIsCollectingFeedback(false);
return { return {
success: true, success: true,

View File

@@ -16,21 +16,13 @@ import {
Card, Card,
CardContent, CardContent,
Chip, Chip,
Divider, Alert
Alert,
IconButton,
Tooltip,
Button
} from '@mui/material'; } from '@mui/material';
import { import {
ContentCopy as CopyIcon,
Check as CheckIcon,
Search as SearchIcon, Search as SearchIcon,
Share as ShareIcon,
Code as CodeIcon, Code as CodeIcon,
Facebook as FacebookIcon, Facebook as FacebookIcon,
Twitter as TwitterIcon, Twitter as TwitterIcon,
LinkedIn as LinkedInIcon,
Google as GoogleIcon Google as GoogleIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
@@ -43,15 +35,6 @@ export const PreviewCard: React.FC<PreviewCardProps> = ({
metadata, metadata,
blogTitle blogTitle
}) => { }) => {
const copyToClipboard = async (text: string, itemId: string) => {
try {
await navigator.clipboard.writeText(text);
// You could add a state to show "Copied!" feedback here
} catch (err) {
console.error('Failed to copy to clipboard:', err);
}
};
const getCurrentDate = () => { const getCurrentDate = () => {
return new Date().toLocaleDateString('en-US', { return new Date().toLocaleDateString('en-US', {
year: 'numeric', year: 'numeric',
@@ -60,13 +43,6 @@ export const PreviewCard: React.FC<PreviewCardProps> = ({
}); });
}; };
const getCurrentTime = () => {
return new Date().toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit'
});
};
return ( return (
<Box> <Box>
<Typography variant="h6" sx={{ mb: 3, display: 'flex', alignItems: 'center', gap: 1 }}> <Typography variant="h6" sx={{ mb: 3, display: 'flex', alignItems: 'center', gap: 1 }}>

View File

@@ -5,11 +5,10 @@
* Integrates with CopilotKit for real-time progress updates and user interactions. * Integrates with CopilotKit for real-time progress updates and user interactions.
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogTitle,
Button, Button,
Chip, Chip,
LinearProgress, LinearProgress,
@@ -23,7 +22,6 @@ import {
Alert, Alert,
Grid, Grid,
Paper, Paper,
Divider,
IconButton, IconButton,
Tooltip Tooltip
} from '@mui/material'; } from '@mui/material';
@@ -33,11 +31,8 @@ import {
Cancel, Cancel,
Warning, Warning,
TrendingUp, TrendingUp,
GpsFixed,
MenuBook,
Search, Search,
BarChart, BarChart,
Lightbulb,
Refresh, Refresh,
Close Close
} from '@mui/icons-material'; } from '@mui/icons-material';
@@ -165,7 +160,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
// Debug logging // Debug logging
console.log('SEOAnalysisModal render:', { isOpen, blogContent: blogContent?.length, researchData: !!researchData }); console.log('SEOAnalysisModal render:', { isOpen, blogContent: blogContent?.length, researchData: !!researchData });
const runSEOAnalysis = async () => { const runSEOAnalysis = useCallback(async () => {
try { try {
setIsAnalyzing(true); setIsAnalyzing(true);
setError(null); setError(null);
@@ -267,7 +262,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
setError(err instanceof Error ? err.message : 'Analysis failed'); setError(err instanceof Error ? err.message : 'Analysis failed');
setIsAnalyzing(false); setIsAnalyzing(false);
} }
}; }, [blogContent, blogTitle, researchData]);
const getScoreColor = (score: number) => { const getScoreColor = (score: number) => {
if (score >= 80) return 'success.main'; if (score >= 80) return 'success.main';
@@ -281,23 +276,6 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
return 'error'; return 'error';
}; };
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'High': return 'error.main';
case 'Medium': return 'warning.main';
case 'Low': return 'success.main';
default: return 'text.secondary';
}
};
const getPriorityIcon = (priority: string) => {
switch (priority) {
case 'High': return <Cancel sx={{ fontSize: 16 }} />;
case 'Medium': return <Warning sx={{ fontSize: 16 }} />;
case 'Low': return <CheckCircle sx={{ fontSize: 16 }} />;
default: return <Warning sx={{ fontSize: 16 }} />;
}
};
// Tooltip content for each metric // Tooltip content for each metric
const getMetricTooltip = (category: string) => { const getMetricTooltip = (category: string) => {
@@ -352,7 +330,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
if (isOpen && !analysisResult) { if (isOpen && !analysisResult) {
runSEOAnalysis(); runSEOAnalysis();
} }
}, [isOpen]); }, [isOpen, analysisResult, runSEOAnalysis]);
return ( return (
<Dialog <Dialog

View File

@@ -20,22 +20,13 @@ import {
Typography, Typography,
Tabs, Tabs,
Tab, Tab,
Paper,
CircularProgress, CircularProgress,
Alert, Alert,
IconButton, IconButton,
Tooltip, Chip
Chip,
Grid,
Card,
CardContent,
Divider,
TextField,
InputAdornment
} from '@mui/material'; } from '@mui/material';
import { import {
Close as CloseIcon, Close as CloseIcon,
ContentCopy as CopyIcon,
Check as CheckIcon, Check as CheckIcon,
Preview as PreviewIcon, Preview as PreviewIcon,
Search as SearchIcon, Search as SearchIcon,

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import { import {
Dialog, Dialog,
DialogTitle, DialogTitle,
@@ -12,15 +12,10 @@ import {
LinearProgress, LinearProgress,
Chip, Chip,
IconButton, IconButton,
Alert, CircularProgress
CircularProgress,
Card
} from '@mui/material'; } from '@mui/material';
import { import {
Close as CloseIcon, Close as CloseIcon,
CheckCircle as CheckCircleIcon,
Error as ErrorIcon,
Refresh as RefreshIcon,
Schedule as ScheduleIcon, Schedule as ScheduleIcon,
TrendingUp as TrendingUpIcon, TrendingUp as TrendingUpIcon,
School as SchoolIcon, School as SchoolIcon,
@@ -31,8 +26,7 @@ import {
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
// Import existing components for reuse // Import existing components for reuse
import DataSourceTransparency from '../DataSourceTransparency'; // Note: DataSourceTransparency and ProgressIndicator are imported but may be used by child components
import ProgressIndicator from '../ProgressIndicator';
// Import panel components // Import panel components
import { import {
@@ -42,57 +36,28 @@ import {
StepResultsPanel, StepResultsPanel,
EducationalPanel, EducationalPanel,
useCalendarGenerationPolling, useCalendarGenerationPolling,
type CalendarGenerationProgress,
type QualityScores type QualityScores
} from './calendarGenerationModalPanels'; } from './calendarGenerationModalPanels';
// Import new StepProgressTracker component // Import new StepProgressTracker component
import StepProgressTracker from './calendarGenerationModalPanels/StepProgressTracker'; import StepProgressTracker from './calendarGenerationModalPanels/StepProgressTracker';
// Import styles // Import styles (only used ones)
import { import {
dialogStyles, dialogStyles,
contentContainerStyles,
progressBarContainerStyles, progressBarContainerStyles,
progressBarStyles, progressBarStyles,
stepProgressBarStyles,
getStepIndicatorStyles, getStepIndicatorStyles,
getStepCardStyles,
stepCircleBaseStyles,
getStepCircleColor,
tabButtonStyles, tabButtonStyles,
activityIndicatorStyles,
qualityScoreContainerStyles,
getQualityScoreBackground,
qualityScoreInnerStyles,
dataSourceCardStyles,
dataSourceIconStyles,
getDataSourceIconColor,
qualityMetricsContainerStyles,
getMetricColor,
stepResultsCardStyles,
stepResultsHeaderStyles,
stepResultsContentStyles,
loadingContainerStyles, loadingContainerStyles,
loadingContentStyles, loadingContentStyles,
animationDurations, animationDurations,
animationEasing, animationEasing,
springConfig,
staggerDelay,
cardStaggerDelay,
fadeInUp,
fadeInLeft, fadeInLeft,
scaleIn,
slideInStaggered,
hoverLift,
hoverScale, hoverScale,
tapScale, tapScale,
pulseAnimation, pulseAnimation,
smallPulseAnimation, progressOverlayStyles
colorPulseAnimation,
progressFillAnimation,
progressOverlayStyles,
stepProgressOverlayStyles
} from './CalendarGenerationModal.styles'; } from './CalendarGenerationModal.styles';
// Types // Types
@@ -262,19 +227,11 @@ const CalendarGenerationModal: React.FC<CalendarGenerationModalProps> = ({
}) => { }) => {
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const [educationalPanelExpanded, setEducationalPanelExpanded] = useState(false); const [educationalPanelExpanded, setEducationalPanelExpanded] = useState(false);
const [expandedSections, setExpandedSections] = useState({
dataSources: true,
progress: true,
educational: false,
messages: true,
stepResults: true
});
// Use polling hook for real backend data only // Use polling hook for real backend data only
const { const {
progress, progress,
isPolling, isPolling,
error,
startPolling, startPolling,
stopPolling, stopPolling,
getStepStatus, getStepStatus,
@@ -309,11 +266,7 @@ const CalendarGenerationModal: React.FC<CalendarGenerationModalProps> = ({
console.log('❌ Calendar generation error:', currentProgress.errors); console.log('❌ Calendar generation error:', currentProgress.errors);
onError(currentProgress.errors[0]?.message || 'Unknown error'); onError(currentProgress.errors[0]?.message || 'Unknown error');
} }
}, [currentProgress?.status, currentProgress?.errors, onError]); }, [currentProgress, onError]);
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
setActiveTab(newValue);
};
const getQualityColor = (score: number) => { const getQualityColor = (score: number) => {
if (score >= 0.9) return 'success'; if (score >= 0.9) return 'success';

View File

@@ -10,10 +10,8 @@ import {
InputLabel, InputLabel,
Select, Select,
MenuItem, MenuItem,
TextField,
Slider, Slider,
FormControlLabel, FormControlLabel,
Checkbox,
Alert, Alert,
IconButton, IconButton,
Tooltip, Tooltip,
@@ -23,12 +21,10 @@ import {
} from '@mui/material'; } from '@mui/material';
import { import {
AutoAwesome as AIIcon, AutoAwesome as AIIcon,
Speed as SpeedIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
TrendingUp as TrendingIcon, TrendingUp as TrendingIcon,
Psychology as PsychologyIcon, Psychology as PsychologyIcon,
Info as InfoIcon, Info as InfoIcon,
Settings as SettingsIcon,
Assessment as AssessmentIcon Assessment as AssessmentIcon
} from '@mui/icons-material'; } from '@mui/icons-material';

View File

@@ -181,8 +181,6 @@ const CalendarConfigurationStep: React.FC<CalendarConfigurationStepProps> = ({
const [userGuidance, setUserGuidance] = useState<UserGuidance | null>(null); const [userGuidance, setUserGuidance] = useState<UserGuidance | null>(null);
const [transparencyIndicators, setTransparencyIndicators] = useState<TransparencyIndicators | null>(null); const [transparencyIndicators, setTransparencyIndicators] = useState<TransparencyIndicators | null>(null);
const [showSmartDefaults, setShowSmartDefaults] = useState(true); const [showSmartDefaults, setShowSmartDefaults] = useState(true);
const [showUserGuidance, setShowUserGuidance] = useState(true);
const [showTransparency, setShowTransparency] = useState(true);
// Generate smart defaults and guidance when strategy context changes // Generate smart defaults and guidance when strategy context changes
useEffect(() => { useEffect(() => {
@@ -245,6 +243,8 @@ const CalendarConfigurationStep: React.FC<CalendarConfigurationStepProps> = ({
// Fix invalid timezone // Fix invalid timezone
onConfigUpdate({ timeZone: 'America/New_York' }); onConfigUpdate({ timeZone: 'America/New_York' });
} }
// timeZones is a const defined later in this component, safe to exclude
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [calendarConfig.timeZone, onConfigUpdate]); }, [calendarConfig.timeZone, onConfigUpdate]);
// Apply smart defaults to configuration // Apply smart defaults to configuration

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useMemo } from 'react'; import React, { useState, useEffect, useRef, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
Box, Box,
@@ -6,60 +6,19 @@ import {
Typography, Typography,
Button, Button,
LinearProgress, LinearProgress,
Alert,
Chip,
IconButton,
Tooltip as MuiTooltip,
Card,
CardContent,
Grid, Grid,
Divider,
CircularProgress,
Badge,
Collapse,
Accordion,
AccordionSummary,
AccordionDetails,
List,
ListItem,
ListItemIcon,
ListItemText,
Dialog, Dialog,
DialogTitle, DialogTitle,
DialogContent, DialogContent,
DialogActions DialogActions
} from '@mui/material'; } from '@mui/material';
import { import {
Business as BusinessIcon,
People as PeopleIcon,
TrendingUp as TrendingUpIcon,
ContentPaste as ContentIcon,
Analytics as AnalyticsIcon,
Help as HelpIcon,
CheckCircle as CheckCircleIcon,
Warning as WarningIcon,
AutoAwesome as AutoAwesomeIcon, AutoAwesome as AutoAwesomeIcon,
Refresh as RefreshIcon, Info as InfoIcon
Save as SaveIcon,
ArrowForward as ArrowForwardIcon,
ArrowBack as ArrowBackIcon,
Assessment as AssessmentIcon,
ExpandMore as ExpandMoreIcon,
Info as InfoIcon,
Visibility as VisibilityIcon,
School as SchoolIcon,
Lightbulb as LightbulbIcon,
Psychology as PsychologyIcon,
Timeline as TimelineIcon,
FiberManualRecord as FiberManualRecordIcon,
Schedule as ScheduleIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { useStrategyBuilderStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/strategyBuilderStore'; import { useStrategyBuilderStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/strategyBuilderStore';
import { useEnhancedStrategyStore } from '../../../stores/enhancedStrategyStore'; import { useEnhancedStrategyStore } from '../../../stores/enhancedStrategyStore';
import StrategicInputField from './ContentStrategyBuilder/StrategicInputField';
import EnhancedTooltip from './ContentStrategyBuilder/EnhancedTooltip'; import EnhancedTooltip from './ContentStrategyBuilder/EnhancedTooltip';
import AIRecommendationsPanel from './AIRecommendationsPanel';
import DataSourceTransparency from './DataSourceTransparency'; import DataSourceTransparency from './DataSourceTransparency';
import StrategyAutofillTransparencyModal from './StrategyAutofillTransparencyModal'; import StrategyAutofillTransparencyModal from './StrategyAutofillTransparencyModal';
import EnterpriseDatapointsModal from './EnterpriseDatapointsModal'; import EnterpriseDatapointsModal from './EnterpriseDatapointsModal';
@@ -79,7 +38,7 @@ import { useStrategyCreation } from './ContentStrategyBuilder/hooks/useStrategyC
import { useCopilotReadable, useCopilotAdditionalInstructions } from "@copilotkit/react-core"; import { useCopilotReadable, useCopilotAdditionalInstructions } from "@copilotkit/react-core";
// Import extracted utilities // Import extracted utilities
import { getCategoryIcon, getCategoryColor, getCategoryName, getCategoryStatus } from './ContentStrategyBuilder/utils/categoryHelpers'; import { getCategoryIcon, getCategoryColor } from './ContentStrategyBuilder/utils/categoryHelpers';
import { getEducationalContent } from './ContentStrategyBuilder/utils/educationalContent'; import { getEducationalContent } from './ContentStrategyBuilder/utils/educationalContent';
import { setupCSSAnimations, cleanupCSSAnimations } from './ContentStrategyBuilder/utils/cssAnimations'; import { setupCSSAnimations, cleanupCSSAnimations } from './ContentStrategyBuilder/utils/cssAnimations';
@@ -115,7 +74,6 @@ const ContentStrategyBuilder: React.FC = () => {
updateFormField, updateFormField,
validateFormField, validateFormField,
validateAllFields, validateAllFields,
resetForm,
autoPopulateFromOnboarding, autoPopulateFromOnboarding,
createStrategy: createEnhancedStrategy, createStrategy: createEnhancedStrategy,
calculateCompletionPercentage, calculateCompletionPercentage,
@@ -128,9 +86,6 @@ const ContentStrategyBuilder: React.FC = () => {
// Enhanced Strategy Store (for AI analysis, progressive disclosure, transparency) // Enhanced Strategy Store (for AI analysis, progressive disclosure, transparency)
const { const {
aiGenerating, aiGenerating,
currentStep,
completedSteps,
disclosureSteps,
transparencyModalOpen, transparencyModalOpen,
transparencyGenerationProgress: storeGenerationProgress, transparencyGenerationProgress: storeGenerationProgress,
currentPhase, currentPhase,
@@ -144,11 +99,6 @@ const ContentStrategyBuilder: React.FC = () => {
addTransparencyMessage, addTransparencyMessage,
clearTransparencyMessages, clearTransparencyMessages,
setTransparencyGenerating: setIsGenerating, setTransparencyGenerating: setIsGenerating,
completeStep,
getNextStep,
getPreviousStep,
setCurrentStep,
canProceedToDisclosureStep: canProceedToStep,
generateAIRecommendations, generateAIRecommendations,
setAIGenerating setAIGenerating
} = useEnhancedStrategyStore(); } = useEnhancedStrategyStore();
@@ -157,7 +107,7 @@ const ContentStrategyBuilder: React.FC = () => {
useCopilotActions(); useCopilotActions();
// Check if this component is currently visible (active tab) // Check if this component is currently visible (active tab)
const [isVisible, setIsVisible] = useState(false); const [, setIsVisible] = useState(false);
useEffect(() => { useEffect(() => {
// Use a small delay to ensure the component is actually rendered // Use a small delay to ensure the component is actually rendered
@@ -171,10 +121,8 @@ const ContentStrategyBuilder: React.FC = () => {
}; };
}, []); }, []);
const [showAIRecommendations, setShowAIRecommendations] = useState(false); const [, setShowAIRecommendations] = useState(false);
const [showDataSourceTransparency, setShowDataSourceTransparency] = useState(false); const [showDataSourceTransparency, setShowDataSourceTransparency] = useState(false);
const [localEducationalContent, setLocalEducationalContent] = useState<any>(null);
const [localGenerationProgress, setLocalGenerationProgress] = useState<number>(0);
const [showAIRecModal, setShowAIRecModal] = useState(false); const [showAIRecModal, setShowAIRecModal] = useState(false);
// Ref to track if we've already set the default category // Ref to track if we've already set the default category
@@ -355,13 +303,9 @@ const ContentStrategyBuilder: React.FC = () => {
const { const {
refreshMessage, refreshMessage,
setRefreshMessage,
refreshProgress, refreshProgress,
setRefreshProgress,
isRefreshing, isRefreshing,
setIsRefreshing,
refreshError, refreshError,
setRefreshError,
handleAIRefresh handleAIRefresh
} = useAIRefresh({ } = useAIRefresh({
setTransparencyModalOpen, setTransparencyModalOpen,
@@ -374,18 +318,16 @@ const ContentStrategyBuilder: React.FC = () => {
setError setError
}); });
// getCompletionStats depends on formData, so we include it to recalculate when data changes
// eslint-disable-next-line react-hooks/exhaustive-deps
const completionStats = useMemo(() => getCompletionStats(), [formData]); const completionStats = useMemo(() => getCompletionStats(), [formData]);
const completionPercentage = useMemo(() => calculateCompletionPercentage(), [formData]);
// Use extracted hooks // Use extracted hooks
const { const {
reviewedCategories, reviewedCategories,
isMarkingReviewed, isMarkingReviewed,
categoryCompletionMessage, categoryCompletionMessage,
handleConfirmCategoryReview, handleConfirmCategoryReview
isCategoryReviewed,
getNextUnreviewedCategory,
setReviewedCategories
} = useCategoryReview({ completionStats, setError, setActiveCategory }); } = useCategoryReview({ completionStats, setError, setActiveCategory });
const { const {

View File

@@ -13,8 +13,6 @@ export const useCopilotActions = () => {
updateFormField, updateFormField,
validateFormField, validateFormField,
setError, setError,
autoPopulatedFields,
dataSources,
calculateCompletionPercentage, calculateCompletionPercentage,
getCompletionStats getCompletionStats
} = useStrategyBuilderStore(); } = useStrategyBuilderStore();
@@ -93,7 +91,7 @@ export const useCopilotActions = () => {
console.log(`📝 Populating field ${fieldId} with value: ${value}`); console.log(`📝 Populating field ${fieldId} with value: ${value}`);
// Call backend API for intelligent field population // Call backend API for intelligent field population
const response = await contentPlanningApi.generateCategoryData( await contentPlanningApi.generateCategoryData(
'individual_field', 'individual_field',
`Populate ${fieldId} with: ${value}. Reasoning: ${reasoning || 'User request'}`, `Populate ${fieldId} with: ${value}. Reasoning: ${reasoning || 'User request'}`,
formData formData
@@ -188,7 +186,7 @@ export const useCopilotActions = () => {
setTransparencyGenerating(false); setTransparencyGenerating(false);
return { success: false, message: error.message || 'Unknown error' }; return { success: false, message: error.message || 'Unknown error' };
} }
}, [formData, updateFormField, setError, calculateCompletionPercentage, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating]); }, [formData, updateFormField, setError, calculateCompletionPercentage, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating, triggerTransparencyFlow]);
// Action 4: Validate field // Action 4: Validate field
const validateStrategyField = useCallback(async ({ fieldId }: any) => { const validateStrategyField = useCallback(async ({ fieldId }: any) => {
@@ -301,11 +299,7 @@ export const useCopilotActions = () => {
// Start transparency flow (same as Refresh & Autofill button) // Start transparency flow (same as Refresh & Autofill button)
const { transparencyInterval } = await triggerTransparencyFlow('autofill', 'Auto-population from onboarding data'); const { transparencyInterval } = await triggerTransparencyFlow('autofill', 'Auto-population from onboarding data');
// Get current form data to see what's already filled // Get empty fields that need to be filled
const currentFilledFields = Object.keys(formData).filter(key => {
const value = formData[key];
return value && typeof value === 'string' && value.trim() !== '';
});
const emptyFields = Object.keys(formData).filter(key => { const emptyFields = Object.keys(formData).filter(key => {
const value = formData[key]; const value = formData[key];
return !value || typeof value !== 'string' || value.trim() === ''; return !value || typeof value !== 'string' || value.trim() === '';
@@ -429,7 +423,7 @@ export const useCopilotActions = () => {
setTransparencyGenerating(false); setTransparencyGenerating(false);
return { success: false, message: error.message || 'Unknown error' }; return { success: false, message: error.message || 'Unknown error' };
} }
}, [formData, updateFormField, calculateCompletionPercentage, setError, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating]); }, [formData, updateFormField, calculateCompletionPercentage, setError, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating, triggerTransparencyFlow]);
// Call useCopilotAction hooks unconditionally - they will handle context availability internally // Call useCopilotAction hooks unconditionally - they will handle context availability internally
// This is the only way to comply with React hooks rules // This is the only way to comply with React hooks rules

View File

@@ -10,8 +10,6 @@ import {
Tooltip, Tooltip,
Alert, Alert,
AlertTitle, AlertTitle,
CircularProgress,
LinearProgress,
Divider, Divider,
Button, Button,
Dialog, Dialog,
@@ -24,13 +22,7 @@ import {
ListItemIcon ListItemIcon
} from '@mui/material'; } from '@mui/material';
import { import {
TrendingUp as TrendingUpIcon,
TrendingDown as TrendingDownIcon,
Assessment as AssessmentIcon, Assessment as AssessmentIcon,
Speed as SpeedIcon,
Visibility as VisibilityIcon,
People as EngagementIcon,
MonetizationOn as MonetizationOnIcon,
Refresh as RefreshIcon, Refresh as RefreshIcon,
AutoAwesome as AutoAwesomeIcon, AutoAwesome as AutoAwesomeIcon,
CheckCircle as CheckCircleIcon, CheckCircle as CheckCircleIcon,
@@ -38,11 +30,10 @@ import {
Error as ErrorIcon, Error as ErrorIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
Lightbulb as LightbulbIcon, Lightbulb as LightbulbIcon,
Timeline as TimelineIcon,
Close as CloseIcon Close as CloseIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion } from 'framer-motion';
import { safeRenderText, safeRenderArray, hasValidData, getFallbackValue } from '../utils/defensiveRendering'; import { safeRenderText } from '../utils/defensiveRendering';
// Import our advanced chart components // Import our advanced chart components
import { import {
@@ -56,9 +47,6 @@ import {
// Import real-time data hook // Import real-time data hook
import { useMockRealTimeData } from '../../../../../hooks/useRealTimeData'; import { useMockRealTimeData } from '../../../../../hooks/useRealTimeData';
// Import API services
import { strategyMonitoringApi } from '../../../../../services/strategyMonitoringApi';
interface EnhancedPerformanceVisualizationProps { interface EnhancedPerformanceVisualizationProps {
strategyId: number; strategyId: number;
strategyData: any; strategyData: any;
@@ -88,7 +76,7 @@ const EnhancedPerformanceVisualization: React.FC<EnhancedPerformanceVisualizatio
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
// Use real-time data hook // Use real-time data hook
const { data: realTimeData, isConnected, error: realTimeError } = useMockRealTimeData(strategyId); const { data: realTimeData, isConnected } = useMockRealTimeData(strategyId);
useEffect(() => { useEffect(() => {
loadQualityAnalysis(); loadQualityAnalysis();

View File

@@ -5,21 +5,16 @@ import {
Typography, Typography,
Chip, Chip,
CircularProgress, CircularProgress,
Alert,
IconButton, IconButton,
Dialog, Dialog,
DialogTitle, DialogTitle,
DialogContent, DialogContent,
DialogActions, DialogActions,
Button, Button,
Grid,
Paper,
LinearProgress,
List, List,
ListItem, ListItem,
ListItemText, ListItemText,
ListItemIcon, ListItemIcon,
Divider,
Card, Card,
CardContent, CardContent,
CardHeader, CardHeader,
@@ -33,14 +28,8 @@ import {
Help as UnknownIcon, Help as UnknownIcon,
Refresh as RefreshIcon, Refresh as RefreshIcon,
Close as CloseIcon, Close as CloseIcon,
TrendingUp as TrendingUpIcon,
TrendingDown as TrendingDownIcon,
Speed as SpeedIcon,
BugReport as BugReportIcon, BugReport as BugReportIcon,
Storage as StorageIcon, Analytics as AnalyticsIcon
Timeline as TimelineIcon,
Analytics as AnalyticsIcon,
NetworkCheck as NetworkCheckIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import MonitoringCharts from './MonitoringCharts'; import MonitoringCharts from './MonitoringCharts';
@@ -95,10 +84,10 @@ const SystemStatusIndicator: React.FC<SystemStatusIndicatorProps> = ({ className
const [statusData, setStatusData] = useState<SystemStatusData | null>(null); const [statusData, setStatusData] = useState<SystemStatusData | null>(null);
const [detailedStats, setDetailedStats] = useState<DetailedStatsData | null>(null); const [detailedStats, setDetailedStats] = useState<DetailedStatsData | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [, setError] = useState<string | null>(null);
const [dashboardOpen, setDashboardOpen] = useState(false); const [dashboardOpen, setDashboardOpen] = useState(false);
const [chartData, setChartData] = useState<any[]>([]); const [chartData, setChartData] = useState<any[]>([]);
const [cachePerf, setCachePerf] = useState<{ hits: number; misses: number; hit_rate: number } | null>(null); const [, setCachePerf] = useState<{ hits: number; misses: number; hit_rate: number } | null>(null);
const fetchStatus = async () => { const fetchStatus = async () => {
setLoading(true); setLoading(true);
@@ -243,10 +232,6 @@ const SystemStatusIndicator: React.FC<SystemStatusIndicatorProps> = ({ className
); );
} }
const total = statusData?.recent_requests ?? 0;
const failed = statusData?.recent_errors ?? 0;
const passed = Math.max(0, total - failed);
return ( return (
<> <>
<Tooltip title={tooltipContent} arrow placement="bottom"> <Tooltip title={tooltipContent} arrow placement="bottom">

View File

@@ -29,10 +29,7 @@ import {
ListItem, ListItem,
ListItemText, ListItemText,
ListItemIcon, ListItemIcon,
Divider, LinearProgress
LinearProgress,
Tooltip,
Badge
} from '@mui/material'; } from '@mui/material';
import { import {
Add as AddIcon, Add as AddIcon,
@@ -42,27 +39,17 @@ import {
Event as EventIcon, Event as EventIcon,
Refresh as RefreshIcon, Refresh as RefreshIcon,
TrendingUp as TrendingIcon, TrendingUp as TrendingIcon,
ContentCopy as RepurposeIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
Schedule as ScheduleIcon, Schedule as ScheduleIcon,
Psychology as PsychologyIcon, Psychology as PsychologyIcon,
Business as BusinessIcon, Business as BusinessIcon,
Group as GroupIcon,
Timeline as TimelineIcon, Timeline as TimelineIcon,
Lightbulb as LightbulbIcon, Lightbulb as LightbulbIcon,
CheckCircle as CheckCircleIcon, CheckCircle as CheckCircleIcon,
Warning as WarningIcon,
Info as InfoIcon,
DataUsage as DataUsageIcon,
Insights as InsightsIcon,
Assessment as AssessmentIcon,
Campaign as CampaignIcon,
Speed as SpeedIcon,
AutoAwesome as AutoAwesomeIcon AutoAwesome as AutoAwesomeIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { useContentPlanningStore } from '../../../stores/contentPlanningStore'; import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
import { contentPlanningApi } from '../../../services/contentPlanningApi';
interface TabPanelProps { interface TabPanelProps {
children?: React.ReactNode; children?: React.ReactNode;
@@ -92,15 +79,10 @@ const CalendarTab: React.FC = () => {
createEvent, createEvent,
updateEvent, updateEvent,
deleteEvent, deleteEvent,
loading,
error, error,
loadCalendarEvents, loadCalendarEvents,
updateCalendarEvents,
// New calendar generation state // New calendar generation state
generatedCalendar, generatedCalendar,
performancePrediction,
contentRepurposing,
aiInsights,
calendarGenerationError, calendarGenerationError,
dataLoading, dataLoading,
calendarGenerationLoading calendarGenerationLoading
@@ -118,28 +100,15 @@ const CalendarTab: React.FC = () => {
status: 'draft' as 'draft' | 'scheduled' | 'published' status: 'draft' as 'draft' | 'scheduled' | 'published'
}); });
// Enhanced state for data transparency
const [userData, setUserData] = useState<any>({
onboardingData: {},
gapAnalysis: {},
strategyData: {},
recommendationsData: [],
performanceData: {},
aiAnalysisResults: []
});
const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : []; const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
useEffect(() => { useEffect(() => {
loadCalendarData(); loadCalendarData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const loadCalendarData = async () => { const loadCalendarData = async () => {
try { try {
// Load comprehensive user data for calendar generation
const comprehensiveData = await contentPlanningApi.getComprehensiveUserData(1); // Pass user ID
setUserData(comprehensiveData.data); // Extract the data from the response
// Load existing calendar events // Load existing calendar events
await loadCalendarEvents(); await loadCalendarEvents();
} catch (error) { } catch (error) {
@@ -211,45 +180,6 @@ const CalendarTab: React.FC = () => {
await loadCalendarData(); await loadCalendarData();
}; };
const handleDataUpdate = (updatedData: any) => {
setUserData((prev: any) => ({ ...prev, ...updatedData }));
};
const handleGenerateCalendar = async (calendarConfig: any) => {
try {
await contentPlanningApi.generateComprehensiveCalendar({
...calendarConfig,
userData
});
} catch (error) {
console.error('Error generating calendar:', error);
}
};
const handleOptimizeContent = async (contentData: any) => {
try {
await contentPlanningApi.optimizeContent(contentData);
} catch (error) {
console.error('Error optimizing content:', error);
}
};
const handlePredictPerformance = async (contentData: any) => {
try {
await contentPlanningApi.predictPerformance(contentData);
} catch (error) {
console.error('Error predicting performance:', error);
}
};
const handleGetTrendingTopics = async () => {
try {
await contentPlanningApi.getTrendingTopics({ user_id: 1, industry: 'technology' });
} catch (error) {
console.error('Error getting trending topics:', error);
}
};
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
switch (status) { switch (status) {
case 'draft': return 'default'; case 'draft': return 'default';

View File

@@ -7,8 +7,7 @@ import {
List, List,
ListItem, ListItem,
ListItemText, ListItemText,
ListItemIcon, ListItemIcon
Chip
} from '@mui/material'; } from '@mui/material';
import { import {
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,

View File

@@ -9,9 +9,6 @@ import {
Button, Button,
CircularProgress CircularProgress
} from '@mui/material'; } from '@mui/material';
import {
PieChart as PieChartIcon
} from '@mui/icons-material';
import { useContentPlanningStore } from '../../../stores/contentPlanningStore'; import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
const ContentPillarsTab: React.FC = () => { const ContentPillarsTab: React.FC = () => {
@@ -21,6 +18,7 @@ const ContentPillarsTab: React.FC = () => {
useEffect(() => { useEffect(() => {
loadContentPillars(); loadContentPillars();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentStrategy]); }, [currentStrategy]);
const loadContentPillars = async () => { const loadContentPillars = async () => {

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from 'react'; import React, { useState, useEffect } from 'react';
import { import {
Box, Box,
Paper, Paper,
@@ -25,25 +25,13 @@ const ContentStrategyTab: React.FC = () => {
const strategies = useContentPlanningStore(state => state.strategies); const strategies = useContentPlanningStore(state => state.strategies);
const currentStrategy = useContentPlanningStore(state => state.currentStrategy); const currentStrategy = useContentPlanningStore(state => state.currentStrategy);
const latestGeneratedStrategy = useContentPlanningStore(state => state.latestGeneratedStrategy); const latestGeneratedStrategy = useContentPlanningStore(state => state.latestGeneratedStrategy);
const aiInsights = useContentPlanningStore(state => state.aiInsights);
const aiRecommendations = useContentPlanningStore(state => state.aiRecommendations);
const loading = useContentPlanningStore(state => state.loading);
const error = useContentPlanningStore(state => state.error); const error = useContentPlanningStore(state => state.error);
const loadStrategies = useContentPlanningStore(state => state.loadStrategies); const loadStrategies = useContentPlanningStore(state => state.loadStrategies);
const loadAIInsights = useContentPlanningStore(state => state.loadAIInsights); const loadAIInsights = useContentPlanningStore(state => state.loadAIInsights);
const loadAIRecommendations = useContentPlanningStore(state => state.loadAIRecommendations); const loadAIRecommendations = useContentPlanningStore(state => state.loadAIRecommendations);
const setLatestGeneratedStrategy = useContentPlanningStore(state => state.setLatestGeneratedStrategy); const setLatestGeneratedStrategy = useContentPlanningStore(state => state.setLatestGeneratedStrategy);
const [strategyForm, setStrategyForm] = useState({
name: '',
description: '',
industry: '',
target_audience: '',
content_pillars: []
});
// Real data states // Real data states
const [strategicIntelligence, setStrategicIntelligence] = useState<any>(null);
const [strategyData, setStrategyData] = useState<StrategyData | null>(null); const [strategyData, setStrategyData] = useState<StrategyData | null>(null);
const [strategyDataLoading, setStrategyDataLoading] = useState(false); const [strategyDataLoading, setStrategyDataLoading] = useState(false);
const [strategyDataError, setStrategyDataError] = useState<string | null>(null); const [strategyDataError, setStrategyDataError] = useState<string | null>(null);
@@ -65,6 +53,7 @@ const ContentStrategyTab: React.FC = () => {
// Load data on component mount // Load data on component mount
useEffect(() => { useEffect(() => {
loadInitialData(); loadInitialData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
// Check if coming from strategy builder // Check if coming from strategy builder
@@ -92,7 +81,7 @@ const ContentStrategyTab: React.FC = () => {
console.log('🧹 Clearing latest generated strategy cache (navigating away from strategy builder)'); console.log('🧹 Clearing latest generated strategy cache (navigating away from strategy builder)');
// Note: We don't clear the cache here as it might be needed for the current session // Note: We don't clear the cache here as it might be needed for the current session
} }
}, [location.state]); }, [location.state, latestGeneratedStrategy]);
// Track strategy status changes for debugging (with debounce) // Track strategy status changes for debugging (with debounce)
useEffect(() => { useEffect(() => {
@@ -136,6 +125,7 @@ const ContentStrategyTab: React.FC = () => {
setShowOnboarding(true); setShowOnboarding(true);
} }
// If strategiesArray.length === 0 and !hasCheckedStrategy, do nothing (wait for data to load) // If strategiesArray.length === 0 and !hasCheckedStrategy, do nothing (wait for data to load)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [strategies, loadStrategies, isFromStrategyBuilder]); }, [strategies, loadStrategies, isFromStrategyBuilder]);
const loadStrategyData = async () => { const loadStrategyData = async () => {
@@ -390,9 +380,6 @@ const ContentStrategyTab: React.FC = () => {
} }
if (strategiesArray.length > 0) { if (strategiesArray.length > 0) {
// Find the most recent strategy
const latestStrategy = strategiesArray[0]; // Assuming strategies are sorted by date
// For now, we'll assume strategies are active if they exist // For now, we'll assume strategies are active if they exist
// In a real implementation, you would check a status field from the database // In a real implementation, you would check a status field from the database
setStrategyStatus('active'); setStrategyStatus('active');

View File

@@ -1,10 +1,9 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { import {
Box, Box,
Typography, Typography,
Tabs, Tabs,
Tab, Tab
Button
} from '@mui/material'; } from '@mui/material';
import { import {
AutoAwesome as AutoAwesomeIcon, AutoAwesome as AutoAwesomeIcon,
@@ -20,7 +19,6 @@ import { apiClient } from '../../../api/client';
// Import hooks and services // Import hooks and services
import { useStrategyCalendarContext } from '../../../contexts/StrategyCalendarContext'; import { useStrategyCalendarContext } from '../../../contexts/StrategyCalendarContext';
import { contentPlanningApi } from '../../../services/contentPlanningApi';
// Import types // Import types
import { type CalendarConfig } from '../components/CalendarWizardSteps/types'; import { type CalendarConfig } from '../components/CalendarWizardSteps/types';
@@ -51,11 +49,10 @@ const CreateTab: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [currentCalendarConfig, setCurrentCalendarConfig] = useState<CalendarConfig | null>(null); const [currentCalendarConfig, setCurrentCalendarConfig] = useState<CalendarConfig | null>(null);
const [sessionId, setSessionId] = useState<string>(''); const [sessionId, setSessionId] = useState<string>('');
const [isStartingGeneration, setIsStartingGeneration] = useState(false);
const location = useLocation(); const location = useLocation();
const { state: { strategyContext }, isFromStrategyActivation } = useStrategyCalendarContext(); const { state: { strategyContext }, isFromStrategyActivation } = useStrategyCalendarContext();
const [userData, setUserData] = useState<any>({}); const [userData] = useState<any>({});
// Handle navigation from strategy activation // Handle navigation from strategy activation
useEffect(() => { useEffect(() => {
@@ -74,7 +71,7 @@ const CreateTab: React.FC = () => {
console.log('🎯 CreateTab: Switching to Calendar Wizard tab (index 1)'); console.log('🎯 CreateTab: Switching to Calendar Wizard tab (index 1)');
setTabValue(1); // Switch to Calendar Wizard tab setTabValue(1); // Switch to Calendar Wizard tab
} }
}, [isFromStrategyActivation, strategyContext?.activationStatus]); }, [isFromStrategyActivation, strategyContext?.activationStatus, location.state]);
// Also check on mount for immediate navigation state // Also check on mount for immediate navigation state
useEffect(() => { useEffect(() => {
@@ -109,9 +106,6 @@ const CreateTab: React.FC = () => {
setCurrentCalendarConfig(calendarConfig); setCurrentCalendarConfig(calendarConfig);
setIsModalOpen(true); setIsModalOpen(true);
// Set loading state to prevent multiple clicks
setIsStartingGeneration(true);
// Transform calendarConfig to match backend CalendarGenerationRequest format // Transform calendarConfig to match backend CalendarGenerationRequest format
const requestData = { const requestData = {
user_id: 1, // Default user ID user_id: 1, // Default user ID
@@ -126,20 +120,19 @@ const CreateTab: React.FC = () => {
// Call the new start endpoint to get session ID with retry logic // Call the new start endpoint to get session ID with retry logic
let startResponse; let startResponse;
let retryCount = 0;
const maxRetries = 3; const maxRetries = 3;
while (retryCount < maxRetries) { for (let retryCount = 0; retryCount < maxRetries; retryCount++) {
try { try {
const response = await apiClient.post('/api/content-planning/calendar-generation/start', requestData); const response = await apiClient.post('/api/content-planning/calendar-generation/start', requestData);
startResponse = { ok: true, data: response.data }; startResponse = { ok: true, data: response.data };
break; // Success, exit retry loop break; // Success, exit retry loop
} catch (error: any) { } catch (error: any) {
console.warn(`⚠️ Attempt ${retryCount + 1} failed with error:`, error); console.warn(`⚠️ Attempt ${retryCount + 1} failed with error:`, error);
retryCount++; if (retryCount < maxRetries - 1) {
if (retryCount < maxRetries) {
// Wait before retry (exponential backoff) // Wait before retry (exponential backoff)
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount)); const delay = 1000 * (retryCount + 1);
await new Promise(resolve => setTimeout(resolve, delay));
} else { } else {
startResponse = { ok: false, data: null }; startResponse = { ok: false, data: null };
} }
@@ -175,8 +168,7 @@ const CreateTab: React.FC = () => {
setCurrentCalendarConfig(null); setCurrentCalendarConfig(null);
setSessionId(''); setSessionId('');
} finally { } finally {
// Clear loading state // Cleanup complete
setIsStartingGeneration(false);
} }
}, [userData, strategyContext]); }, [userData, strategyContext]);

View File

@@ -3,8 +3,7 @@ import {
Box, Box,
Tabs, Tabs,
Tab, Tab,
Typography, Typography
Alert
} from '@mui/material'; } from '@mui/material';
import { import {
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,

View File

@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
import { import {
Box, Box,
Grid, Grid,
Paper,
Typography, Typography,
Card, Card,
CardContent, CardContent,
@@ -16,10 +15,6 @@ import {
TableRow, TableRow,
Button Button
} from '@mui/material'; } from '@mui/material';
import {
Search as SearchIcon
} from '@mui/icons-material';
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
import { contentPlanningApi } from '../../../services/contentPlanningApi'; import { contentPlanningApi } from '../../../services/contentPlanningApi';
const KeywordResearchTab: React.FC = () => { const KeywordResearchTab: React.FC = () => {

View File

@@ -49,6 +49,7 @@ const RefineAnalysisTab: React.FC = () => {
useEffect(() => { useEffect(() => {
loadGapAnalysisData(); loadGapAnalysisData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const loadGapAnalysisData = async () => { const loadGapAnalysisData = async () => {

View File

@@ -87,16 +87,16 @@ function diffMarkup(oldText: string, newText: string): string {
out += a[i]; out += a[i];
i++; j++; i++; j++;
} else if (dp[i + 1][j] >= dp[i][j + 1]) { } else if (dp[i + 1][j] >= dp[i][j + 1]) {
out += `<s class=\"fbw-del\">${escapeHtml(a[i])}</s>`; out += `<s class="fbw-del">${escapeHtml(a[i])}</s>`;
i++; i++;
} else { } else {
out += `<em class=\"fbw-add\">${escapeHtml(b[j])}</em>`; out += `<em class="fbw-add">${escapeHtml(b[j])}</em>`;
j++; j++;
} }
} }
while (i < n) { out += `<s class=\"fbw-del\">${escapeHtml(a[i++])}</s>`; } while (i < n) { out += `<s class="fbw-del">${escapeHtml(a[i++])}</s>`; }
while (j < m) { out += `<em class=\"fbw-add\">${escapeHtml(b[j++])}</em>`; } while (j < m) { out += `<em class="fbw-add">${escapeHtml(b[j++])}</em>`; }
if (oldText.length > MAX || newText.length > MAX) out += '<span class=\"fbw-more\"> …</span>'; if (oldText.length > MAX || newText.length > MAX) out += '<span class="fbw-more"> …</span>';
return out; return out;
} }
@@ -148,7 +148,7 @@ const FacebookWriterContent: React.FC<FacebookWriterProps> = ({ className = '' }
const [livePreviewHtml, setLivePreviewHtml] = React.useState<string>(''); const [livePreviewHtml, setLivePreviewHtml] = React.useState<string>('');
const [isPreviewing, setIsPreviewing] = React.useState<boolean>(false); const [isPreviewing, setIsPreviewing] = React.useState<boolean>(false);
const [pendingEdit, setPendingEdit] = React.useState<{ src: string; target: string } | null>(null); const [pendingEdit, setPendingEdit] = React.useState<{ src: string; target: string } | null>(null);
const [historyVersion, setHistoryVersion] = React.useState<number>(0); const [, setHistoryVersion] = React.useState<number>(0);
const [adVariations, setAdVariations] = React.useState<{ const [adVariations, setAdVariations] = React.useState<{
headline_variations: string[]; headline_variations: string[];
primary_text_variations: string[]; primary_text_variations: string[];

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { useCopilotAction } from '@copilotkit/react-core'; import { useCopilotAction } from '@copilotkit/react-core';
import { linkedInWriterApi, LinkedInPostRequest, GroundingLevel } from '../../services/linkedInWriterApi'; import { linkedInWriterApi, GroundingLevel } from '../../services/linkedInWriterApi';
import { import {
mapPostType, mapPostType,
mapTone, mapTone,
@@ -8,7 +8,6 @@ import {
mapSearchEngine, mapSearchEngine,
readPrefs readPrefs
} from './utils/linkedInWriterUtils'; } from './utils/linkedInWriterUtils';
import { PostHITL, ArticleHITL, CarouselHITL, VideoScriptHITL, CommentResponseHITL } from './components';
import { apiClient } from '../../api/client'; import { apiClient } from '../../api/client';
const useCopilotActionTyped = useCopilotAction as any; const useCopilotActionTyped = useCopilotAction as any;
@@ -125,17 +124,6 @@ const RegisterLinkedInActions: React.FC = () => {
] ]
}})); }}));
// If refining existing content, use the current draft as context
let existingContent = '';
if (args?.refine_existing) {
// Get current draft from the page context
const textarea = document.querySelector('textarea') as HTMLTextAreaElement;
const currentDraft = textarea?.value || '';
if (currentDraft) {
existingContent = `\n\nREFINE THIS EXISTING CONTENT:\n${currentDraft}`;
}
}
// Start detailed progress tracking // Start detailed progress tracking
window.dispatchEvent(new CustomEvent('linkedinwriter:progressStep', { window.dispatchEvent(new CustomEvent('linkedinwriter:progressStep', {
detail: { detail: {
@@ -750,7 +738,7 @@ const RegisterLinkedInActions: React.FC = () => {
{ name: 'improvement_type', type: 'string', required: false } { name: 'improvement_type', type: 'string', required: false }
], ],
handler: async (args: any) => { handler: async (args: any) => {
const { recommendation, current_content, improvement_type } = args; const { recommendation } = args;
// Analyze the recommendation and provide specific improvement guidance // Analyze the recommendation and provide specific improvement guidance
let improvementGuidance = ''; let improvementGuidance = '';

View File

@@ -57,7 +57,6 @@ const RegisterLinkedInEditActions: React.FC = () => {
], ],
handler: async (args: any) => { handler: async (args: any) => {
const content = args?.content || ''; const content = args?.content || '';
const industry = args?.industry || 'Technology';
// Placeholder for hashtag addition // Placeholder for hashtag addition
const hashtags = '#ProfessionalDevelopment #Networking #IndustryInsights #CareerGrowth'; const hashtags = '#ProfessionalDevelopment #Networking #IndustryInsights #CareerGrowth';

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo } from 'react';
import { usePlatformPersonaContext } from '../../shared/PersonaContext/PlatformPersonaProvider'; import { usePlatformPersonaContext } from '../../shared/PersonaContext/PlatformPersonaProvider';
import { apiClient } from '../../../api/client'; import { apiClient } from '../../../api/client';

View File

@@ -29,8 +29,6 @@ interface ContentEditorProps {
topic?: string; topic?: string;
} }
export { ContentEditor };
const ContentEditor: React.FC<ContentEditorProps> = ({ const ContentEditor: React.FC<ContentEditorProps> = ({
isPreviewing, isPreviewing,
pendingEdit, pendingEdit,
@@ -62,6 +60,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
const ctaCooldownRef = useRef<number | null>(null); // 15s cooldown after dismissing CTA const ctaCooldownRef = useRef<number | null>(null); // 15s cooldown after dismissing CTA
useEffect(() => { useEffect(() => {
if (DEBUG_WA) console.log('🎯 [ContentEditor] waSuggestion changed:', waSuggestion); if (DEBUG_WA) console.log('🎯 [ContentEditor] waSuggestion changed:', waSuggestion);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [waSuggestion]); }, [waSuggestion]);
const waTimerRef = useRef<NodeJS.Timeout | null>(null); const waTimerRef = useRef<NodeJS.Timeout | null>(null);
const hasTriggeredOnceRef = useRef<boolean>(false); const hasTriggeredOnceRef = useRef<boolean>(false);
@@ -228,8 +227,6 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
lastWords: words.slice(-5).join(' ') lastWords: words.slice(-5).join(' ')
}); });
const now = Date.now();
const last = lastSuggestMetaRef.current;
const textHash = getStableContextHash(uptoCaret); const textHash = getStableContextHash(uptoCaret);
// After first auto-trigger, stop auto-calling API. Show CTA instead. // After first auto-trigger, stop auto-calling API. Show CTA instead.
@@ -271,7 +268,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
let userError = "Failed to get writing suggestion"; let userError = "Failed to get writing suggestion";
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) { if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
userError = "API quota exceeded. Please try again later or upgrade your plan."; userError = "API quota exceeded. Please try again later or upgrade your plan.";
const match = msg.match(/\"retryDelay\"\s*:\s*\"(\d+)s\"/); const match = msg.match(/"retryDelay"\s*:\s*"(\d+)s"/);
const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000; const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000;
coolDownUntilRef.current = Date.now() + retryMs; coolDownUntilRef.current = Date.now() + retryMs;
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs); console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
@@ -316,7 +313,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
let userError = "Failed to get writing suggestion"; let userError = "Failed to get writing suggestion";
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) { if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
userError = "API quota exceeded. Please try again later or upgrade your plan."; userError = "API quota exceeded. Please try again later or upgrade your plan.";
const match = msg.match(/\"retryDelay\"\s*:\s*\"(\d+)s\"/); const match = msg.match(/"retryDelay"\s*:\s*"(\d+)s"/);
const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000; const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000;
coolDownUntilRef.current = Date.now() + retryMs; coolDownUntilRef.current = Date.now() + retryMs;
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs); console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
@@ -421,4 +418,6 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
<CitationHoverHandler researchSources={researchSources || []} /> <CitationHoverHandler researchSources={researchSources || []} />
</div> </div>
); );
}; };
export { ContentEditor };

View File

@@ -3,29 +3,12 @@ import {
Box, Box,
Container, Container,
Typography, Typography,
Card,
CardContent, CardContent,
useTheme,
useMediaQuery,
Chip, Chip,
Tooltip, Tooltip,
Paper, Paper
Modal,
Button,
IconButton,
Divider,
LinearProgress,
Avatar,
Stack
} from '@mui/material'; } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import {
Close as CloseIcon,
Settings as SettingsIcon,
CheckCircle as CheckIcon,
RadioButtonUnchecked as UncheckedIcon,
TrendingUp as TrendingUpIcon
} from '@mui/icons-material';
import GeneratePillarChips from './components/GeneratePillarChips'; import GeneratePillarChips from './components/GeneratePillarChips';
import PublishPillarChips from './components/PublishPillarChips'; import PublishPillarChips from './components/PublishPillarChips';
import AnalyzePillarChips from './components/AnalyzePillarChips'; import AnalyzePillarChips from './components/AnalyzePillarChips';
@@ -484,14 +467,11 @@ const PillarCard: React.FC<{
// Main Content Lifecycle Pillars Component // Main Content Lifecycle Pillars Component
const ContentLifecyclePillars: React.FC = () => { const ContentLifecyclePillars: React.FC = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [onboardingModalOpen, setOnboardingModalOpen] = useState(false); const [onboardingModalOpen, setOnboardingModalOpen] = useState(false);
// Workflow store hooks // Workflow store hooks
const { const {
currentWorkflow, currentWorkflow,
workflowProgress,
isLoading: workflowLoading, isLoading: workflowLoading,
startWorkflow, startWorkflow,
} = useWorkflowStore(); } = useWorkflowStore();

View File

@@ -23,7 +23,7 @@ import CompactSidebar from './components/CompactSidebar';
// Shared types and utilities // Shared types and utilities
import { Tool } from '../shared/types'; import { Tool } from '../shared/types';
import { getFilteredCategories, getToolsForCategory } from '../shared/utils'; import { getToolsForCategory } from '../shared/utils';
// Zustand stores // Zustand stores
import { useDashboardStore } from '../../stores/dashboardStore'; import { useDashboardStore } from '../../stores/dashboardStore';

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Box, Chip, useTheme } from '@mui/material'; import { Box, Chip } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {

View File

@@ -3,7 +3,6 @@ import {
Box, Box,
Paper, Paper,
Typography, Typography,
Chip,
IconButton, IconButton,
Tooltip, Tooltip,
Divider, Divider,
@@ -14,8 +13,6 @@ import {
Filter, Filter,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
Activity,
Zap,
Star Star
} from 'lucide-react'; } from 'lucide-react';
@@ -78,26 +75,16 @@ const CompactSidebar: React.FC<CompactSidebarProps> = ({
// State for search expansion on hover // State for search expansion on hover
const [isSearchExpanded, setIsSearchExpanded] = useState(false); const [isSearchExpanded, setIsSearchExpanded] = useState(false);
// State for sidebar hover expansion // State for sidebar hover expansion
const [isSidebarHovered, setIsSidebarHovered] = useState(false); const [, setIsSidebarHovered] = useState(false);
// State for favorites expansion on hover // State for favorites expansion on hover
const [isFavoritesExpanded, setIsFavoritesExpanded] = useState(false); const [isFavoritesExpanded, setIsFavoritesExpanded] = useState(false);
// Track original collapsed state for hover behavior // Track original collapsed state for hover behavior
const [wasOriginallyCollapsed, setWasOriginallyCollapsed] = useState(false); const [wasOriginallyCollapsed, setWasOriginallyCollapsed] = useState(false);
const [isAnimating, setIsAnimating] = useState(false); const [isAnimating, setIsAnimating] = useState(false);
const [rippleIndex, setRippleIndex] = useState(-1); const [, setRippleIndex] = useState(-1);
const [shouldAutoExpand, setShouldAutoExpand] = useState(false); const [shouldAutoExpand, setShouldAutoExpand] = useState(false);
const [userHasInteracted, setUserHasInteracted] = useState(false); const [userHasInteracted, setUserHasInteracted] = useState(false);
// Calculate total tools count
const totalTools = Object.values(toolCategories).reduce((sum, category) => {
if ('tools' in category) {
return sum + category.tools.length;
} else if ('subCategories' in category) {
return sum + Object.values(category.subCategories).reduce((subSum, subCat) => subSum + subCat.tools.length, 0);
}
return sum;
}, 0);
// Ripple effect for chips // Ripple effect for chips
const startRippleEffect = useCallback(() => { const startRippleEffect = useCallback(() => {
const categoryEntries = Object.entries(toolCategories).slice(0, 5); const categoryEntries = Object.entries(toolCategories).slice(0, 5);

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Box, Chip, useTheme } from '@mui/material'; import { Box, Chip } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React from 'react';
import { import {
Box, Box,
Typography, Typography,
@@ -10,7 +10,6 @@ import {
IconButton, IconButton,
Avatar, Avatar,
Stack, Stack,
LinearProgress,
CircularProgress, CircularProgress,
Card, Card,
CardContent CardContent
@@ -57,13 +56,10 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
navigationState, navigationState,
completeTask, completeTask,
skipTask, skipTask,
moveToNextTask,
isLoading, isLoading,
isWorkflowComplete isWorkflowComplete
} = useWorkflowStore(); } = useWorkflowStore();
const [selectedTask, setSelectedTask] = useState<TodayTask | null>(null);
// Prefer live workflow tasks (to reflect updated statuses), fallback to props // Prefer live workflow tasks (to reflect updated statuses), fallback to props
const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0 const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0
? currentWorkflow.tasks ? currentWorkflow.tasks
@@ -100,12 +96,6 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
} }
}; };
const handleStartWorkflow = async () => {
if (currentWorkflow) {
await moveToNextTask();
}
};
const handleNextPillar = async () => { const handleNextPillar = async () => {
// Close current modal // Close current modal
onClose(); onClose();
@@ -158,9 +148,6 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
task.status === 'completed' || task.status === 'skipped' task.status === 'completed' || task.status === 'skipped'
); );
// Check if this is the Plan pillar
const isPlanPillar = pillarId === 'plan';
// Define pillar order for navigation // Define pillar order for navigation
const pillarOrder = ['plan', 'generate', 'publish', 'analyze', 'engage', 'remarket']; const pillarOrder = ['plan', 'generate', 'publish', 'analyze', 'engage', 'remarket'];
const currentPillarIndex = pillarOrder.indexOf(pillarId); const currentPillarIndex = pillarOrder.indexOf(pillarId);

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useEffect } from 'react';
import { import {
Box, Box,
Card, Card,
@@ -7,10 +7,6 @@ import {
IconButton, IconButton,
Button, Button,
Typography, Typography,
Stepper,
Step,
StepLabel,
StepConnector,
Fade, Fade,
LinearProgress, LinearProgress,
} from '@mui/material'; } from '@mui/material';
@@ -25,7 +21,6 @@ import {
Key, Key,
ContentPasteRounded, ContentPasteRounded,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { styled } from '@mui/material/styles';
interface ApiKeyCarouselProps { interface ApiKeyCarouselProps {
providers: Array<{ providers: Array<{
@@ -40,40 +35,19 @@ interface ApiKeyCarouselProps {
link: string; link: string;
free: boolean; free: boolean;
recommended: boolean; recommended: boolean;
benefits: string[]; benefits: string[];
}>; }>;
currentProvider: number; currentProvider: number;
setCurrentProvider: (index: number) => void; setCurrentProvider: (index: number) => void;
onProviderFocus: (provider: any) => void; onProviderFocus: (provider: any) => void;
} }
const CustomStepConnector = styled(StepConnector)(({ theme }) => ({
'&.MuiStepConnector-alternativeLabel': {
top: 10,
left: 'calc(-50% + 16px)',
right: 'calc(50% + 16px)',
},
'& .MuiStepConnector-line': {
height: 3,
border: 0,
background: 'linear-gradient(90deg, #E2E8F0 0%, #CBD5E1 100%)',
borderRadius: 2,
},
'&.MuiStepConnector-active .MuiStepConnector-line': {
background: 'linear-gradient(90deg, #3B82F6 0%, #1D4ED8 100%)',
},
'&.MuiStepConnector-completed .MuiStepConnector-line': {
background: 'linear-gradient(90deg, #10B981 0%, #059669 100%)',
},
}));
const ApiKeyCarousel: React.FC<ApiKeyCarouselProps> = ({ const ApiKeyCarousel: React.FC<ApiKeyCarouselProps> = ({
providers, providers,
currentProvider, currentProvider,
setCurrentProvider, setCurrentProvider,
onProviderFocus, onProviderFocus,
}) => { }) => {
const [autoProgress, setAutoProgress] = useState(false);
const provider = providers[currentProvider]; const provider = providers[currentProvider];
const getAccentColor = (name: string) => { const getAccentColor = (name: string) => {
@@ -117,14 +91,6 @@ const ApiKeyCarousel: React.FC<ApiKeyCarouselProps> = ({
} }
}; };
const getStepIcon = (index: number) => {
const stepProvider = providers[index];
if (stepProvider.status === 'valid') {
return <CheckCircle sx={{ color: 'success.main' }} />;
}
return <Key sx={{ color: stepProvider === provider ? 'primary.main' : 'text.disabled' }} />;
};
return ( return (
<Box sx={{ width: '100%', maxWidth: 600, mx: 'auto' }}> <Box sx={{ width: '100%', maxWidth: 600, mx: 'auto' }}>
{/* Progress Stepper - Hidden as requested */} {/* Progress Stepper - Hidden as requested */}

View File

@@ -10,13 +10,10 @@ import {
ListItemText, ListItemText,
Chip, Chip,
Divider, Divider,
Alert,
} from '@mui/material'; } from '@mui/material';
import { import {
CheckCircle, CheckCircle,
Star, Star,
Security,
Speed,
TrendingUp, TrendingUp,
Insights, Insights,
Search, Search,

View File

@@ -1,12 +1,10 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { useAuth } from '@clerk/clerk-react';
import { getApiKeysForOnboarding, getStep1ApiKeysFromProgress, saveApiKey } from '../../../../api/onboarding'; import { getApiKeysForOnboarding, getStep1ApiKeysFromProgress, saveApiKey } from '../../../../api/onboarding';
import { getKeyStatus, formatErrorMessage } from '../../common/onboardingUtils'; import { getKeyStatus, formatErrorMessage } from '../../common/onboardingUtils';
import { Provider } from './ProviderCard'; import { Provider } from './ProviderCard';
import { apiClient } from '../../../../api/client'; import { apiClient } from '../../../../api/client';
export const useApiKeyStep = (onContinue: (stepData?: any) => void) => { export const useApiKeyStep = (onContinue: (stepData?: any) => void) => {
const { getToken } = useAuth();
const [geminiKey, setGeminiKey] = useState(''); const [geminiKey, setGeminiKey] = useState('');
const [exaKey, setExaKey] = useState(''); const [exaKey, setExaKey] = useState('');
const [copilotkitKey, setCopilotkitKey] = useState(''); const [copilotkitKey, setCopilotkitKey] = useState('');

View File

@@ -52,7 +52,7 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
const [analysisStep, setAnalysisStep] = useState(''); const [analysisStep, setAnalysisStep] = useState('');
const [competitors, setCompetitors] = useState<Competitor[]>([]); const [competitors, setCompetitors] = useState<Competitor[]>([]);
const [socialMediaAccounts, setSocialMediaAccounts] = useState<any>({}); const [socialMediaAccounts, setSocialMediaAccounts] = useState<any>({});
const [socialMediaCitations, setSocialMediaCitations] = useState<any[]>([]); const [, setSocialMediaCitations] = useState<any[]>([]);
const [researchSummary, setResearchSummary] = useState<ResearchSummary | null>(null); const [researchSummary, setResearchSummary] = useState<ResearchSummary | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [showProgressModal, setShowProgressModal] = useState(false); const [showProgressModal, setShowProgressModal] = useState(false);
@@ -260,6 +260,7 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
} finally { } finally {
setIsAnalyzingSitemap(false); setIsAnalyzingSitemap(false);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userUrl, competitors, industryContext, isAnalyzingSitemap]); }, [userUrl, competitors, industryContext, isAnalyzingSitemap]);
// Initialize: Check cache first, then run analysis if needed // Initialize: Check cache first, then run analysis if needed
@@ -313,36 +314,6 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
}; };
}, [competitors, researchSummary, sitemapAnalysis, userUrl, industryContext]); }, [competitors, researchSummary, sitemapAnalysis, userUrl, industryContext]);
const handleContinue = async () => {
// Save research preferences to backend before continuing
try {
const researchData = getResearchData();
// Extract research preferences for saving (use defaults if not available)
const researchPreferences = {
research_depth: 'Comprehensive',
content_types: ['blog_posts', 'social_media'],
auto_research: true,
factual_content: true
};
// Save research preferences to backend
await aiApiClient.post('/api/ai-research/configure-preferences', {
research_depth: researchPreferences.research_depth,
content_types: researchPreferences.content_types,
auto_research: researchPreferences.auto_research,
factual_content: researchPreferences.factual_content
});
console.log('Research preferences saved to backend');
} catch (error) {
console.error('Error saving research preferences:', error);
// Continue anyway - don't block user progress for save errors
}
// Continue with wizard navigation
onContinue(getResearchData());
};
// Expose data collection function to parent (only when onDataReady changes) // Expose data collection function to parent (only when onDataReady changes)
useEffect(() => { useEffect(() => {

View File

@@ -15,22 +15,13 @@ import {
ListItem, ListItem,
ListItemIcon, ListItemIcon,
ListItemText, ListItemText,
Alert, Alert
LinearProgress
} from '@mui/material'; } from '@mui/material';
import { import {
Search as SearchIcon, Search as SearchIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
TrendingUp as TrendingIcon,
Speed as SpeedIcon,
Security as SecurityIcon,
CheckCircle as CheckIcon, CheckCircle as CheckIcon,
Schedule as ScheduleIcon, Insights as InsightsIcon
Rocket as RocketIcon,
DataUsage as DataIcon,
Compare as CompareIcon,
Insights as InsightsIcon,
Assessment as AssessmentIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
export const ComingSoonSection: React.FC = () => { export const ComingSoonSection: React.FC = () => {

View File

@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useAuth } from '@clerk/clerk-react';
import { import {
Box, Box,
Fade, Fade,
@@ -17,19 +16,7 @@ import {
// Platform Icons // Platform Icons
Web as WordPressIcon, Web as WordPressIcon,
Web as WixIcon, Web as WixIcon,
Google as GoogleIcon, Google as GoogleIcon
// Status Icons
CheckCircle as CheckIcon,
Error as ErrorIcon,
Info as InfoIcon,
Launch as LaunchIcon,
Security as SecurityIcon,
Verified as VerifiedIcon,
Schedule as ScheduleIcon,
TrendingUp as TrendingUpIcon,
Email as EmailIcon,
Business as BusinessIcon,
Notifications as NotificationsIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
// Import refactored components // Import refactored components
@@ -59,12 +46,11 @@ interface IntegrationPlatform {
} }
const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateHeaderContent }) => { const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateHeaderContent }) => {
const { getToken } = useAuth();
const [email, setEmail] = useState<string>(''); const [email, setEmail] = useState<string>('');
// Use custom hooks // Use custom hooks
const { gscSites, connectedPlatforms, setConnectedPlatforms, setGscSites, handleGSCConnect } = useGSCConnection(); const { gscSites, connectedPlatforms, setConnectedPlatforms, handleGSCConnect } = useGSCConnection();
const { isLoading, showToast, setShowToast, toastMessage, setToastMessage, handleConnect } = usePlatformConnections(); const { isLoading, showToast, setShowToast, toastMessage, handleConnect } = usePlatformConnections();
// Initialize integrations data // Initialize integrations data
const [integrations] = useState<IntegrationPlatform[]>([ const [integrations] = useState<IntegrationPlatform[]>([
@@ -211,6 +197,7 @@ const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateH
// Remove query parameters from URL // Remove query parameters from URL
window.history.replaceState({}, document.title, window.location.pathname); window.history.replaceState({}, document.title, window.location.pathname);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
// Get user email from Clerk // Get user email from Clerk
@@ -255,6 +242,7 @@ const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateH
const userEmail = getUserEmail(); const userEmail = getUserEmail();
setEmail(userEmail); setEmail(userEmail);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const handlePlatformConnect = async (platformId: string) => { const handlePlatformConnect = async (platformId: string) => {

View File

@@ -15,22 +15,12 @@ import {
ListItem, ListItem,
ListItemIcon, ListItemIcon,
ListItemText, ListItemText,
Divider, Alert
Alert,
LinearProgress
} from '@mui/material'; } from '@mui/material';
import { import {
AutoAwesome as AutoAwesomeIcon,
ContentPaste as ContentIcon,
Psychology as PsychologyIcon, Psychology as PsychologyIcon,
TrendingUp as TrendingIcon,
Security as SecurityIcon,
Speed as SpeedIcon,
CheckCircle as CheckIcon, CheckCircle as CheckIcon,
Schedule as ScheduleIcon,
Rocket as RocketIcon,
DataUsage as DataIcon, DataUsage as DataIcon,
Tune as TuneIcon,
SmartToy as SmartToyIcon SmartToy as SmartToyIcon
} from '@mui/icons-material'; } from '@mui/icons-material';

View File

@@ -5,10 +5,8 @@
import React from 'react'; import React from 'react';
import { import {
Box,
Typography, Typography,
Card, Card,
CardContent,
LinearProgress, LinearProgress,
Stepper, Stepper,
Step, Step,

View File

@@ -17,26 +17,18 @@ import {
Alert, Alert,
Button, Button,
Slide, Slide,
Zoom, Zoom
Divider
} from '@mui/material'; } from '@mui/material';
import { useTheme, alpha } from '@mui/material/styles'; import { useTheme, alpha } from '@mui/material/styles';
import { import {
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
CheckCircle as CheckIcon, CheckCircle as CheckIcon,
Info as InfoIcon,
Psychology as PsychologyIcon, Psychology as PsychologyIcon,
TrendingUp as TrendingUpIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
Business as BusinessIcon, Business as BusinessIcon,
AutoAwesome as AutoAwesomeIcon, AutoAwesome as AutoAwesomeIcon,
Star as StarIcon, Star as StarIcon,
Warning as WarningIcon, Warning as WarningIcon
Language as LanguageIcon,
Web as WebIcon,
Palette as PaletteIcon,
Speed as SpeedIcon,
Group as GroupIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
/** /**

View File

@@ -110,7 +110,6 @@ const Wizard: React.FC<WizardProps> = ({ onComplete }) => {
// Use refs to avoid dependency cycles // Use refs to avoid dependency cycles
const stepDataRef = useRef(stepData); const stepDataRef = useRef(stepData);
const competitorDataCollectorRef = useRef(competitorDataCollector); const competitorDataCollectorRef = useRef(competitorDataCollector);
const personaStepRef = useRef<{ handleContinue: () => void } | null>(null);
// Keep refs in sync with state // Keep refs in sync with state
useEffect(() => { useEffect(() => {

View File

@@ -10,10 +10,9 @@ import {
Tooltip Tooltip
} from '@mui/material'; } from '@mui/material';
import { import {
Google as GoogleIcon,
Refresh as RefreshIcon Refresh as RefreshIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { gscAPI, type GSCSite } from '../../../api/gsc'; import { type GSCSite } from '../../../api/gsc';
interface GSCPlatformCardProps { interface GSCPlatformCardProps {
platform: { platform: {

View File

@@ -17,7 +17,6 @@ import {
} from '@mui/material'; } from '@mui/material';
import { import {
Web as WixIcon, Web as WixIcon,
Add as AddIcon,
CheckCircle as CheckCircleIcon, CheckCircle as CheckCircleIcon,
Error as ErrorIcon, Error as ErrorIcon,
Refresh as RefreshIcon Refresh as RefreshIcon

View File

@@ -15,7 +15,6 @@ import {
DialogTitle, DialogTitle,
DialogContent, DialogContent,
DialogActions, DialogActions,
Alert,
CircularProgress, CircularProgress,
IconButton, IconButton,
Tooltip, Tooltip,
@@ -27,12 +26,10 @@ import {
} from '@mui/material'; } from '@mui/material';
import { import {
Web as WordPressIcon, Web as WordPressIcon,
Add as AddIcon,
Delete as DeleteIcon, Delete as DeleteIcon,
CheckCircle as CheckCircleIcon, CheckCircle as CheckCircleIcon,
Error as ErrorIcon, Error as ErrorIcon,
Refresh as RefreshIcon, Refresh as RefreshIcon
Launch as LaunchIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { useWordPressOAuth } from '../../../hooks/useWordPressOAuth'; import { useWordPressOAuth } from '../../../hooks/useWordPressOAuth';

View File

@@ -14,8 +14,7 @@ const SEOCopilotContext: React.FC<{ children: React.ReactNode }> = ({ children }
isLoading, isLoading,
isAnalyzing, isAnalyzing,
isGenerating, isGenerating,
error, error
loadPersonalizationData
} = useSEOCopilotStore(); } = useSEOCopilotStore();
const hasLoadedPersonalization = useRef(false); const hasLoadedPersonalization = useRef(false);

View File

@@ -1,7 +1,7 @@
// SEO CopilotKit Test Component // SEO CopilotKit Test Component
// Simple test to verify CopilotKit sidebar functionality // Simple test to verify CopilotKit sidebar functionality
import React, { useEffect, useState } from 'react'; import React, { useState } from 'react';
import { Box, Button, Typography, Paper, Alert } from '@mui/material'; import { Box, Button, Typography, Paper, Alert } from '@mui/material';
import { useCopilotAction } from '@copilotkit/react-core'; import { useCopilotAction } from '@copilotkit/react-core';

View File

@@ -6,7 +6,6 @@ import {
Typography, Typography,
Alert, Alert,
Skeleton, Skeleton,
useTheme,
Chip, Chip,
Button Button
} from '@mui/material'; } from '@mui/material';
@@ -31,8 +30,6 @@ import { userDataAPI } from '../../api/userData';
// SEO Dashboard component // SEO Dashboard component
const SEODashboard: React.FC = () => { const SEODashboard: React.FC = () => {
const theme = useTheme();
// Clerk authentication hooks // Clerk authentication hooks
const { isSignedIn, isLoaded } = useAuth(); const { isSignedIn, isLoaded } = useAuth();
const { user } = useUser(); const { user } = useUser();

View File

@@ -60,6 +60,7 @@ const GSCLoginButton: React.FC<GSCLoginButtonProps> = ({ onStatusChange }) => {
// Check GSC connection status on component mount // Check GSC connection status on component mount
useEffect(() => { useEffect(() => {
checkGSCStatus(); checkGSCStatus();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const checkGSCStatus = async () => { const checkGSCStatus = async () => {

View File

@@ -31,7 +31,6 @@ import {
} from './seoUtils'; } from './seoUtils';
// Components // Components
import CategoryCard from './CategoryCard';
import CriticalIssueCard from './CriticalIssueCard'; import CriticalIssueCard from './CriticalIssueCard';
import AnalysisTabs from './AnalysisTabs'; import AnalysisTabs from './AnalysisTabs';
import IssueDetailsDialog from './IssueDetailsDialog'; import IssueDetailsDialog from './IssueDetailsDialog';

View File

@@ -80,8 +80,6 @@ const CitationHoverHandler: React.FC<CitationHoverHandlerProps> = ({ researchSou
modal.style.padding = '18px 20px'; modal.style.padding = '18px 20px';
const title = (src.title || 'Untitled').replace(/</g, '&lt;'); const title = (src.title || 'Untitled').replace(/</g, '&lt;');
const url = (src.url || '').replace(/</g, '&lt;');
const sourceType = src.source_type ? String(src.source_type).replace('_', ' ') : '';
modal.innerHTML = modal.innerHTML =
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">' + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">' +
@@ -163,8 +161,6 @@ const CitationHoverHandler: React.FC<CitationHoverHandlerProps> = ({ researchSou
tip.style.backdropFilter = 'blur(5px)'; tip.style.backdropFilter = 'blur(5px)';
const title = (src.title || 'Untitled').replace(/</g, '&lt;'); const title = (src.title || 'Untitled').replace(/</g, '&lt;');
const url = (src.url || '').replace(/</g, '&lt;');
const sourceType = src.source_type ? String(src.source_type).replace('_', ' ') : '';
tip.innerHTML = tip.innerHTML =
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">' + '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">' +

View File

@@ -85,7 +85,7 @@ const ContentDisplayArea: React.FC<ContentDisplayAreaProps> = ({
if (draft !== localDraft) { if (draft !== localDraft) {
setLocalDraft(draft); setLocalDraft(draft);
} }
}, [draft]); }, [draft, localDraft]);
// Cleanup debounced saver // Cleanup debounced saver
useEffect(() => { useEffect(() => {

View File

@@ -356,6 +356,7 @@ const ContentPreviewHeaderWithModals: React.FC<ContentPreviewHeaderProps> = (pro
window.removeEventListener('showCitationsModal', handleShowCitationsModal as EventListener); window.removeEventListener('showCitationsModal', handleShowCitationsModal as EventListener);
window.removeEventListener('showSearchQueriesModal', handleShowSearchQueriesModal as EventListener); window.removeEventListener('showSearchQueriesModal', handleShowSearchQueriesModal as EventListener);
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
return ( return (

View File

@@ -36,7 +36,6 @@ const MainContentPreviewHeader: React.FC<MainContentPreviewHeaderProps> = ({
onAssistantToggle, onAssistantToggle,
topic topic
}) => { }) => {
const formatPercent = (v?: number) => typeof v === 'number' ? `${Math.round(v * 100)}%` : '—';
const getChipColor = (v?: number) => { const getChipColor = (v?: number) => {
if (typeof v !== 'number') return '#6b7280'; if (typeof v !== 'number') return '#6b7280';
if (v >= 0.8) return '#10b981'; if (v >= 0.8) return '#10b981';

View File

@@ -97,6 +97,7 @@ const PersonaChip: React.FC<PersonaChipProps> = ({
useEffect(() => { useEffect(() => {
fetchPersonaData(); fetchPersonaData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [platform, userId]); }, [platform, userId]);
const handleSavePersona = async (data: PersonaData, saveToDatabase: boolean) => { const handleSavePersona = async (data: PersonaData, saveToDatabase: boolean) => {

View File

@@ -24,7 +24,7 @@ const QuickEditToolbar: React.FC<QuickEditToolbarProps> = ({ draft, isPreviewing
const lines = draft.split('\n'); const lines = draft.split('\n');
if (lines.length > 0) { if (lines.length > 0) {
const first = lines[0].trim(); const first = lines[0].trim();
lines[0] = first.replace(/^(.*?)([\.!?])?$/, '👉 $1$2'); lines[0] = first.replace(/^(.*?)([.!?])?$/, '👉 $1$2');
} }
const target = lines.join('\n'); const target = lines.join('\n');
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } })); window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
@@ -62,7 +62,7 @@ const QuickEditToolbar: React.FC<QuickEditToolbarProps> = ({ draft, isPreviewing
</button> </button>
<button <button
onClick={() => { onClick={() => {
const target = `[Professionalized]` + '\n\n' + draft; const target = '[Professionalized]\n\n' + draft;
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } })); window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
}} }}
style={{ padding: '6px 10px', border: '1px solid #ddd', borderRadius: 6, background: '#fff', cursor: 'pointer' }} style={{ padding: '6px 10px', border: '1px solid #ddd', borderRadius: 6, background: '#fff', cursor: 'pointer' }}

View File

@@ -2,10 +2,6 @@ import React, { useState, useRef, useEffect } from 'react';
import { hallucinationDetectorService, HallucinationDetectionResponse } from '../../services/hallucinationDetectorService'; import { hallucinationDetectorService, HallucinationDetectionResponse } from '../../services/hallucinationDetectorService';
import FactCheckResults from '../LinkedInWriter/components/FactCheckResults'; import FactCheckResults from '../LinkedInWriter/components/FactCheckResults';
interface TextSelectionHandlerProps {
contentRef: React.RefObject<HTMLDivElement>;
}
const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) => { const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) => {
const [selectionMenu, setSelectionMenu] = useState<{ x: number; y: number; text: string } | null>(null); const [selectionMenu, setSelectionMenu] = useState<{ x: number; y: number; text: string } | null>(null);
const [factCheckResults, setFactCheckResults] = useState<HallucinationDetectionResponse | null>(null); const [factCheckResults, setFactCheckResults] = useState<HallucinationDetectionResponse | null>(null);
@@ -112,7 +108,7 @@ const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) =>
switch (editType) { switch (editType) {
case 'tighten': case 'tighten':
// Add hook emoji to the beginning // Add hook emoji to the beginning
editedText = selectedText.replace(/^(.*?)([\.!?])?$/, '👉 $1$2'); editedText = selectedText.replace(/^(.*?)([.!?])?$/, '👉 $1$2');
break; break;
case 'add-cta': case 'add-cta':
// Add call-to-action // Add call-to-action

View File

@@ -13,12 +13,11 @@ import {
Select, Select,
MenuItem, MenuItem,
Chip, Chip,
Divider, Divider
Link
} from '@mui/material'; } from '@mui/material';
import { apiClient } from '../../api/client'; import { apiClient } from '../../api/client';
import { createClient, OAuthStrategy } from '@wix/sdk'; import { createClient, OAuthStrategy } from '@wix/sdk';
import { categories as blogCategoriesModule, tags as blogTagsModule, posts as blogPostsModule, draftPosts as blogDraftPostsModule } from '@wix/blog'; import { categories as blogCategoriesModule, tags as blogTagsModule } from '@wix/blog';
interface WixConnectionStatus { interface WixConnectionStatus {
connected: boolean; connected: boolean;
@@ -49,7 +48,6 @@ const WixTestPage: React.FC = () => {
const [publishing, setPublishing] = useState(false); const [publishing, setPublishing] = useState(false);
const [categories, setCategories] = useState<BlogCategories | null>(null); const [categories, setCategories] = useState<BlogCategories | null>(null);
const [tags, setTags] = useState<BlogTags | null>(null); const [tags, setTags] = useState<BlogTags | null>(null);
const [authUrl, setAuthUrl] = useState<string>('');
// Blog post form state // Blog post form state
const [blogTitle, setBlogTitle] = useState('Test Blog Post from ALwrity'); const [blogTitle, setBlogTitle] = useState('Test Blog Post from ALwrity');
@@ -120,7 +118,6 @@ This integration opens up new possibilities for content creators who want to lev
// Use sessionStorage to ensure data is scoped to this tab/session // Use sessionStorage to ensure data is scoped to this tab/session
sessionStorage.setItem('wix_oauth_data', JSON.stringify(oauthData)); sessionStorage.setItem('wix_oauth_data', JSON.stringify(oauthData));
const { authUrl } = await wixClient.auth.getAuthUrl(oauthData); const { authUrl } = await wixClient.auth.getAuthUrl(oauthData);
setAuthUrl(authUrl);
window.location.href = authUrl; window.location.href = authUrl;
} catch (error) { } catch (error) {
console.error('Failed to start Wix OAuth flow:', error); console.error('Failed to start Wix OAuth flow:', error);

View File

@@ -12,11 +12,7 @@ import {
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { import {
DollarSign, DollarSign,
TrendingUp,
RefreshCw, RefreshCw,
AlertTriangle,
CheckCircle,
XCircle,
Info Info
} from 'lucide-react'; } from 'lucide-react';
@@ -28,7 +24,6 @@ import {
formatCurrency, formatCurrency,
formatNumber, formatNumber,
formatPercentage, formatPercentage,
getUsageStatusColor,
getUsageStatusIcon, getUsageStatusIcon,
calculateUsagePercentage calculateUsagePercentage
} from '../../services/billingService'; } from '../../services/billingService';
@@ -53,7 +48,6 @@ const BillingOverview: React.FC<BillingOverviewProps> = ({
const getStatusChip = () => { const getStatusChip = () => {
const status = usageStats.usage_status; const status = usageStats.usage_status;
const color = getUsageStatusColor(status);
const icon = getUsageStatusIcon(status); const icon = getUsageStatusIcon(status);
let chipColor: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default'; let chipColor: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default';

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect } from 'react';
import { import {
Card, Card,
CardContent, CardContent,
@@ -26,9 +26,6 @@ import { billingService } from '../../services/billingService';
import { monitoringService } from '../../services/monitoringService'; import { monitoringService } from '../../services/monitoringService';
import { onApiEvent } from '../../utils/apiEvents'; import { onApiEvent } from '../../utils/apiEvents';
// Components
import ComprehensiveAPIBreakdown from './ComprehensiveAPIBreakdown';
interface CompactBillingDashboardProps { interface CompactBillingDashboardProps {
userId?: string; userId?: string;
} }
@@ -38,7 +35,6 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null); const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
const fetchData = async () => { const fetchData = async () => {
try { try {
@@ -52,7 +48,6 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
setDashboardData(billingData); setDashboardData(billingData);
setSystemHealth(healthData); setSystemHealth(healthData);
setLastUpdated(new Date());
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch data'); setError(err instanceof Error ? err.message : 'Failed to fetch data');
} finally { } finally {
@@ -62,6 +57,7 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userId]); }, [userId]);
// Event-driven refresh // Event-driven refresh
@@ -77,11 +73,11 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
fetchData(); fetchData();
}); });
return unsubscribe; return unsubscribe;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const formatCurrency = (amount: number) => `$${amount.toFixed(4)}`; const formatCurrency = (amount: number) => `$${amount.toFixed(4)}`;
const formatNumber = (num: number) => num.toLocaleString(); const formatNumber = (num: number) => num.toLocaleString();
const formatPercentage = (num: number) => `${num.toFixed(1)}%`;
if (loading && !dashboardData) { if (loading && !dashboardData) {
return ( return (
@@ -118,9 +114,7 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
if (!dashboardData) return null; if (!dashboardData) return null;
const { current_usage, trends, limits, alerts } = dashboardData; const { current_usage, limits, alerts } = dashboardData;
const activeProviders = Object.entries(current_usage.provider_breakdown)
.filter(([_, data]) => data.cost > 0);
return ( return (
<motion.div <motion.div

View File

@@ -10,26 +10,15 @@ import {
Accordion, Accordion,
AccordionSummary, AccordionSummary,
AccordionDetails, AccordionDetails,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
} from '@mui/material'; } from '@mui/material';
import { ExpandMore } from '@mui/icons-material'; import { ExpandMore } from '@mui/icons-material';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { import {
Info, Info,
DollarSign,
Activity,
Zap,
Search, Search,
Image, Image,
Code, Code,
Database, Database,
Globe,
FileText, FileText,
BarChart3 BarChart3
} from 'lucide-react'; } from 'lucide-react';

View File

@@ -8,11 +8,8 @@ import {
Chip, Chip,
} from '@mui/material'; } from '@mui/material';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts'; import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';
import { import {
DollarSign,
TrendingUp,
BarChart3,
PieChart as PieChartIcon PieChart as PieChartIcon
} from 'lucide-react'; } from 'lucide-react';

View File

@@ -3,13 +3,9 @@ import {
Box, Box,
Container, Container,
Grid, Grid,
Card,
CardContent,
Typography, Typography,
Alert, Alert,
CircularProgress, CircularProgress,
useTheme,
useMediaQuery,
ToggleButton, ToggleButton,
ToggleButtonGroup, ToggleButtonGroup,
Tooltip, Tooltip,
@@ -18,14 +14,6 @@ import {
} from '@mui/material'; } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { import {
DollarSign,
TrendingUp,
AlertTriangle,
Activity,
Zap,
BarChart3,
PieChart,
Clock,
Grid3X3, Grid3X3,
List, List,
Info, Info,
@@ -61,11 +49,7 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null); const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
const [viewMode, setViewMode] = useState<ViewMode>('compact'); const [viewMode, setViewMode] = useState<ViewMode>('compact');
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const fetchDashboardData = async () => { const fetchDashboardData = async () => {
try { try {
@@ -75,7 +59,6 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
]); ]);
setDashboardData(billingData); setDashboardData(billingData);
setSystemHealth(healthData); setSystemHealth(healthData);
setLastUpdated(new Date());
} catch (error) { } catch (error) {
setError(error instanceof Error ? error.message : 'Failed to fetch dashboard data'); setError(error instanceof Error ? error.message : 'Failed to fetch dashboard data');
} finally { } finally {
@@ -95,7 +78,6 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
.then(([billingData, health]) => { .then(([billingData, health]) => {
setDashboardData(billingData); setDashboardData(billingData);
setSystemHealth(health); setSystemHealth(health);
setLastUpdated(new Date());
}) })
.catch(() => {/* ignore */}); .catch(() => {/* ignore */});
}); });

View File

@@ -12,7 +12,6 @@ import {
IconButton, IconButton,
Tooltip, Tooltip,
Collapse, Collapse,
Alert,
} from '@mui/material'; } from '@mui/material';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { import {

View File

@@ -22,7 +22,6 @@ import {
import { import {
TrendingUp, TrendingUp,
TrendingDown, TrendingDown,
BarChart3,
Calendar Calendar
} from 'lucide-react'; } from 'lucide-react';

View File

@@ -13,9 +13,6 @@ import { motion } from 'framer-motion';
import { import {
Activity, Activity,
RefreshCw, RefreshCw,
AlertTriangle,
CheckCircle,
XCircle,
Clock, Clock,
Zap Zap
} from 'lucide-react'; } from 'lucide-react';
@@ -27,7 +24,6 @@ import { SystemHealth } from '../../types/monitoring';
import { import {
getHealthStatusColor, getHealthStatusColor,
getHealthStatusIcon, getHealthStatusIcon,
formatResponseTime,
formatErrorRate, formatErrorRate,
getPerformanceStatus getPerformanceStatus
} from '../../services/monitoringService'; } from '../../services/monitoringService';

View File

@@ -1,5 +1,5 @@
import React, { Component, ErrorInfo, ReactNode } from 'react'; import React, { Component, ErrorInfo, ReactNode } from 'react';
import { Box, Typography, Button, Alert, Stack } from '@mui/material'; import { Typography, Button, Alert, Stack } from '@mui/material';
import { Refresh as RefreshIcon } from '@mui/icons-material'; import { Refresh as RefreshIcon } from '@mui/icons-material';
interface ComponentErrorBoundaryProps { interface ComponentErrorBoundaryProps {

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Box, Typography, Chip, Button, CircularProgress, Tooltip } from '@mui/material'; import { Box, Typography, Chip, Button, Tooltip } from '@mui/material';
import { PlayArrow, Pause, Stop } from '@mui/icons-material'; import { PlayArrow } from '@mui/icons-material';
import { ShimmerHeader } from './styled'; import { ShimmerHeader } from './styled';
import UserBadge from './UserBadge'; import UserBadge from './UserBadge';
import { DashboardHeaderProps } from './types'; import { DashboardHeaderProps } from './types';

View File

@@ -8,7 +8,6 @@ import {
Stack, Stack,
Alert, Alert,
Collapse, Collapse,
IconButton,
Divider Divider
} from '@mui/material'; } from '@mui/material';
import { import {

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Box, Container, Alert, Button } from '@mui/material'; import { Container, Alert, Button } from '@mui/material';
import { DashboardContainer } from './styled'; import { DashboardContainer } from './styled';
import { ErrorDisplayProps } from './types'; import { ErrorDisplayProps } from './types';

View File

@@ -9,8 +9,7 @@ import { useCopilotReadable } from '@copilotkit/react-core';
import { import {
WritingPersona, WritingPersona,
PlatformAdaptation, PlatformAdaptation,
PlatformType, PlatformType
UserPersonasResponse
} from '../../../types/PlatformPersonaTypes'; } from '../../../types/PlatformPersonaTypes';
import { import {
getUserPersonas, getUserPersonas,
@@ -37,6 +36,9 @@ interface PlatformPersonaProviderProps {
userId?: number; // Default to 1 for now, can be enhanced with auth context later userId?: number; // Default to 1 for now, can be enhanced with auth context later
} }
// Cache duration: 5 minutes (constant outside component to avoid dependency issues)
const CACHE_DURATION = 5 * 60 * 1000;
// Provider component // Provider component
export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = ({ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = ({
children, children,
@@ -53,9 +55,6 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
const lastRequestTime = useRef<number>(0); const lastRequestTime = useRef<number>(0);
const requestInProgress = useRef<boolean>(false); const requestInProgress = useRef<boolean>(false);
const dataCacheTime = useRef<number>(0); const dataCacheTime = useRef<number>(0);
// Cache duration: 5 minutes
const CACHE_DURATION = 5 * 60 * 1000;
// Fetch persona data function // Fetch persona data function
const fetchPersonas = useCallback(async () => { const fetchPersonas = useCallback(async () => {
@@ -253,7 +252,7 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
dataCacheTime.current = Date.now(); dataCacheTime.current = Date.now();
requestInProgress.current = false; requestInProgress.current = false;
} }
}, [userId, platform, corePersona]); }, [userId, platform, corePersona, platformPersona]);
// Initial data fetch // Initial data fetch
useEffect(() => { useEffect(() => {

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Avatar, Box, Button, Menu, MenuItem, Typography, Tooltip } from '@mui/material'; import { Avatar, Box, Menu, MenuItem, Typography, Tooltip } from '@mui/material';
import { useUser, useClerk } from '@clerk/clerk-react'; import { useUser, useClerk } from '@clerk/clerk-react';
interface UserBadgeProps { interface UserBadgeProps {

View File

@@ -1,13 +1,11 @@
import React from 'react'; import React from 'react';
import { import {
LineChart,
Line, Line,
BarChart, BarChart,
Bar, Bar,
PieChart, PieChart,
Pie, Pie,
Cell, Cell,
AreaChart,
Area, Area,
XAxis, XAxis,
YAxis, YAxis,

View File

@@ -121,7 +121,7 @@ function validateContextIntegrity(state: StrategyCalendarState): boolean {
// Check if calendar context is valid when it exists // Check if calendar context is valid when it exists
if (state.calendarContext) { if (state.calendarContext) {
const { strategyContext, autoPopulatedData } = state.calendarContext; const { strategyContext } = state.calendarContext;
if (strategyContext && !validateContextIntegrity({ ...state, strategyContext })) { if (strategyContext && !validateContextIntegrity({ ...state, strategyContext })) {
return false; return false;
} }

View File

@@ -2,14 +2,12 @@ import React from 'react';
import { import {
Article as ArticleIcon, Article as ArticleIcon,
Search as SearchIcon, Search as SearchIcon,
Campaign as CampaignIcon,
Analytics as AnalyticsIcon, Analytics as AnalyticsIcon,
Psychology as PsychologyIcon, Psychology as PsychologyIcon,
AutoAwesome as AutoAwesomeIcon, AutoAwesome as AutoAwesomeIcon,
Speed as SpeedIcon, Speed as SpeedIcon,
Business as BusinessIcon, Business as BusinessIcon,
SocialDistance as SocialIcon, SocialDistance as SocialIcon,
Create as CreateIcon,
Dashboard as DashboardIcon, Dashboard as DashboardIcon,
Facebook as FacebookIcon, Facebook as FacebookIcon,
LinkedIn as LinkedInIcon, LinkedIn as LinkedInIcon,

View File

@@ -40,7 +40,6 @@ export interface UsePersonaPollingReturn {
export function usePersonaPolling(options: UsePersonaPollingOptions = {}): UsePersonaPollingReturn { export function usePersonaPolling(options: UsePersonaPollingOptions = {}): UsePersonaPollingReturn {
const { const {
interval = 2000, // 2 seconds default interval = 2000, // 2 seconds default
maxAttempts = 0, // No timeout - poll until backend says done
onProgress, onProgress,
onComplete, onComplete,
onError onError

View File

@@ -25,7 +25,6 @@ export function usePolling(
): UsePollingReturn { ): UsePollingReturn {
const { const {
interval = 2000, // 2 seconds default interval = 2000, // 2 seconds default
maxAttempts = 0, // No timeout - poll until backend says done
onProgress, onProgress,
onComplete, onComplete,
onError onError

View File

@@ -40,8 +40,6 @@ export const useWixConnection = () => {
const tokensRaw = sessionStorage.getItem('wix_tokens'); const tokensRaw = sessionStorage.getItem('wix_tokens');
if (connectedFlag && tokensRaw) { if (connectedFlag && tokensRaw) {
const tokens = JSON.parse(tokensRaw);
// Set connected status with site information from tokens // Set connected status with site information from tokens
setStatus({ setStatus({
connected: true, connected: true,

View File

@@ -319,7 +319,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
loadOnboardingIntegration: async (strategyId) => { loadOnboardingIntegration: async (strategyId) => {
try { try {
const integration = await contentPlanningApi.getOnboardingIntegration(strategyId); await contentPlanningApi.getOnboardingIntegration(strategyId);
// Handle onboarding integration data // Handle onboarding integration data
} catch (error: any) { } catch (error: any) {
console.error('Failed to load onboarding integration:', error); console.error('Failed to load onboarding integration:', error);

View File

@@ -1,14 +1,7 @@
{ {
"version": 2, "version": 2,
"builds": [ "buildCommand": "npm run build",
{ "outputDirectory": "build",
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "build"
}
}
],
"routes": [ "routes": [
{ {
"src": "/(.*)", "src": "/(.*)",