ALwrity + Wix + Wordpress + GSC + Bug Fixes
This commit is contained in:
@@ -1428,7 +1428,7 @@
|
||||
"title": "Personalization",
|
||||
"description": "Set up personalization features",
|
||||
"status": "completed",
|
||||
"completed_at": "2025-10-09T13:05:12.886271",
|
||||
"completed_at": "2025-10-10T08:44:52.292617",
|
||||
"data": {
|
||||
"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.",
|
||||
@@ -2677,7 +2677,7 @@
|
||||
],
|
||||
"current_step": 5,
|
||||
"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,
|
||||
"completed_at": null
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": {
|
||||
"distDir": "build"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "build",
|
||||
"routes": [
|
||||
{
|
||||
"src": "/(.*)",
|
||||
|
||||
Reference in New Issue
Block a user