ALwrity + Wix + Wordpress + GSC + Bug Fixes
This commit is contained in:
@@ -52,14 +52,14 @@ export const ResearchPollingHandler: React.FC<ResearchPollingHandlerProps> = ({
|
||||
} else {
|
||||
polling.stopPolling();
|
||||
}
|
||||
}, [taskId]);
|
||||
}, [taskId, polling]);
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
polling.stopPolling();
|
||||
};
|
||||
}, []);
|
||||
}, [polling]);
|
||||
|
||||
console.log('ResearchPollingHandler render:', {
|
||||
taskId,
|
||||
|
||||
@@ -213,7 +213,7 @@ export const RewriteFeedbackForm: React.FC<RewriteFeedbackFormProps> = ({
|
||||
onRewriteStarted,
|
||||
onRewriteTriggered
|
||||
}) => {
|
||||
const [isCollectingFeedback, setIsCollectingFeedback] = useState(false);
|
||||
// Note: isCollectingFeedback state removed as it was unused
|
||||
|
||||
// Rewrite Blog Action with HITL
|
||||
useCopilotActionTyped({
|
||||
@@ -303,7 +303,6 @@ export const RewriteFeedbackForm: React.FC<RewriteFeedbackFormProps> = ({
|
||||
|
||||
if (result.success && result.taskId) {
|
||||
onRewriteStarted?.(result.taskId);
|
||||
setIsCollectingFeedback(false);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -16,21 +16,13 @@ import {
|
||||
Card,
|
||||
CardContent,
|
||||
Chip,
|
||||
Divider,
|
||||
Alert,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Button
|
||||
Alert
|
||||
} from '@mui/material';
|
||||
import {
|
||||
ContentCopy as CopyIcon,
|
||||
Check as CheckIcon,
|
||||
Search as SearchIcon,
|
||||
Share as ShareIcon,
|
||||
Code as CodeIcon,
|
||||
Facebook as FacebookIcon,
|
||||
Twitter as TwitterIcon,
|
||||
LinkedIn as LinkedInIcon,
|
||||
Google as GoogleIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
@@ -43,15 +35,6 @@ export const PreviewCard: React.FC<PreviewCardProps> = ({
|
||||
metadata,
|
||||
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 = () => {
|
||||
return new Date().toLocaleDateString('en-US', {
|
||||
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 (
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ mb: 3, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
* 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 {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Button,
|
||||
Chip,
|
||||
LinearProgress,
|
||||
@@ -23,7 +22,6 @@ import {
|
||||
Alert,
|
||||
Grid,
|
||||
Paper,
|
||||
Divider,
|
||||
IconButton,
|
||||
Tooltip
|
||||
} from '@mui/material';
|
||||
@@ -33,11 +31,8 @@ import {
|
||||
Cancel,
|
||||
Warning,
|
||||
TrendingUp,
|
||||
GpsFixed,
|
||||
MenuBook,
|
||||
Search,
|
||||
BarChart,
|
||||
Lightbulb,
|
||||
Refresh,
|
||||
Close
|
||||
} from '@mui/icons-material';
|
||||
@@ -165,7 +160,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
||||
// Debug logging
|
||||
console.log('SEOAnalysisModal render:', { isOpen, blogContent: blogContent?.length, researchData: !!researchData });
|
||||
|
||||
const runSEOAnalysis = async () => {
|
||||
const runSEOAnalysis = useCallback(async () => {
|
||||
try {
|
||||
setIsAnalyzing(true);
|
||||
setError(null);
|
||||
@@ -267,7 +262,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
||||
setError(err instanceof Error ? err.message : 'Analysis failed');
|
||||
setIsAnalyzing(false);
|
||||
}
|
||||
};
|
||||
}, [blogContent, blogTitle, researchData]);
|
||||
|
||||
const getScoreColor = (score: number) => {
|
||||
if (score >= 80) return 'success.main';
|
||||
@@ -281,23 +276,6 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
||||
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
|
||||
const getMetricTooltip = (category: string) => {
|
||||
@@ -352,7 +330,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
||||
if (isOpen && !analysisResult) {
|
||||
runSEOAnalysis();
|
||||
}
|
||||
}, [isOpen]);
|
||||
}, [isOpen, analysisResult, runSEOAnalysis]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@@ -20,22 +20,13 @@ import {
|
||||
Typography,
|
||||
Tabs,
|
||||
Tab,
|
||||
Paper,
|
||||
CircularProgress,
|
||||
Alert,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Chip,
|
||||
Grid,
|
||||
Card,
|
||||
CardContent,
|
||||
Divider,
|
||||
TextField,
|
||||
InputAdornment
|
||||
Chip
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
ContentCopy as CopyIcon,
|
||||
Check as CheckIcon,
|
||||
Preview as PreviewIcon,
|
||||
Search as SearchIcon,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
@@ -12,15 +12,10 @@ import {
|
||||
LinearProgress,
|
||||
Chip,
|
||||
IconButton,
|
||||
Alert,
|
||||
CircularProgress,
|
||||
Card
|
||||
CircularProgress
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
Error as ErrorIcon,
|
||||
Refresh as RefreshIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
TrendingUp as TrendingUpIcon,
|
||||
School as SchoolIcon,
|
||||
@@ -31,8 +26,7 @@ import {
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
// Import existing components for reuse
|
||||
import DataSourceTransparency from '../DataSourceTransparency';
|
||||
import ProgressIndicator from '../ProgressIndicator';
|
||||
// Note: DataSourceTransparency and ProgressIndicator are imported but may be used by child components
|
||||
|
||||
// Import panel components
|
||||
import {
|
||||
@@ -42,57 +36,28 @@ import {
|
||||
StepResultsPanel,
|
||||
EducationalPanel,
|
||||
useCalendarGenerationPolling,
|
||||
type CalendarGenerationProgress,
|
||||
type QualityScores
|
||||
} from './calendarGenerationModalPanels';
|
||||
|
||||
// Import new StepProgressTracker component
|
||||
import StepProgressTracker from './calendarGenerationModalPanels/StepProgressTracker';
|
||||
|
||||
// Import styles
|
||||
// Import styles (only used ones)
|
||||
import {
|
||||
dialogStyles,
|
||||
contentContainerStyles,
|
||||
progressBarContainerStyles,
|
||||
progressBarStyles,
|
||||
stepProgressBarStyles,
|
||||
getStepIndicatorStyles,
|
||||
getStepCardStyles,
|
||||
stepCircleBaseStyles,
|
||||
getStepCircleColor,
|
||||
tabButtonStyles,
|
||||
activityIndicatorStyles,
|
||||
qualityScoreContainerStyles,
|
||||
getQualityScoreBackground,
|
||||
qualityScoreInnerStyles,
|
||||
dataSourceCardStyles,
|
||||
dataSourceIconStyles,
|
||||
getDataSourceIconColor,
|
||||
qualityMetricsContainerStyles,
|
||||
getMetricColor,
|
||||
stepResultsCardStyles,
|
||||
stepResultsHeaderStyles,
|
||||
stepResultsContentStyles,
|
||||
loadingContainerStyles,
|
||||
loadingContentStyles,
|
||||
animationDurations,
|
||||
animationEasing,
|
||||
springConfig,
|
||||
staggerDelay,
|
||||
cardStaggerDelay,
|
||||
fadeInUp,
|
||||
fadeInLeft,
|
||||
scaleIn,
|
||||
slideInStaggered,
|
||||
hoverLift,
|
||||
hoverScale,
|
||||
tapScale,
|
||||
pulseAnimation,
|
||||
smallPulseAnimation,
|
||||
colorPulseAnimation,
|
||||
progressFillAnimation,
|
||||
progressOverlayStyles,
|
||||
stepProgressOverlayStyles
|
||||
progressOverlayStyles
|
||||
} from './CalendarGenerationModal.styles';
|
||||
|
||||
// Types
|
||||
@@ -262,19 +227,11 @@ const CalendarGenerationModal: React.FC<CalendarGenerationModalProps> = ({
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
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
|
||||
const {
|
||||
progress,
|
||||
isPolling,
|
||||
error,
|
||||
startPolling,
|
||||
stopPolling,
|
||||
getStepStatus,
|
||||
@@ -309,11 +266,7 @@ const CalendarGenerationModal: React.FC<CalendarGenerationModalProps> = ({
|
||||
console.log('❌ Calendar generation error:', currentProgress.errors);
|
||||
onError(currentProgress.errors[0]?.message || 'Unknown error');
|
||||
}
|
||||
}, [currentProgress?.status, currentProgress?.errors, onError]);
|
||||
|
||||
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setActiveTab(newValue);
|
||||
};
|
||||
}, [currentProgress, onError]);
|
||||
|
||||
const getQualityColor = (score: number) => {
|
||||
if (score >= 0.9) return 'success';
|
||||
|
||||
@@ -10,10 +10,8 @@ import {
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
TextField,
|
||||
Slider,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Alert,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
@@ -23,12 +21,10 @@ import {
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AutoAwesome as AIIcon,
|
||||
Speed as SpeedIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
TrendingUp as TrendingIcon,
|
||||
Psychology as PsychologyIcon,
|
||||
Info as InfoIcon,
|
||||
Settings as SettingsIcon,
|
||||
Assessment as AssessmentIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
@@ -181,8 +181,6 @@ const CalendarConfigurationStep: React.FC<CalendarConfigurationStepProps> = ({
|
||||
const [userGuidance, setUserGuidance] = useState<UserGuidance | null>(null);
|
||||
const [transparencyIndicators, setTransparencyIndicators] = useState<TransparencyIndicators | null>(null);
|
||||
const [showSmartDefaults, setShowSmartDefaults] = useState(true);
|
||||
const [showUserGuidance, setShowUserGuidance] = useState(true);
|
||||
const [showTransparency, setShowTransparency] = useState(true);
|
||||
|
||||
// Generate smart defaults and guidance when strategy context changes
|
||||
useEffect(() => {
|
||||
@@ -245,6 +243,8 @@ const CalendarConfigurationStep: React.FC<CalendarConfigurationStepProps> = ({
|
||||
// Fix invalid timezone
|
||||
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]);
|
||||
|
||||
// Apply smart defaults to configuration
|
||||
|
||||
@@ -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 {
|
||||
Box,
|
||||
@@ -6,60 +6,19 @@ import {
|
||||
Typography,
|
||||
Button,
|
||||
LinearProgress,
|
||||
Alert,
|
||||
Chip,
|
||||
IconButton,
|
||||
Tooltip as MuiTooltip,
|
||||
Card,
|
||||
CardContent,
|
||||
Grid,
|
||||
Divider,
|
||||
CircularProgress,
|
||||
Badge,
|
||||
Collapse,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions
|
||||
} from '@mui/material';
|
||||
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,
|
||||
Refresh as RefreshIcon,
|
||||
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
|
||||
Info as InfoIcon
|
||||
} from '@mui/icons-material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useStrategyBuilderStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/strategyBuilderStore';
|
||||
import { useEnhancedStrategyStore } from '../../../stores/enhancedStrategyStore';
|
||||
import StrategicInputField from './ContentStrategyBuilder/StrategicInputField';
|
||||
import EnhancedTooltip from './ContentStrategyBuilder/EnhancedTooltip';
|
||||
import AIRecommendationsPanel from './AIRecommendationsPanel';
|
||||
import DataSourceTransparency from './DataSourceTransparency';
|
||||
import StrategyAutofillTransparencyModal from './StrategyAutofillTransparencyModal';
|
||||
import EnterpriseDatapointsModal from './EnterpriseDatapointsModal';
|
||||
@@ -79,7 +38,7 @@ import { useStrategyCreation } from './ContentStrategyBuilder/hooks/useStrategyC
|
||||
import { useCopilotReadable, useCopilotAdditionalInstructions } from "@copilotkit/react-core";
|
||||
|
||||
// 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 { setupCSSAnimations, cleanupCSSAnimations } from './ContentStrategyBuilder/utils/cssAnimations';
|
||||
|
||||
@@ -115,7 +74,6 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
updateFormField,
|
||||
validateFormField,
|
||||
validateAllFields,
|
||||
resetForm,
|
||||
autoPopulateFromOnboarding,
|
||||
createStrategy: createEnhancedStrategy,
|
||||
calculateCompletionPercentage,
|
||||
@@ -128,9 +86,6 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
// Enhanced Strategy Store (for AI analysis, progressive disclosure, transparency)
|
||||
const {
|
||||
aiGenerating,
|
||||
currentStep,
|
||||
completedSteps,
|
||||
disclosureSteps,
|
||||
transparencyModalOpen,
|
||||
transparencyGenerationProgress: storeGenerationProgress,
|
||||
currentPhase,
|
||||
@@ -144,11 +99,6 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
addTransparencyMessage,
|
||||
clearTransparencyMessages,
|
||||
setTransparencyGenerating: setIsGenerating,
|
||||
completeStep,
|
||||
getNextStep,
|
||||
getPreviousStep,
|
||||
setCurrentStep,
|
||||
canProceedToDisclosureStep: canProceedToStep,
|
||||
generateAIRecommendations,
|
||||
setAIGenerating
|
||||
} = useEnhancedStrategyStore();
|
||||
@@ -157,7 +107,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
useCopilotActions();
|
||||
|
||||
// Check if this component is currently visible (active tab)
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 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 [localEducationalContent, setLocalEducationalContent] = useState<any>(null);
|
||||
const [localGenerationProgress, setLocalGenerationProgress] = useState<number>(0);
|
||||
const [showAIRecModal, setShowAIRecModal] = useState(false);
|
||||
|
||||
// Ref to track if we've already set the default category
|
||||
@@ -355,13 +303,9 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
|
||||
const {
|
||||
refreshMessage,
|
||||
setRefreshMessage,
|
||||
refreshProgress,
|
||||
setRefreshProgress,
|
||||
isRefreshing,
|
||||
setIsRefreshing,
|
||||
refreshError,
|
||||
setRefreshError,
|
||||
handleAIRefresh
|
||||
} = useAIRefresh({
|
||||
setTransparencyModalOpen,
|
||||
@@ -374,18 +318,16 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
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 completionPercentage = useMemo(() => calculateCompletionPercentage(), [formData]);
|
||||
|
||||
// Use extracted hooks
|
||||
const {
|
||||
reviewedCategories,
|
||||
isMarkingReviewed,
|
||||
categoryCompletionMessage,
|
||||
handleConfirmCategoryReview,
|
||||
isCategoryReviewed,
|
||||
getNextUnreviewedCategory,
|
||||
setReviewedCategories
|
||||
handleConfirmCategoryReview
|
||||
} = useCategoryReview({ completionStats, setError, setActiveCategory });
|
||||
|
||||
const {
|
||||
|
||||
@@ -13,8 +13,6 @@ export const useCopilotActions = () => {
|
||||
updateFormField,
|
||||
validateFormField,
|
||||
setError,
|
||||
autoPopulatedFields,
|
||||
dataSources,
|
||||
calculateCompletionPercentage,
|
||||
getCompletionStats
|
||||
} = useStrategyBuilderStore();
|
||||
@@ -93,7 +91,7 @@ export const useCopilotActions = () => {
|
||||
console.log(`📝 Populating field ${fieldId} with value: ${value}`);
|
||||
|
||||
// Call backend API for intelligent field population
|
||||
const response = await contentPlanningApi.generateCategoryData(
|
||||
await contentPlanningApi.generateCategoryData(
|
||||
'individual_field',
|
||||
`Populate ${fieldId} with: ${value}. Reasoning: ${reasoning || 'User request'}`,
|
||||
formData
|
||||
@@ -188,7 +186,7 @@ export const useCopilotActions = () => {
|
||||
setTransparencyGenerating(false);
|
||||
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
|
||||
const validateStrategyField = useCallback(async ({ fieldId }: any) => {
|
||||
@@ -301,11 +299,7 @@ export const useCopilotActions = () => {
|
||||
// Start transparency flow (same as Refresh & Autofill button)
|
||||
const { transparencyInterval } = await triggerTransparencyFlow('autofill', 'Auto-population from onboarding data');
|
||||
|
||||
// Get current form data to see what's already filled
|
||||
const currentFilledFields = Object.keys(formData).filter(key => {
|
||||
const value = formData[key];
|
||||
return value && typeof value === 'string' && value.trim() !== '';
|
||||
});
|
||||
// Get empty fields that need to be filled
|
||||
const emptyFields = Object.keys(formData).filter(key => {
|
||||
const value = formData[key];
|
||||
return !value || typeof value !== 'string' || value.trim() === '';
|
||||
@@ -429,7 +423,7 @@ export const useCopilotActions = () => {
|
||||
setTransparencyGenerating(false);
|
||||
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
|
||||
// This is the only way to comply with React hooks rules
|
||||
|
||||
@@ -10,8 +10,6 @@ import {
|
||||
Tooltip,
|
||||
Alert,
|
||||
AlertTitle,
|
||||
CircularProgress,
|
||||
LinearProgress,
|
||||
Divider,
|
||||
Button,
|
||||
Dialog,
|
||||
@@ -24,13 +22,7 @@ import {
|
||||
ListItemIcon
|
||||
} from '@mui/material';
|
||||
import {
|
||||
TrendingUp as TrendingUpIcon,
|
||||
TrendingDown as TrendingDownIcon,
|
||||
Assessment as AssessmentIcon,
|
||||
Speed as SpeedIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
People as EngagementIcon,
|
||||
MonetizationOn as MonetizationOnIcon,
|
||||
Refresh as RefreshIcon,
|
||||
AutoAwesome as AutoAwesomeIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
@@ -38,11 +30,10 @@ import {
|
||||
Error as ErrorIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
Timeline as TimelineIcon,
|
||||
Close as CloseIcon
|
||||
} from '@mui/icons-material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { safeRenderText, safeRenderArray, hasValidData, getFallbackValue } from '../utils/defensiveRendering';
|
||||
import { motion } from 'framer-motion';
|
||||
import { safeRenderText } from '../utils/defensiveRendering';
|
||||
|
||||
// Import our advanced chart components
|
||||
import {
|
||||
@@ -56,9 +47,6 @@ import {
|
||||
// Import real-time data hook
|
||||
import { useMockRealTimeData } from '../../../../../hooks/useRealTimeData';
|
||||
|
||||
// Import API services
|
||||
import { strategyMonitoringApi } from '../../../../../services/strategyMonitoringApi';
|
||||
|
||||
interface EnhancedPerformanceVisualizationProps {
|
||||
strategyId: number;
|
||||
strategyData: any;
|
||||
@@ -88,7 +76,7 @@ const EnhancedPerformanceVisualization: React.FC<EnhancedPerformanceVisualizatio
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Use real-time data hook
|
||||
const { data: realTimeData, isConnected, error: realTimeError } = useMockRealTimeData(strategyId);
|
||||
const { data: realTimeData, isConnected } = useMockRealTimeData(strategyId);
|
||||
|
||||
useEffect(() => {
|
||||
loadQualityAnalysis();
|
||||
|
||||
@@ -5,21 +5,16 @@ import {
|
||||
Typography,
|
||||
Chip,
|
||||
CircularProgress,
|
||||
Alert,
|
||||
IconButton,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Grid,
|
||||
Paper,
|
||||
LinearProgress,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
Divider,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
@@ -33,14 +28,8 @@ import {
|
||||
Help as UnknownIcon,
|
||||
Refresh as RefreshIcon,
|
||||
Close as CloseIcon,
|
||||
TrendingUp as TrendingUpIcon,
|
||||
TrendingDown as TrendingDownIcon,
|
||||
Speed as SpeedIcon,
|
||||
BugReport as BugReportIcon,
|
||||
Storage as StorageIcon,
|
||||
Timeline as TimelineIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
NetworkCheck as NetworkCheckIcon
|
||||
Analytics as AnalyticsIcon
|
||||
} from '@mui/icons-material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import MonitoringCharts from './MonitoringCharts';
|
||||
@@ -95,10 +84,10 @@ const SystemStatusIndicator: React.FC<SystemStatusIndicatorProps> = ({ className
|
||||
const [statusData, setStatusData] = useState<SystemStatusData | null>(null);
|
||||
const [detailedStats, setDetailedStats] = useState<DetailedStatsData | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [, setError] = useState<string | null>(null);
|
||||
const [dashboardOpen, setDashboardOpen] = useState(false);
|
||||
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 () => {
|
||||
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 (
|
||||
<>
|
||||
<Tooltip title={tooltipContent} arrow placement="bottom">
|
||||
|
||||
@@ -29,10 +29,7 @@ import {
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
Divider,
|
||||
LinearProgress,
|
||||
Tooltip,
|
||||
Badge
|
||||
LinearProgress
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Add as AddIcon,
|
||||
@@ -42,27 +39,17 @@ import {
|
||||
Event as EventIcon,
|
||||
Refresh as RefreshIcon,
|
||||
TrendingUp as TrendingIcon,
|
||||
ContentCopy as RepurposeIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
Psychology as PsychologyIcon,
|
||||
Business as BusinessIcon,
|
||||
Group as GroupIcon,
|
||||
Timeline as TimelineIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
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
|
||||
} from '@mui/icons-material';
|
||||
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
||||
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
@@ -92,15 +79,10 @@ const CalendarTab: React.FC = () => {
|
||||
createEvent,
|
||||
updateEvent,
|
||||
deleteEvent,
|
||||
loading,
|
||||
error,
|
||||
loadCalendarEvents,
|
||||
updateCalendarEvents,
|
||||
// New calendar generation state
|
||||
generatedCalendar,
|
||||
performancePrediction,
|
||||
contentRepurposing,
|
||||
aiInsights,
|
||||
calendarGenerationError,
|
||||
dataLoading,
|
||||
calendarGenerationLoading
|
||||
@@ -118,28 +100,15 @@ const CalendarTab: React.FC = () => {
|
||||
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 : [];
|
||||
|
||||
useEffect(() => {
|
||||
loadCalendarData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const loadCalendarData = async () => {
|
||||
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
|
||||
await loadCalendarEvents();
|
||||
} catch (error) {
|
||||
@@ -211,45 +180,6 @@ const CalendarTab: React.FC = () => {
|
||||
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) => {
|
||||
switch (status) {
|
||||
case 'draft': return 'default';
|
||||
|
||||
@@ -7,8 +7,7 @@ import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
Chip
|
||||
ListItemIcon
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Analytics as AnalyticsIcon,
|
||||
|
||||
@@ -9,9 +9,6 @@ import {
|
||||
Button,
|
||||
CircularProgress
|
||||
} from '@mui/material';
|
||||
import {
|
||||
PieChart as PieChartIcon
|
||||
} from '@mui/icons-material';
|
||||
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
||||
|
||||
const ContentPillarsTab: React.FC = () => {
|
||||
@@ -21,6 +18,7 @@ const ContentPillarsTab: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
loadContentPillars();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentStrategy]);
|
||||
|
||||
const loadContentPillars = async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
@@ -25,25 +25,13 @@ const ContentStrategyTab: React.FC = () => {
|
||||
const strategies = useContentPlanningStore(state => state.strategies);
|
||||
const currentStrategy = useContentPlanningStore(state => state.currentStrategy);
|
||||
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 loadStrategies = useContentPlanningStore(state => state.loadStrategies);
|
||||
const loadAIInsights = useContentPlanningStore(state => state.loadAIInsights);
|
||||
const loadAIRecommendations = useContentPlanningStore(state => state.loadAIRecommendations);
|
||||
const setLatestGeneratedStrategy = useContentPlanningStore(state => state.setLatestGeneratedStrategy);
|
||||
|
||||
const [strategyForm, setStrategyForm] = useState({
|
||||
name: '',
|
||||
description: '',
|
||||
industry: '',
|
||||
target_audience: '',
|
||||
content_pillars: []
|
||||
});
|
||||
|
||||
// Real data states
|
||||
const [strategicIntelligence, setStrategicIntelligence] = useState<any>(null);
|
||||
const [strategyData, setStrategyData] = useState<StrategyData | null>(null);
|
||||
const [strategyDataLoading, setStrategyDataLoading] = useState(false);
|
||||
const [strategyDataError, setStrategyDataError] = useState<string | null>(null);
|
||||
@@ -65,6 +53,7 @@ const ContentStrategyTab: React.FC = () => {
|
||||
// Load data on component mount
|
||||
useEffect(() => {
|
||||
loadInitialData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// 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)');
|
||||
// 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)
|
||||
useEffect(() => {
|
||||
@@ -136,6 +125,7 @@ const ContentStrategyTab: React.FC = () => {
|
||||
setShowOnboarding(true);
|
||||
}
|
||||
// If strategiesArray.length === 0 and !hasCheckedStrategy, do nothing (wait for data to load)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [strategies, loadStrategies, isFromStrategyBuilder]);
|
||||
|
||||
const loadStrategyData = async () => {
|
||||
@@ -390,9 +380,6 @@ const ContentStrategyTab: React.FC = () => {
|
||||
}
|
||||
|
||||
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
|
||||
// In a real implementation, you would check a status field from the database
|
||||
setStrategyStatus('active');
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Tabs,
|
||||
Tab,
|
||||
Button
|
||||
Tab
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AutoAwesome as AutoAwesomeIcon,
|
||||
@@ -20,7 +19,6 @@ import { apiClient } from '../../../api/client';
|
||||
|
||||
// Import hooks and services
|
||||
import { useStrategyCalendarContext } from '../../../contexts/StrategyCalendarContext';
|
||||
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
||||
|
||||
// Import types
|
||||
import { type CalendarConfig } from '../components/CalendarWizardSteps/types';
|
||||
@@ -51,11 +49,10 @@ const CreateTab: React.FC = () => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [currentCalendarConfig, setCurrentCalendarConfig] = useState<CalendarConfig | null>(null);
|
||||
const [sessionId, setSessionId] = useState<string>('');
|
||||
const [isStartingGeneration, setIsStartingGeneration] = useState(false);
|
||||
|
||||
const location = useLocation();
|
||||
const { state: { strategyContext }, isFromStrategyActivation } = useStrategyCalendarContext();
|
||||
const [userData, setUserData] = useState<any>({});
|
||||
const [userData] = useState<any>({});
|
||||
|
||||
// Handle navigation from strategy activation
|
||||
useEffect(() => {
|
||||
@@ -74,7 +71,7 @@ const CreateTab: React.FC = () => {
|
||||
console.log('🎯 CreateTab: Switching to Calendar Wizard tab (index 1)');
|
||||
setTabValue(1); // Switch to Calendar Wizard tab
|
||||
}
|
||||
}, [isFromStrategyActivation, strategyContext?.activationStatus]);
|
||||
}, [isFromStrategyActivation, strategyContext?.activationStatus, location.state]);
|
||||
|
||||
// Also check on mount for immediate navigation state
|
||||
useEffect(() => {
|
||||
@@ -109,9 +106,6 @@ const CreateTab: React.FC = () => {
|
||||
setCurrentCalendarConfig(calendarConfig);
|
||||
setIsModalOpen(true);
|
||||
|
||||
// Set loading state to prevent multiple clicks
|
||||
setIsStartingGeneration(true);
|
||||
|
||||
// Transform calendarConfig to match backend CalendarGenerationRequest format
|
||||
const requestData = {
|
||||
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
|
||||
let startResponse;
|
||||
let retryCount = 0;
|
||||
const maxRetries = 3;
|
||||
|
||||
while (retryCount < maxRetries) {
|
||||
for (let retryCount = 0; retryCount < maxRetries; retryCount++) {
|
||||
try {
|
||||
const response = await apiClient.post('/api/content-planning/calendar-generation/start', requestData);
|
||||
startResponse = { ok: true, data: response.data };
|
||||
break; // Success, exit retry loop
|
||||
} catch (error: any) {
|
||||
console.warn(`⚠️ Attempt ${retryCount + 1} failed with error:`, error);
|
||||
retryCount++;
|
||||
if (retryCount < maxRetries) {
|
||||
if (retryCount < maxRetries - 1) {
|
||||
// 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 {
|
||||
startResponse = { ok: false, data: null };
|
||||
}
|
||||
@@ -175,8 +168,7 @@ const CreateTab: React.FC = () => {
|
||||
setCurrentCalendarConfig(null);
|
||||
setSessionId('');
|
||||
} finally {
|
||||
// Clear loading state
|
||||
setIsStartingGeneration(false);
|
||||
// Cleanup complete
|
||||
}
|
||||
}, [userData, strategyContext]);
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import {
|
||||
Box,
|
||||
Tabs,
|
||||
Tab,
|
||||
Typography,
|
||||
Alert
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Analytics as AnalyticsIcon,
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -16,10 +15,6 @@ import {
|
||||
TableRow,
|
||||
Button
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Search as SearchIcon
|
||||
} from '@mui/icons-material';
|
||||
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
||||
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
||||
|
||||
const KeywordResearchTab: React.FC = () => {
|
||||
|
||||
@@ -49,6 +49,7 @@ const RefineAnalysisTab: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
loadGapAnalysisData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const loadGapAnalysisData = async () => {
|
||||
|
||||
@@ -87,16 +87,16 @@ function diffMarkup(oldText: string, newText: string): string {
|
||||
out += a[i];
|
||||
i++; j++;
|
||||
} 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++;
|
||||
} else {
|
||||
out += `<em class=\"fbw-add\">${escapeHtml(b[j])}</em>`;
|
||||
out += `<em class="fbw-add">${escapeHtml(b[j])}</em>`;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
while (i < n) { out += `<s class=\"fbw-del\">${escapeHtml(a[i++])}</s>`; }
|
||||
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>';
|
||||
while (i < n) { out += `<s class="fbw-del">${escapeHtml(a[i++])}</s>`; }
|
||||
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>';
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ const FacebookWriterContent: React.FC<FacebookWriterProps> = ({ className = '' }
|
||||
const [livePreviewHtml, setLivePreviewHtml] = React.useState<string>('');
|
||||
const [isPreviewing, setIsPreviewing] = React.useState<boolean>(false);
|
||||
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<{
|
||||
headline_variations: string[];
|
||||
primary_text_variations: string[];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useCopilotAction } from '@copilotkit/react-core';
|
||||
import { linkedInWriterApi, LinkedInPostRequest, GroundingLevel } from '../../services/linkedInWriterApi';
|
||||
import { linkedInWriterApi, GroundingLevel } from '../../services/linkedInWriterApi';
|
||||
import {
|
||||
mapPostType,
|
||||
mapTone,
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
mapSearchEngine,
|
||||
readPrefs
|
||||
} from './utils/linkedInWriterUtils';
|
||||
import { PostHITL, ArticleHITL, CarouselHITL, VideoScriptHITL, CommentResponseHITL } from './components';
|
||||
import { apiClient } from '../../api/client';
|
||||
|
||||
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
|
||||
window.dispatchEvent(new CustomEvent('linkedinwriter:progressStep', {
|
||||
detail: {
|
||||
@@ -750,7 +738,7 @@ const RegisterLinkedInActions: React.FC = () => {
|
||||
{ name: 'improvement_type', type: 'string', required: false }
|
||||
],
|
||||
handler: async (args: any) => {
|
||||
const { recommendation, current_content, improvement_type } = args;
|
||||
const { recommendation } = args;
|
||||
|
||||
// Analyze the recommendation and provide specific improvement guidance
|
||||
let improvementGuidance = '';
|
||||
|
||||
@@ -57,7 +57,6 @@ const RegisterLinkedInEditActions: React.FC = () => {
|
||||
],
|
||||
handler: async (args: any) => {
|
||||
const content = args?.content || '';
|
||||
const industry = args?.industry || 'Technology';
|
||||
|
||||
// Placeholder for hashtag addition
|
||||
const hashtags = '#ProfessionalDevelopment #Networking #IndustryInsights #CareerGrowth';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { usePlatformPersonaContext } from '../../shared/PersonaContext/PlatformPersonaProvider';
|
||||
import { apiClient } from '../../../api/client';
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ interface ContentEditorProps {
|
||||
topic?: string;
|
||||
}
|
||||
|
||||
export { ContentEditor };
|
||||
|
||||
const ContentEditor: React.FC<ContentEditorProps> = ({
|
||||
isPreviewing,
|
||||
pendingEdit,
|
||||
@@ -62,6 +60,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
||||
const ctaCooldownRef = useRef<number | null>(null); // 15s cooldown after dismissing CTA
|
||||
useEffect(() => {
|
||||
if (DEBUG_WA) console.log('🎯 [ContentEditor] waSuggestion changed:', waSuggestion);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [waSuggestion]);
|
||||
const waTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const hasTriggeredOnceRef = useRef<boolean>(false);
|
||||
@@ -228,8 +227,6 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
||||
lastWords: words.slice(-5).join(' ')
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const last = lastSuggestMetaRef.current;
|
||||
const textHash = getStableContextHash(uptoCaret);
|
||||
|
||||
// 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";
|
||||
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
|
||||
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;
|
||||
coolDownUntilRef.current = Date.now() + 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";
|
||||
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
|
||||
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;
|
||||
coolDownUntilRef.current = Date.now() + retryMs;
|
||||
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
|
||||
@@ -421,4 +418,6 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
||||
<CitationHoverHandler researchSources={researchSources || []} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export { ContentEditor };
|
||||
@@ -3,29 +3,12 @@ import {
|
||||
Box,
|
||||
Container,
|
||||
Typography,
|
||||
Card,
|
||||
CardContent,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
Chip,
|
||||
Tooltip,
|
||||
Paper,
|
||||
Modal,
|
||||
Button,
|
||||
IconButton,
|
||||
Divider,
|
||||
LinearProgress,
|
||||
Avatar,
|
||||
Stack
|
||||
Paper
|
||||
} from '@mui/material';
|
||||
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 PublishPillarChips from './components/PublishPillarChips';
|
||||
import AnalyzePillarChips from './components/AnalyzePillarChips';
|
||||
@@ -484,14 +467,11 @@ const PillarCard: React.FC<{
|
||||
|
||||
// Main Content Lifecycle Pillars Component
|
||||
const ContentLifecyclePillars: React.FC = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [onboardingModalOpen, setOnboardingModalOpen] = useState(false);
|
||||
|
||||
// Workflow store hooks
|
||||
const {
|
||||
currentWorkflow,
|
||||
workflowProgress,
|
||||
isLoading: workflowLoading,
|
||||
startWorkflow,
|
||||
} = useWorkflowStore();
|
||||
|
||||
@@ -23,7 +23,7 @@ import CompactSidebar from './components/CompactSidebar';
|
||||
|
||||
// Shared types and utilities
|
||||
import { Tool } from '../shared/types';
|
||||
import { getFilteredCategories, getToolsForCategory } from '../shared/utils';
|
||||
import { getToolsForCategory } from '../shared/utils';
|
||||
|
||||
// Zustand stores
|
||||
import { useDashboardStore } from '../../stores/dashboardStore';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, Chip, useTheme } from '@mui/material';
|
||||
import { Box, Chip } from '@mui/material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Box,
|
||||
Paper,
|
||||
Typography,
|
||||
Chip,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Divider,
|
||||
@@ -14,8 +13,6 @@ import {
|
||||
Filter,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Activity,
|
||||
Zap,
|
||||
Star
|
||||
} from 'lucide-react';
|
||||
|
||||
@@ -78,26 +75,16 @@ const CompactSidebar: React.FC<CompactSidebarProps> = ({
|
||||
// State for search expansion on hover
|
||||
const [isSearchExpanded, setIsSearchExpanded] = useState(false);
|
||||
// State for sidebar hover expansion
|
||||
const [isSidebarHovered, setIsSidebarHovered] = useState(false);
|
||||
const [, setIsSidebarHovered] = useState(false);
|
||||
// State for favorites expansion on hover
|
||||
const [isFavoritesExpanded, setIsFavoritesExpanded] = useState(false);
|
||||
// Track original collapsed state for hover behavior
|
||||
const [wasOriginallyCollapsed, setWasOriginallyCollapsed] = useState(false);
|
||||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
const [rippleIndex, setRippleIndex] = useState(-1);
|
||||
const [, setRippleIndex] = useState(-1);
|
||||
const [shouldAutoExpand, setShouldAutoExpand] = 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
|
||||
const startRippleEffect = useCallback(() => {
|
||||
const categoryEntries = Object.entries(toolCategories).slice(0, 5);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, Chip, useTheme } from '@mui/material';
|
||||
import { Box, Chip } from '@mui/material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
IconButton,
|
||||
Avatar,
|
||||
Stack,
|
||||
LinearProgress,
|
||||
CircularProgress,
|
||||
Card,
|
||||
CardContent
|
||||
@@ -57,13 +56,10 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
|
||||
navigationState,
|
||||
completeTask,
|
||||
skipTask,
|
||||
moveToNextTask,
|
||||
isLoading,
|
||||
isWorkflowComplete
|
||||
} = useWorkflowStore();
|
||||
|
||||
const [selectedTask, setSelectedTask] = useState<TodayTask | null>(null);
|
||||
|
||||
// Prefer live workflow tasks (to reflect updated statuses), fallback to props
|
||||
const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0
|
||||
? currentWorkflow.tasks
|
||||
@@ -100,12 +96,6 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleStartWorkflow = async () => {
|
||||
if (currentWorkflow) {
|
||||
await moveToNextTask();
|
||||
}
|
||||
};
|
||||
|
||||
const handleNextPillar = async () => {
|
||||
// Close current modal
|
||||
onClose();
|
||||
@@ -158,9 +148,6 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
|
||||
task.status === 'completed' || task.status === 'skipped'
|
||||
);
|
||||
|
||||
// Check if this is the Plan pillar
|
||||
const isPlanPillar = pillarId === 'plan';
|
||||
|
||||
// Define pillar order for navigation
|
||||
const pillarOrder = ['plan', 'generate', 'publish', 'analyze', 'engage', 'remarket'];
|
||||
const currentPillarIndex = pillarOrder.indexOf(pillarId);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
@@ -7,10 +7,6 @@ import {
|
||||
IconButton,
|
||||
Button,
|
||||
Typography,
|
||||
Stepper,
|
||||
Step,
|
||||
StepLabel,
|
||||
StepConnector,
|
||||
Fade,
|
||||
LinearProgress,
|
||||
} from '@mui/material';
|
||||
@@ -25,7 +21,6 @@ import {
|
||||
Key,
|
||||
ContentPasteRounded,
|
||||
} from '@mui/icons-material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
interface ApiKeyCarouselProps {
|
||||
providers: Array<{
|
||||
@@ -40,40 +35,19 @@ interface ApiKeyCarouselProps {
|
||||
link: string;
|
||||
free: boolean;
|
||||
recommended: boolean;
|
||||
benefits: string[];
|
||||
}>;
|
||||
benefits: string[];
|
||||
}>;
|
||||
currentProvider: number;
|
||||
setCurrentProvider: (index: number) => 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> = ({
|
||||
providers,
|
||||
currentProvider,
|
||||
setCurrentProvider,
|
||||
onProviderFocus,
|
||||
}) => {
|
||||
const [autoProgress, setAutoProgress] = useState(false);
|
||||
const provider = providers[currentProvider];
|
||||
|
||||
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 (
|
||||
<Box sx={{ width: '100%', maxWidth: 600, mx: 'auto' }}>
|
||||
{/* Progress Stepper - Hidden as requested */}
|
||||
|
||||
@@ -10,13 +10,10 @@ import {
|
||||
ListItemText,
|
||||
Chip,
|
||||
Divider,
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
CheckCircle,
|
||||
Star,
|
||||
Security,
|
||||
Speed,
|
||||
TrendingUp,
|
||||
Insights,
|
||||
Search,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useAuth } from '@clerk/clerk-react';
|
||||
import { getApiKeysForOnboarding, getStep1ApiKeysFromProgress, saveApiKey } from '../../../../api/onboarding';
|
||||
import { getKeyStatus, formatErrorMessage } from '../../common/onboardingUtils';
|
||||
import { Provider } from './ProviderCard';
|
||||
import { apiClient } from '../../../../api/client';
|
||||
|
||||
export const useApiKeyStep = (onContinue: (stepData?: any) => void) => {
|
||||
const { getToken } = useAuth();
|
||||
const [geminiKey, setGeminiKey] = useState('');
|
||||
const [exaKey, setExaKey] = useState('');
|
||||
const [copilotkitKey, setCopilotkitKey] = useState('');
|
||||
|
||||
@@ -52,7 +52,7 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
|
||||
const [analysisStep, setAnalysisStep] = useState('');
|
||||
const [competitors, setCompetitors] = useState<Competitor[]>([]);
|
||||
const [socialMediaAccounts, setSocialMediaAccounts] = useState<any>({});
|
||||
const [socialMediaCitations, setSocialMediaCitations] = useState<any[]>([]);
|
||||
const [, setSocialMediaCitations] = useState<any[]>([]);
|
||||
const [researchSummary, setResearchSummary] = useState<ResearchSummary | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showProgressModal, setShowProgressModal] = useState(false);
|
||||
@@ -260,6 +260,7 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
|
||||
} finally {
|
||||
setIsAnalyzingSitemap(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [userUrl, competitors, industryContext, isAnalyzingSitemap]);
|
||||
|
||||
// Initialize: Check cache first, then run analysis if needed
|
||||
@@ -313,36 +314,6 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
|
||||
};
|
||||
}, [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)
|
||||
useEffect(() => {
|
||||
|
||||
@@ -15,22 +15,13 @@ import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Alert,
|
||||
LinearProgress
|
||||
Alert
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Search as SearchIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
TrendingUp as TrendingIcon,
|
||||
Speed as SpeedIcon,
|
||||
Security as SecurityIcon,
|
||||
CheckCircle as CheckIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
Rocket as RocketIcon,
|
||||
DataUsage as DataIcon,
|
||||
Compare as CompareIcon,
|
||||
Insights as InsightsIcon,
|
||||
Assessment as AssessmentIcon
|
||||
Insights as InsightsIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
export const ComingSoonSection: React.FC = () => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAuth } from '@clerk/clerk-react';
|
||||
import {
|
||||
Box,
|
||||
Fade,
|
||||
@@ -17,19 +16,7 @@ import {
|
||||
// Platform Icons
|
||||
Web as WordPressIcon,
|
||||
Web as WixIcon,
|
||||
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
|
||||
Google as GoogleIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
// Import refactored components
|
||||
@@ -59,12 +46,11 @@ interface IntegrationPlatform {
|
||||
}
|
||||
|
||||
const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateHeaderContent }) => {
|
||||
const { getToken } = useAuth();
|
||||
const [email, setEmail] = useState<string>('');
|
||||
|
||||
// Use custom hooks
|
||||
const { gscSites, connectedPlatforms, setConnectedPlatforms, setGscSites, handleGSCConnect } = useGSCConnection();
|
||||
const { isLoading, showToast, setShowToast, toastMessage, setToastMessage, handleConnect } = usePlatformConnections();
|
||||
const { gscSites, connectedPlatforms, setConnectedPlatforms, handleGSCConnect } = useGSCConnection();
|
||||
const { isLoading, showToast, setShowToast, toastMessage, handleConnect } = usePlatformConnections();
|
||||
|
||||
// Initialize integrations data
|
||||
const [integrations] = useState<IntegrationPlatform[]>([
|
||||
@@ -211,6 +197,7 @@ const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateH
|
||||
// Remove query parameters from URL
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Get user email from Clerk
|
||||
@@ -255,6 +242,7 @@ const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateH
|
||||
|
||||
const userEmail = getUserEmail();
|
||||
setEmail(userEmail);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handlePlatformConnect = async (platformId: string) => {
|
||||
|
||||
@@ -15,22 +15,12 @@ import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Divider,
|
||||
Alert,
|
||||
LinearProgress
|
||||
Alert
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AutoAwesome as AutoAwesomeIcon,
|
||||
ContentPaste as ContentIcon,
|
||||
Psychology as PsychologyIcon,
|
||||
TrendingUp as TrendingIcon,
|
||||
Security as SecurityIcon,
|
||||
Speed as SpeedIcon,
|
||||
CheckCircle as CheckIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
Rocket as RocketIcon,
|
||||
DataUsage as DataIcon,
|
||||
Tune as TuneIcon,
|
||||
SmartToy as SmartToyIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Card,
|
||||
CardContent,
|
||||
LinearProgress,
|
||||
Stepper,
|
||||
Step,
|
||||
|
||||
@@ -17,26 +17,18 @@ import {
|
||||
Alert,
|
||||
Button,
|
||||
Slide,
|
||||
Zoom,
|
||||
Divider
|
||||
Zoom
|
||||
} from '@mui/material';
|
||||
import { useTheme, alpha } from '@mui/material/styles';
|
||||
import {
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
CheckCircle as CheckIcon,
|
||||
Info as InfoIcon,
|
||||
Psychology as PsychologyIcon,
|
||||
TrendingUp as TrendingUpIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
Business as BusinessIcon,
|
||||
AutoAwesome as AutoAwesomeIcon,
|
||||
Star as StarIcon,
|
||||
Warning as WarningIcon,
|
||||
Language as LanguageIcon,
|
||||
Web as WebIcon,
|
||||
Palette as PaletteIcon,
|
||||
Speed as SpeedIcon,
|
||||
Group as GroupIcon
|
||||
Warning as WarningIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,7 +110,6 @@ const Wizard: React.FC<WizardProps> = ({ onComplete }) => {
|
||||
// Use refs to avoid dependency cycles
|
||||
const stepDataRef = useRef(stepData);
|
||||
const competitorDataCollectorRef = useRef(competitorDataCollector);
|
||||
const personaStepRef = useRef<{ handleContinue: () => void } | null>(null);
|
||||
|
||||
// Keep refs in sync with state
|
||||
useEffect(() => {
|
||||
|
||||
@@ -10,10 +10,9 @@ import {
|
||||
Tooltip
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Google as GoogleIcon,
|
||||
Refresh as RefreshIcon
|
||||
} from '@mui/icons-material';
|
||||
import { gscAPI, type GSCSite } from '../../../api/gsc';
|
||||
import { type GSCSite } from '../../../api/gsc';
|
||||
|
||||
interface GSCPlatformCardProps {
|
||||
platform: {
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Web as WixIcon,
|
||||
Add as AddIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
Error as ErrorIcon,
|
||||
Refresh as RefreshIcon
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Alert,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
@@ -27,12 +26,10 @@ import {
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Web as WordPressIcon,
|
||||
Add as AddIcon,
|
||||
Delete as DeleteIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
Error as ErrorIcon,
|
||||
Refresh as RefreshIcon,
|
||||
Launch as LaunchIcon
|
||||
Refresh as RefreshIcon
|
||||
} from '@mui/icons-material';
|
||||
import { useWordPressOAuth } from '../../../hooks/useWordPressOAuth';
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ const SEOCopilotContext: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
isLoading,
|
||||
isAnalyzing,
|
||||
isGenerating,
|
||||
error,
|
||||
loadPersonalizationData
|
||||
error
|
||||
} = useSEOCopilotStore();
|
||||
|
||||
const hasLoadedPersonalization = useRef(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SEO CopilotKit Test Component
|
||||
// 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 { useCopilotAction } from '@copilotkit/react-core';
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Typography,
|
||||
Alert,
|
||||
Skeleton,
|
||||
useTheme,
|
||||
Chip,
|
||||
Button
|
||||
} from '@mui/material';
|
||||
@@ -31,8 +30,6 @@ import { userDataAPI } from '../../api/userData';
|
||||
|
||||
// SEO Dashboard component
|
||||
const SEODashboard: React.FC = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
// Clerk authentication hooks
|
||||
const { isSignedIn, isLoaded } = useAuth();
|
||||
const { user } = useUser();
|
||||
|
||||
@@ -60,6 +60,7 @@ const GSCLoginButton: React.FC<GSCLoginButtonProps> = ({ onStatusChange }) => {
|
||||
// Check GSC connection status on component mount
|
||||
useEffect(() => {
|
||||
checkGSCStatus();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const checkGSCStatus = async () => {
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
} from './seoUtils';
|
||||
|
||||
// Components
|
||||
import CategoryCard from './CategoryCard';
|
||||
import CriticalIssueCard from './CriticalIssueCard';
|
||||
import AnalysisTabs from './AnalysisTabs';
|
||||
import IssueDetailsDialog from './IssueDetailsDialog';
|
||||
|
||||
@@ -80,8 +80,6 @@ const CitationHoverHandler: React.FC<CitationHoverHandlerProps> = ({ researchSou
|
||||
modal.style.padding = '18px 20px';
|
||||
|
||||
const title = (src.title || 'Untitled').replace(/</g, '<');
|
||||
const url = (src.url || '').replace(/</g, '<');
|
||||
const sourceType = src.source_type ? String(src.source_type).replace('_', ' ') : '';
|
||||
|
||||
modal.innerHTML =
|
||||
'<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)';
|
||||
|
||||
const title = (src.title || 'Untitled').replace(/</g, '<');
|
||||
const url = (src.url || '').replace(/</g, '<');
|
||||
const sourceType = src.source_type ? String(src.source_type).replace('_', ' ') : '';
|
||||
|
||||
tip.innerHTML =
|
||||
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">' +
|
||||
|
||||
@@ -85,7 +85,7 @@ const ContentDisplayArea: React.FC<ContentDisplayAreaProps> = ({
|
||||
if (draft !== localDraft) {
|
||||
setLocalDraft(draft);
|
||||
}
|
||||
}, [draft]);
|
||||
}, [draft, localDraft]);
|
||||
|
||||
// Cleanup debounced saver
|
||||
useEffect(() => {
|
||||
|
||||
@@ -356,6 +356,7 @@ const ContentPreviewHeaderWithModals: React.FC<ContentPreviewHeaderProps> = (pro
|
||||
window.removeEventListener('showCitationsModal', handleShowCitationsModal as EventListener);
|
||||
window.removeEventListener('showSearchQueriesModal', handleShowSearchQueriesModal as EventListener);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -36,7 +36,6 @@ const MainContentPreviewHeader: React.FC<MainContentPreviewHeaderProps> = ({
|
||||
onAssistantToggle,
|
||||
topic
|
||||
}) => {
|
||||
const formatPercent = (v?: number) => typeof v === 'number' ? `${Math.round(v * 100)}%` : '—';
|
||||
const getChipColor = (v?: number) => {
|
||||
if (typeof v !== 'number') return '#6b7280';
|
||||
if (v >= 0.8) return '#10b981';
|
||||
|
||||
@@ -97,6 +97,7 @@ const PersonaChip: React.FC<PersonaChipProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
fetchPersonaData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [platform, userId]);
|
||||
|
||||
const handleSavePersona = async (data: PersonaData, saveToDatabase: boolean) => {
|
||||
|
||||
@@ -24,7 +24,7 @@ const QuickEditToolbar: React.FC<QuickEditToolbarProps> = ({ draft, isPreviewing
|
||||
const lines = draft.split('\n');
|
||||
if (lines.length > 0) {
|
||||
const first = lines[0].trim();
|
||||
lines[0] = first.replace(/^(.*?)([\.!?])?$/, '👉 $1$2');
|
||||
lines[0] = first.replace(/^(.*?)([.!?])?$/, '👉 $1$2');
|
||||
}
|
||||
const target = lines.join('\n');
|
||||
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
|
||||
@@ -62,7 +62,7 @@ const QuickEditToolbar: React.FC<QuickEditToolbarProps> = ({ draft, isPreviewing
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const target = `[Professionalized]` + '\n\n' + draft;
|
||||
const target = '[Professionalized]\n\n' + draft;
|
||||
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
|
||||
}}
|
||||
style={{ padding: '6px 10px', border: '1px solid #ddd', borderRadius: 6, background: '#fff', cursor: 'pointer' }}
|
||||
|
||||
@@ -2,10 +2,6 @@ import React, { useState, useRef, useEffect } from 'react';
|
||||
import { hallucinationDetectorService, HallucinationDetectionResponse } from '../../services/hallucinationDetectorService';
|
||||
import FactCheckResults from '../LinkedInWriter/components/FactCheckResults';
|
||||
|
||||
interface TextSelectionHandlerProps {
|
||||
contentRef: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) => {
|
||||
const [selectionMenu, setSelectionMenu] = useState<{ x: number; y: number; text: string } | null>(null);
|
||||
const [factCheckResults, setFactCheckResults] = useState<HallucinationDetectionResponse | null>(null);
|
||||
@@ -112,7 +108,7 @@ const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) =>
|
||||
switch (editType) {
|
||||
case 'tighten':
|
||||
// Add hook emoji to the beginning
|
||||
editedText = selectedText.replace(/^(.*?)([\.!?])?$/, '👉 $1$2');
|
||||
editedText = selectedText.replace(/^(.*?)([.!?])?$/, '👉 $1$2');
|
||||
break;
|
||||
case 'add-cta':
|
||||
// Add call-to-action
|
||||
|
||||
@@ -13,12 +13,11 @@ import {
|
||||
Select,
|
||||
MenuItem,
|
||||
Chip,
|
||||
Divider,
|
||||
Link
|
||||
Divider
|
||||
} from '@mui/material';
|
||||
import { apiClient } from '../../api/client';
|
||||
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 {
|
||||
connected: boolean;
|
||||
@@ -49,7 +48,6 @@ const WixTestPage: React.FC = () => {
|
||||
const [publishing, setPublishing] = useState(false);
|
||||
const [categories, setCategories] = useState<BlogCategories | null>(null);
|
||||
const [tags, setTags] = useState<BlogTags | null>(null);
|
||||
const [authUrl, setAuthUrl] = useState<string>('');
|
||||
|
||||
// Blog post form state
|
||||
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
|
||||
sessionStorage.setItem('wix_oauth_data', JSON.stringify(oauthData));
|
||||
const { authUrl } = await wixClient.auth.getAuthUrl(oauthData);
|
||||
setAuthUrl(authUrl);
|
||||
window.location.href = authUrl;
|
||||
} catch (error) {
|
||||
console.error('Failed to start Wix OAuth flow:', error);
|
||||
|
||||
@@ -12,11 +12,7 @@ import {
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
RefreshCw,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
Info
|
||||
} from 'lucide-react';
|
||||
|
||||
@@ -28,7 +24,6 @@ import {
|
||||
formatCurrency,
|
||||
formatNumber,
|
||||
formatPercentage,
|
||||
getUsageStatusColor,
|
||||
getUsageStatusIcon,
|
||||
calculateUsagePercentage
|
||||
} from '../../services/billingService';
|
||||
@@ -53,7 +48,6 @@ const BillingOverview: React.FC<BillingOverviewProps> = ({
|
||||
|
||||
const getStatusChip = () => {
|
||||
const status = usageStats.usage_status;
|
||||
const color = getUsageStatusColor(status);
|
||||
const icon = getUsageStatusIcon(status);
|
||||
|
||||
let chipColor: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -26,9 +26,6 @@ import { billingService } from '../../services/billingService';
|
||||
import { monitoringService } from '../../services/monitoringService';
|
||||
import { onApiEvent } from '../../utils/apiEvents';
|
||||
|
||||
// Components
|
||||
import ComprehensiveAPIBreakdown from './ComprehensiveAPIBreakdown';
|
||||
|
||||
interface CompactBillingDashboardProps {
|
||||
userId?: string;
|
||||
}
|
||||
@@ -38,7 +35,6 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
||||
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
@@ -52,7 +48,6 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
||||
|
||||
setDashboardData(billingData);
|
||||
setSystemHealth(healthData);
|
||||
setLastUpdated(new Date());
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch data');
|
||||
} finally {
|
||||
@@ -62,6 +57,7 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [userId]);
|
||||
|
||||
// Event-driven refresh
|
||||
@@ -77,11 +73,11 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
||||
fetchData();
|
||||
});
|
||||
return unsubscribe;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const formatCurrency = (amount: number) => `$${amount.toFixed(4)}`;
|
||||
const formatNumber = (num: number) => num.toLocaleString();
|
||||
const formatPercentage = (num: number) => `${num.toFixed(1)}%`;
|
||||
|
||||
if (loading && !dashboardData) {
|
||||
return (
|
||||
@@ -118,9 +114,7 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
||||
|
||||
if (!dashboardData) return null;
|
||||
|
||||
const { current_usage, trends, limits, alerts } = dashboardData;
|
||||
const activeProviders = Object.entries(current_usage.provider_breakdown)
|
||||
.filter(([_, data]) => data.cost > 0);
|
||||
const { current_usage, limits, alerts } = dashboardData;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
|
||||
@@ -10,26 +10,15 @@ import {
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
} from '@mui/material';
|
||||
import { ExpandMore } from '@mui/icons-material';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
Info,
|
||||
DollarSign,
|
||||
Activity,
|
||||
Zap,
|
||||
Search,
|
||||
Image,
|
||||
Code,
|
||||
Database,
|
||||
Globe,
|
||||
FileText,
|
||||
BarChart3
|
||||
} from 'lucide-react';
|
||||
|
||||
@@ -8,11 +8,8 @@ import {
|
||||
Chip,
|
||||
} from '@mui/material';
|
||||
import { motion } from 'framer-motion';
|
||||
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts';
|
||||
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';
|
||||
import {
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
BarChart3,
|
||||
PieChart as PieChartIcon
|
||||
} from 'lucide-react';
|
||||
|
||||
|
||||
@@ -3,13 +3,9 @@ import {
|
||||
Box,
|
||||
Container,
|
||||
Grid,
|
||||
Card,
|
||||
CardContent,
|
||||
Typography,
|
||||
Alert,
|
||||
CircularProgress,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
ToggleButton,
|
||||
ToggleButtonGroup,
|
||||
Tooltip,
|
||||
@@ -18,14 +14,6 @@ import {
|
||||
} from '@mui/material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
AlertTriangle,
|
||||
Activity,
|
||||
Zap,
|
||||
BarChart3,
|
||||
PieChart,
|
||||
Clock,
|
||||
Grid3X3,
|
||||
List,
|
||||
Info,
|
||||
@@ -61,11 +49,7 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
|
||||
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('compact');
|
||||
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const fetchDashboardData = async () => {
|
||||
try {
|
||||
@@ -75,7 +59,6 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
|
||||
]);
|
||||
setDashboardData(billingData);
|
||||
setSystemHealth(healthData);
|
||||
setLastUpdated(new Date());
|
||||
} catch (error) {
|
||||
setError(error instanceof Error ? error.message : 'Failed to fetch dashboard data');
|
||||
} finally {
|
||||
@@ -95,7 +78,6 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
|
||||
.then(([billingData, health]) => {
|
||||
setDashboardData(billingData);
|
||||
setSystemHealth(health);
|
||||
setLastUpdated(new Date());
|
||||
})
|
||||
.catch(() => {/* ignore */});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Collapse,
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
import {
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
BarChart3,
|
||||
Calendar
|
||||
} from 'lucide-react';
|
||||
|
||||
|
||||
@@ -13,9 +13,6 @@ import { motion } from 'framer-motion';
|
||||
import {
|
||||
Activity,
|
||||
RefreshCw,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
Clock,
|
||||
Zap
|
||||
} from 'lucide-react';
|
||||
@@ -27,7 +24,6 @@ import { SystemHealth } from '../../types/monitoring';
|
||||
import {
|
||||
getHealthStatusColor,
|
||||
getHealthStatusIcon,
|
||||
formatResponseTime,
|
||||
formatErrorRate,
|
||||
getPerformanceStatus
|
||||
} from '../../services/monitoringService';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
interface ComponentErrorBoundaryProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box, Typography, Chip, Button, CircularProgress, Tooltip } from '@mui/material';
|
||||
import { PlayArrow, Pause, Stop } from '@mui/icons-material';
|
||||
import { Box, Typography, Chip, Button, Tooltip } from '@mui/material';
|
||||
import { PlayArrow } from '@mui/icons-material';
|
||||
import { ShimmerHeader } from './styled';
|
||||
import UserBadge from './UserBadge';
|
||||
import { DashboardHeaderProps } from './types';
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Stack,
|
||||
Alert,
|
||||
Collapse,
|
||||
IconButton,
|
||||
Divider
|
||||
} from '@mui/material';
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, Container, Alert, Button } from '@mui/material';
|
||||
import { Container, Alert, Button } from '@mui/material';
|
||||
import { DashboardContainer } from './styled';
|
||||
import { ErrorDisplayProps } from './types';
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ import { useCopilotReadable } from '@copilotkit/react-core';
|
||||
import {
|
||||
WritingPersona,
|
||||
PlatformAdaptation,
|
||||
PlatformType,
|
||||
UserPersonasResponse
|
||||
PlatformType
|
||||
} from '../../../types/PlatformPersonaTypes';
|
||||
import {
|
||||
getUserPersonas,
|
||||
@@ -37,6 +36,9 @@ interface PlatformPersonaProviderProps {
|
||||
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
|
||||
export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = ({
|
||||
children,
|
||||
@@ -53,9 +55,6 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
const lastRequestTime = useRef<number>(0);
|
||||
const requestInProgress = useRef<boolean>(false);
|
||||
const dataCacheTime = useRef<number>(0);
|
||||
|
||||
// Cache duration: 5 minutes
|
||||
const CACHE_DURATION = 5 * 60 * 1000;
|
||||
|
||||
// Fetch persona data function
|
||||
const fetchPersonas = useCallback(async () => {
|
||||
@@ -253,7 +252,7 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
dataCacheTime.current = Date.now();
|
||||
requestInProgress.current = false;
|
||||
}
|
||||
}, [userId, platform, corePersona]);
|
||||
}, [userId, platform, corePersona, platformPersona]);
|
||||
|
||||
// Initial data fetch
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
interface UserBadgeProps {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
BarChart,
|
||||
Bar,
|
||||
PieChart,
|
||||
Pie,
|
||||
Cell,
|
||||
AreaChart,
|
||||
Area,
|
||||
XAxis,
|
||||
YAxis,
|
||||
|
||||
@@ -121,7 +121,7 @@ function validateContextIntegrity(state: StrategyCalendarState): boolean {
|
||||
|
||||
// Check if calendar context is valid when it exists
|
||||
if (state.calendarContext) {
|
||||
const { strategyContext, autoPopulatedData } = state.calendarContext;
|
||||
const { strategyContext } = state.calendarContext;
|
||||
if (strategyContext && !validateContextIntegrity({ ...state, strategyContext })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ import React from 'react';
|
||||
import {
|
||||
Article as ArticleIcon,
|
||||
Search as SearchIcon,
|
||||
Campaign as CampaignIcon,
|
||||
Analytics as AnalyticsIcon,
|
||||
Psychology as PsychologyIcon,
|
||||
AutoAwesome as AutoAwesomeIcon,
|
||||
Speed as SpeedIcon,
|
||||
Business as BusinessIcon,
|
||||
SocialDistance as SocialIcon,
|
||||
Create as CreateIcon,
|
||||
Dashboard as DashboardIcon,
|
||||
Facebook as FacebookIcon,
|
||||
LinkedIn as LinkedInIcon,
|
||||
|
||||
@@ -40,7 +40,6 @@ export interface UsePersonaPollingReturn {
|
||||
export function usePersonaPolling(options: UsePersonaPollingOptions = {}): UsePersonaPollingReturn {
|
||||
const {
|
||||
interval = 2000, // 2 seconds default
|
||||
maxAttempts = 0, // No timeout - poll until backend says done
|
||||
onProgress,
|
||||
onComplete,
|
||||
onError
|
||||
|
||||
@@ -25,7 +25,6 @@ export function usePolling(
|
||||
): UsePollingReturn {
|
||||
const {
|
||||
interval = 2000, // 2 seconds default
|
||||
maxAttempts = 0, // No timeout - poll until backend says done
|
||||
onProgress,
|
||||
onComplete,
|
||||
onError
|
||||
|
||||
@@ -40,8 +40,6 @@ export const useWixConnection = () => {
|
||||
const tokensRaw = sessionStorage.getItem('wix_tokens');
|
||||
|
||||
if (connectedFlag && tokensRaw) {
|
||||
const tokens = JSON.parse(tokensRaw);
|
||||
|
||||
// Set connected status with site information from tokens
|
||||
setStatus({
|
||||
connected: true,
|
||||
|
||||
@@ -319,7 +319,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
|
||||
|
||||
loadOnboardingIntegration: async (strategyId) => {
|
||||
try {
|
||||
const integration = await contentPlanningApi.getOnboardingIntegration(strategyId);
|
||||
await contentPlanningApi.getOnboardingIntegration(strategyId);
|
||||
// Handle onboarding integration data
|
||||
} catch (error: any) {
|
||||
console.error('Failed to load onboarding integration:', error);
|
||||
|
||||
Reference in New Issue
Block a user