ALwrity + Wix + Wordpress + GSC + Bug Fixes
This commit is contained in:
@@ -1428,7 +1428,7 @@
|
|||||||
"title": "Personalization",
|
"title": "Personalization",
|
||||||
"description": "Set up personalization features",
|
"description": "Set up personalization features",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"completed_at": "2025-10-09T13:05:12.886271",
|
"completed_at": "2025-10-10T08:44:52.292617",
|
||||||
"data": {
|
"data": {
|
||||||
"corePersona": {
|
"corePersona": {
|
||||||
"analysis_notes": "This persona is generated based on general best practices for professional and engaging content, as no specific website, content, audience, or brand data was provided. The details are inferred to create a functional, albeit generic, persona. For a truly precise and actionable persona, comprehensive input data across all specified categories (website analysis, content insights, audience intelligence, brand voice, technical metrics, competitive analysis, content strategy, research preferences) is essential. The current persona serves as a foundational template that would be significantly refined and customized with actual data.",
|
"analysis_notes": "This persona is generated based on general best practices for professional and engaging content, as no specific website, content, audience, or brand data was provided. The details are inferred to create a functional, albeit generic, persona. For a truly precise and actionable persona, comprehensive input data across all specified categories (website analysis, content insights, audience intelligence, brand voice, technical metrics, competitive analysis, content strategy, research preferences) is essential. The current persona serves as a foundational template that would be significantly refined and customized with actual data.",
|
||||||
@@ -2677,7 +2677,7 @@
|
|||||||
],
|
],
|
||||||
"current_step": 5,
|
"current_step": 5,
|
||||||
"started_at": "2025-09-29T17:22:14.375002",
|
"started_at": "2025-09-29T17:22:14.375002",
|
||||||
"last_updated": "2025-10-09T13:05:12.888068",
|
"last_updated": "2025-10-10T08:44:52.293336",
|
||||||
"is_completed": false,
|
"is_completed": false,
|
||||||
"completed_at": null
|
"completed_at": null
|
||||||
}
|
}
|
||||||
@@ -52,14 +52,14 @@ export const ResearchPollingHandler: React.FC<ResearchPollingHandlerProps> = ({
|
|||||||
} else {
|
} else {
|
||||||
polling.stopPolling();
|
polling.stopPolling();
|
||||||
}
|
}
|
||||||
}, [taskId]);
|
}, [taskId, polling]);
|
||||||
|
|
||||||
// Cleanup on unmount
|
// Cleanup on unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
polling.stopPolling();
|
polling.stopPolling();
|
||||||
};
|
};
|
||||||
}, []);
|
}, [polling]);
|
||||||
|
|
||||||
console.log('ResearchPollingHandler render:', {
|
console.log('ResearchPollingHandler render:', {
|
||||||
taskId,
|
taskId,
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export const RewriteFeedbackForm: React.FC<RewriteFeedbackFormProps> = ({
|
|||||||
onRewriteStarted,
|
onRewriteStarted,
|
||||||
onRewriteTriggered
|
onRewriteTriggered
|
||||||
}) => {
|
}) => {
|
||||||
const [isCollectingFeedback, setIsCollectingFeedback] = useState(false);
|
// Note: isCollectingFeedback state removed as it was unused
|
||||||
|
|
||||||
// Rewrite Blog Action with HITL
|
// Rewrite Blog Action with HITL
|
||||||
useCopilotActionTyped({
|
useCopilotActionTyped({
|
||||||
@@ -303,7 +303,6 @@ export const RewriteFeedbackForm: React.FC<RewriteFeedbackFormProps> = ({
|
|||||||
|
|
||||||
if (result.success && result.taskId) {
|
if (result.success && result.taskId) {
|
||||||
onRewriteStarted?.(result.taskId);
|
onRewriteStarted?.(result.taskId);
|
||||||
setIsCollectingFeedback(false);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -16,21 +16,13 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
Chip,
|
Chip,
|
||||||
Divider,
|
Alert
|
||||||
Alert,
|
|
||||||
IconButton,
|
|
||||||
Tooltip,
|
|
||||||
Button
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
ContentCopy as CopyIcon,
|
|
||||||
Check as CheckIcon,
|
|
||||||
Search as SearchIcon,
|
Search as SearchIcon,
|
||||||
Share as ShareIcon,
|
|
||||||
Code as CodeIcon,
|
Code as CodeIcon,
|
||||||
Facebook as FacebookIcon,
|
Facebook as FacebookIcon,
|
||||||
Twitter as TwitterIcon,
|
Twitter as TwitterIcon,
|
||||||
LinkedIn as LinkedInIcon,
|
|
||||||
Google as GoogleIcon
|
Google as GoogleIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
@@ -43,15 +35,6 @@ export const PreviewCard: React.FC<PreviewCardProps> = ({
|
|||||||
metadata,
|
metadata,
|
||||||
blogTitle
|
blogTitle
|
||||||
}) => {
|
}) => {
|
||||||
const copyToClipboard = async (text: string, itemId: string) => {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(text);
|
|
||||||
// You could add a state to show "Copied!" feedback here
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to copy to clipboard:', err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurrentDate = () => {
|
const getCurrentDate = () => {
|
||||||
return new Date().toLocaleDateString('en-US', {
|
return new Date().toLocaleDateString('en-US', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@@ -60,13 +43,6 @@ export const PreviewCard: React.FC<PreviewCardProps> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentTime = () => {
|
|
||||||
return new Date().toLocaleTimeString('en-US', {
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h6" sx={{ mb: 3, display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Typography variant="h6" sx={{ mb: 3, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
|||||||
@@ -5,11 +5,10 @@
|
|||||||
* Integrates with CopilotKit for real-time progress updates and user interactions.
|
* Integrates with CopilotKit for real-time progress updates and user interactions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
|
||||||
Button,
|
Button,
|
||||||
Chip,
|
Chip,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
@@ -23,7 +22,6 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
Paper,
|
||||||
Divider,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
@@ -33,11 +31,8 @@ import {
|
|||||||
Cancel,
|
Cancel,
|
||||||
Warning,
|
Warning,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
GpsFixed,
|
|
||||||
MenuBook,
|
|
||||||
Search,
|
Search,
|
||||||
BarChart,
|
BarChart,
|
||||||
Lightbulb,
|
|
||||||
Refresh,
|
Refresh,
|
||||||
Close
|
Close
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
@@ -165,7 +160,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
|||||||
// Debug logging
|
// Debug logging
|
||||||
console.log('SEOAnalysisModal render:', { isOpen, blogContent: blogContent?.length, researchData: !!researchData });
|
console.log('SEOAnalysisModal render:', { isOpen, blogContent: blogContent?.length, researchData: !!researchData });
|
||||||
|
|
||||||
const runSEOAnalysis = async () => {
|
const runSEOAnalysis = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsAnalyzing(true);
|
setIsAnalyzing(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -267,7 +262,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
|||||||
setError(err instanceof Error ? err.message : 'Analysis failed');
|
setError(err instanceof Error ? err.message : 'Analysis failed');
|
||||||
setIsAnalyzing(false);
|
setIsAnalyzing(false);
|
||||||
}
|
}
|
||||||
};
|
}, [blogContent, blogTitle, researchData]);
|
||||||
|
|
||||||
const getScoreColor = (score: number) => {
|
const getScoreColor = (score: number) => {
|
||||||
if (score >= 80) return 'success.main';
|
if (score >= 80) return 'success.main';
|
||||||
@@ -281,23 +276,6 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
|||||||
return 'error';
|
return 'error';
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPriorityColor = (priority: string) => {
|
|
||||||
switch (priority) {
|
|
||||||
case 'High': return 'error.main';
|
|
||||||
case 'Medium': return 'warning.main';
|
|
||||||
case 'Low': return 'success.main';
|
|
||||||
default: return 'text.secondary';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPriorityIcon = (priority: string) => {
|
|
||||||
switch (priority) {
|
|
||||||
case 'High': return <Cancel sx={{ fontSize: 16 }} />;
|
|
||||||
case 'Medium': return <Warning sx={{ fontSize: 16 }} />;
|
|
||||||
case 'Low': return <CheckCircle sx={{ fontSize: 16 }} />;
|
|
||||||
default: return <Warning sx={{ fontSize: 16 }} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tooltip content for each metric
|
// Tooltip content for each metric
|
||||||
const getMetricTooltip = (category: string) => {
|
const getMetricTooltip = (category: string) => {
|
||||||
@@ -352,7 +330,7 @@ export const SEOAnalysisModal: React.FC<SEOAnalysisModalProps> = ({
|
|||||||
if (isOpen && !analysisResult) {
|
if (isOpen && !analysisResult) {
|
||||||
runSEOAnalysis();
|
runSEOAnalysis();
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen, analysisResult, runSEOAnalysis]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|||||||
@@ -20,22 +20,13 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab,
|
||||||
Paper,
|
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Alert,
|
Alert,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Chip
|
||||||
Chip,
|
|
||||||
Grid,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Divider,
|
|
||||||
TextField,
|
|
||||||
InputAdornment
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Close as CloseIcon,
|
Close as CloseIcon,
|
||||||
ContentCopy as CopyIcon,
|
|
||||||
Check as CheckIcon,
|
Check as CheckIcon,
|
||||||
Preview as PreviewIcon,
|
Preview as PreviewIcon,
|
||||||
Search as SearchIcon,
|
Search as SearchIcon,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
@@ -12,15 +12,10 @@ import {
|
|||||||
LinearProgress,
|
LinearProgress,
|
||||||
Chip,
|
Chip,
|
||||||
IconButton,
|
IconButton,
|
||||||
Alert,
|
CircularProgress
|
||||||
CircularProgress,
|
|
||||||
Card
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Close as CloseIcon,
|
Close as CloseIcon,
|
||||||
CheckCircle as CheckCircleIcon,
|
|
||||||
Error as ErrorIcon,
|
|
||||||
Refresh as RefreshIcon,
|
|
||||||
Schedule as ScheduleIcon,
|
Schedule as ScheduleIcon,
|
||||||
TrendingUp as TrendingUpIcon,
|
TrendingUp as TrendingUpIcon,
|
||||||
School as SchoolIcon,
|
School as SchoolIcon,
|
||||||
@@ -31,8 +26,7 @@ import {
|
|||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
// Import existing components for reuse
|
// Import existing components for reuse
|
||||||
import DataSourceTransparency from '../DataSourceTransparency';
|
// Note: DataSourceTransparency and ProgressIndicator are imported but may be used by child components
|
||||||
import ProgressIndicator from '../ProgressIndicator';
|
|
||||||
|
|
||||||
// Import panel components
|
// Import panel components
|
||||||
import {
|
import {
|
||||||
@@ -42,57 +36,28 @@ import {
|
|||||||
StepResultsPanel,
|
StepResultsPanel,
|
||||||
EducationalPanel,
|
EducationalPanel,
|
||||||
useCalendarGenerationPolling,
|
useCalendarGenerationPolling,
|
||||||
type CalendarGenerationProgress,
|
|
||||||
type QualityScores
|
type QualityScores
|
||||||
} from './calendarGenerationModalPanels';
|
} from './calendarGenerationModalPanels';
|
||||||
|
|
||||||
// Import new StepProgressTracker component
|
// Import new StepProgressTracker component
|
||||||
import StepProgressTracker from './calendarGenerationModalPanels/StepProgressTracker';
|
import StepProgressTracker from './calendarGenerationModalPanels/StepProgressTracker';
|
||||||
|
|
||||||
// Import styles
|
// Import styles (only used ones)
|
||||||
import {
|
import {
|
||||||
dialogStyles,
|
dialogStyles,
|
||||||
contentContainerStyles,
|
|
||||||
progressBarContainerStyles,
|
progressBarContainerStyles,
|
||||||
progressBarStyles,
|
progressBarStyles,
|
||||||
stepProgressBarStyles,
|
|
||||||
getStepIndicatorStyles,
|
getStepIndicatorStyles,
|
||||||
getStepCardStyles,
|
|
||||||
stepCircleBaseStyles,
|
|
||||||
getStepCircleColor,
|
|
||||||
tabButtonStyles,
|
tabButtonStyles,
|
||||||
activityIndicatorStyles,
|
|
||||||
qualityScoreContainerStyles,
|
|
||||||
getQualityScoreBackground,
|
|
||||||
qualityScoreInnerStyles,
|
|
||||||
dataSourceCardStyles,
|
|
||||||
dataSourceIconStyles,
|
|
||||||
getDataSourceIconColor,
|
|
||||||
qualityMetricsContainerStyles,
|
|
||||||
getMetricColor,
|
|
||||||
stepResultsCardStyles,
|
|
||||||
stepResultsHeaderStyles,
|
|
||||||
stepResultsContentStyles,
|
|
||||||
loadingContainerStyles,
|
loadingContainerStyles,
|
||||||
loadingContentStyles,
|
loadingContentStyles,
|
||||||
animationDurations,
|
animationDurations,
|
||||||
animationEasing,
|
animationEasing,
|
||||||
springConfig,
|
|
||||||
staggerDelay,
|
|
||||||
cardStaggerDelay,
|
|
||||||
fadeInUp,
|
|
||||||
fadeInLeft,
|
fadeInLeft,
|
||||||
scaleIn,
|
|
||||||
slideInStaggered,
|
|
||||||
hoverLift,
|
|
||||||
hoverScale,
|
hoverScale,
|
||||||
tapScale,
|
tapScale,
|
||||||
pulseAnimation,
|
pulseAnimation,
|
||||||
smallPulseAnimation,
|
progressOverlayStyles
|
||||||
colorPulseAnimation,
|
|
||||||
progressFillAnimation,
|
|
||||||
progressOverlayStyles,
|
|
||||||
stepProgressOverlayStyles
|
|
||||||
} from './CalendarGenerationModal.styles';
|
} from './CalendarGenerationModal.styles';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -262,19 +227,11 @@ const CalendarGenerationModal: React.FC<CalendarGenerationModalProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
const [educationalPanelExpanded, setEducationalPanelExpanded] = useState(false);
|
const [educationalPanelExpanded, setEducationalPanelExpanded] = useState(false);
|
||||||
const [expandedSections, setExpandedSections] = useState({
|
|
||||||
dataSources: true,
|
|
||||||
progress: true,
|
|
||||||
educational: false,
|
|
||||||
messages: true,
|
|
||||||
stepResults: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Use polling hook for real backend data only
|
// Use polling hook for real backend data only
|
||||||
const {
|
const {
|
||||||
progress,
|
progress,
|
||||||
isPolling,
|
isPolling,
|
||||||
error,
|
|
||||||
startPolling,
|
startPolling,
|
||||||
stopPolling,
|
stopPolling,
|
||||||
getStepStatus,
|
getStepStatus,
|
||||||
@@ -309,11 +266,7 @@ const CalendarGenerationModal: React.FC<CalendarGenerationModalProps> = ({
|
|||||||
console.log('❌ Calendar generation error:', currentProgress.errors);
|
console.log('❌ Calendar generation error:', currentProgress.errors);
|
||||||
onError(currentProgress.errors[0]?.message || 'Unknown error');
|
onError(currentProgress.errors[0]?.message || 'Unknown error');
|
||||||
}
|
}
|
||||||
}, [currentProgress?.status, currentProgress?.errors, onError]);
|
}, [currentProgress, onError]);
|
||||||
|
|
||||||
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
|
||||||
setActiveTab(newValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getQualityColor = (score: number) => {
|
const getQualityColor = (score: number) => {
|
||||||
if (score >= 0.9) return 'success';
|
if (score >= 0.9) return 'success';
|
||||||
|
|||||||
@@ -10,10 +10,8 @@ import {
|
|||||||
InputLabel,
|
InputLabel,
|
||||||
Select,
|
Select,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
TextField,
|
|
||||||
Slider,
|
Slider,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Checkbox,
|
|
||||||
Alert,
|
Alert,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -23,12 +21,10 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
AutoAwesome as AIIcon,
|
AutoAwesome as AIIcon,
|
||||||
Speed as SpeedIcon,
|
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
TrendingUp as TrendingIcon,
|
TrendingUp as TrendingIcon,
|
||||||
Psychology as PsychologyIcon,
|
Psychology as PsychologyIcon,
|
||||||
Info as InfoIcon,
|
Info as InfoIcon,
|
||||||
Settings as SettingsIcon,
|
|
||||||
Assessment as AssessmentIcon
|
Assessment as AssessmentIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
|
|||||||
@@ -181,8 +181,6 @@ const CalendarConfigurationStep: React.FC<CalendarConfigurationStepProps> = ({
|
|||||||
const [userGuidance, setUserGuidance] = useState<UserGuidance | null>(null);
|
const [userGuidance, setUserGuidance] = useState<UserGuidance | null>(null);
|
||||||
const [transparencyIndicators, setTransparencyIndicators] = useState<TransparencyIndicators | null>(null);
|
const [transparencyIndicators, setTransparencyIndicators] = useState<TransparencyIndicators | null>(null);
|
||||||
const [showSmartDefaults, setShowSmartDefaults] = useState(true);
|
const [showSmartDefaults, setShowSmartDefaults] = useState(true);
|
||||||
const [showUserGuidance, setShowUserGuidance] = useState(true);
|
|
||||||
const [showTransparency, setShowTransparency] = useState(true);
|
|
||||||
|
|
||||||
// Generate smart defaults and guidance when strategy context changes
|
// Generate smart defaults and guidance when strategy context changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -245,6 +243,8 @@ const CalendarConfigurationStep: React.FC<CalendarConfigurationStepProps> = ({
|
|||||||
// Fix invalid timezone
|
// Fix invalid timezone
|
||||||
onConfigUpdate({ timeZone: 'America/New_York' });
|
onConfigUpdate({ timeZone: 'America/New_York' });
|
||||||
}
|
}
|
||||||
|
// timeZones is a const defined later in this component, safe to exclude
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [calendarConfig.timeZone, onConfigUpdate]);
|
}, [calendarConfig.timeZone, onConfigUpdate]);
|
||||||
|
|
||||||
// Apply smart defaults to configuration
|
// Apply smart defaults to configuration
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -6,60 +6,19 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Button,
|
Button,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
Alert,
|
|
||||||
Chip,
|
|
||||||
IconButton,
|
|
||||||
Tooltip as MuiTooltip,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Grid,
|
Grid,
|
||||||
Divider,
|
|
||||||
CircularProgress,
|
|
||||||
Badge,
|
|
||||||
Collapse,
|
|
||||||
Accordion,
|
|
||||||
AccordionSummary,
|
|
||||||
AccordionDetails,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogActions
|
DialogActions
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Business as BusinessIcon,
|
|
||||||
People as PeopleIcon,
|
|
||||||
TrendingUp as TrendingUpIcon,
|
|
||||||
ContentPaste as ContentIcon,
|
|
||||||
Analytics as AnalyticsIcon,
|
|
||||||
Help as HelpIcon,
|
|
||||||
CheckCircle as CheckCircleIcon,
|
|
||||||
Warning as WarningIcon,
|
|
||||||
AutoAwesome as AutoAwesomeIcon,
|
AutoAwesome as AutoAwesomeIcon,
|
||||||
Refresh as RefreshIcon,
|
Info as InfoIcon
|
||||||
Save as SaveIcon,
|
|
||||||
ArrowForward as ArrowForwardIcon,
|
|
||||||
ArrowBack as ArrowBackIcon,
|
|
||||||
Assessment as AssessmentIcon,
|
|
||||||
ExpandMore as ExpandMoreIcon,
|
|
||||||
Info as InfoIcon,
|
|
||||||
Visibility as VisibilityIcon,
|
|
||||||
School as SchoolIcon,
|
|
||||||
Lightbulb as LightbulbIcon,
|
|
||||||
Psychology as PsychologyIcon,
|
|
||||||
Timeline as TimelineIcon,
|
|
||||||
FiberManualRecord as FiberManualRecordIcon,
|
|
||||||
Schedule as ScheduleIcon
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
|
||||||
import { useStrategyBuilderStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/strategyBuilderStore';
|
import { useStrategyBuilderStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/strategyBuilderStore';
|
||||||
import { useEnhancedStrategyStore } from '../../../stores/enhancedStrategyStore';
|
import { useEnhancedStrategyStore } from '../../../stores/enhancedStrategyStore';
|
||||||
import StrategicInputField from './ContentStrategyBuilder/StrategicInputField';
|
|
||||||
import EnhancedTooltip from './ContentStrategyBuilder/EnhancedTooltip';
|
import EnhancedTooltip from './ContentStrategyBuilder/EnhancedTooltip';
|
||||||
import AIRecommendationsPanel from './AIRecommendationsPanel';
|
|
||||||
import DataSourceTransparency from './DataSourceTransparency';
|
import DataSourceTransparency from './DataSourceTransparency';
|
||||||
import StrategyAutofillTransparencyModal from './StrategyAutofillTransparencyModal';
|
import StrategyAutofillTransparencyModal from './StrategyAutofillTransparencyModal';
|
||||||
import EnterpriseDatapointsModal from './EnterpriseDatapointsModal';
|
import EnterpriseDatapointsModal from './EnterpriseDatapointsModal';
|
||||||
@@ -79,7 +38,7 @@ import { useStrategyCreation } from './ContentStrategyBuilder/hooks/useStrategyC
|
|||||||
import { useCopilotReadable, useCopilotAdditionalInstructions } from "@copilotkit/react-core";
|
import { useCopilotReadable, useCopilotAdditionalInstructions } from "@copilotkit/react-core";
|
||||||
|
|
||||||
// Import extracted utilities
|
// Import extracted utilities
|
||||||
import { getCategoryIcon, getCategoryColor, getCategoryName, getCategoryStatus } from './ContentStrategyBuilder/utils/categoryHelpers';
|
import { getCategoryIcon, getCategoryColor } from './ContentStrategyBuilder/utils/categoryHelpers';
|
||||||
import { getEducationalContent } from './ContentStrategyBuilder/utils/educationalContent';
|
import { getEducationalContent } from './ContentStrategyBuilder/utils/educationalContent';
|
||||||
import { setupCSSAnimations, cleanupCSSAnimations } from './ContentStrategyBuilder/utils/cssAnimations';
|
import { setupCSSAnimations, cleanupCSSAnimations } from './ContentStrategyBuilder/utils/cssAnimations';
|
||||||
|
|
||||||
@@ -115,7 +74,6 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
updateFormField,
|
updateFormField,
|
||||||
validateFormField,
|
validateFormField,
|
||||||
validateAllFields,
|
validateAllFields,
|
||||||
resetForm,
|
|
||||||
autoPopulateFromOnboarding,
|
autoPopulateFromOnboarding,
|
||||||
createStrategy: createEnhancedStrategy,
|
createStrategy: createEnhancedStrategy,
|
||||||
calculateCompletionPercentage,
|
calculateCompletionPercentage,
|
||||||
@@ -128,9 +86,6 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
// Enhanced Strategy Store (for AI analysis, progressive disclosure, transparency)
|
// Enhanced Strategy Store (for AI analysis, progressive disclosure, transparency)
|
||||||
const {
|
const {
|
||||||
aiGenerating,
|
aiGenerating,
|
||||||
currentStep,
|
|
||||||
completedSteps,
|
|
||||||
disclosureSteps,
|
|
||||||
transparencyModalOpen,
|
transparencyModalOpen,
|
||||||
transparencyGenerationProgress: storeGenerationProgress,
|
transparencyGenerationProgress: storeGenerationProgress,
|
||||||
currentPhase,
|
currentPhase,
|
||||||
@@ -144,11 +99,6 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
addTransparencyMessage,
|
addTransparencyMessage,
|
||||||
clearTransparencyMessages,
|
clearTransparencyMessages,
|
||||||
setTransparencyGenerating: setIsGenerating,
|
setTransparencyGenerating: setIsGenerating,
|
||||||
completeStep,
|
|
||||||
getNextStep,
|
|
||||||
getPreviousStep,
|
|
||||||
setCurrentStep,
|
|
||||||
canProceedToDisclosureStep: canProceedToStep,
|
|
||||||
generateAIRecommendations,
|
generateAIRecommendations,
|
||||||
setAIGenerating
|
setAIGenerating
|
||||||
} = useEnhancedStrategyStore();
|
} = useEnhancedStrategyStore();
|
||||||
@@ -157,7 +107,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
useCopilotActions();
|
useCopilotActions();
|
||||||
|
|
||||||
// Check if this component is currently visible (active tab)
|
// Check if this component is currently visible (active tab)
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [, setIsVisible] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Use a small delay to ensure the component is actually rendered
|
// Use a small delay to ensure the component is actually rendered
|
||||||
@@ -171,10 +121,8 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [showAIRecommendations, setShowAIRecommendations] = useState(false);
|
const [, setShowAIRecommendations] = useState(false);
|
||||||
const [showDataSourceTransparency, setShowDataSourceTransparency] = useState(false);
|
const [showDataSourceTransparency, setShowDataSourceTransparency] = useState(false);
|
||||||
const [localEducationalContent, setLocalEducationalContent] = useState<any>(null);
|
|
||||||
const [localGenerationProgress, setLocalGenerationProgress] = useState<number>(0);
|
|
||||||
const [showAIRecModal, setShowAIRecModal] = useState(false);
|
const [showAIRecModal, setShowAIRecModal] = useState(false);
|
||||||
|
|
||||||
// Ref to track if we've already set the default category
|
// Ref to track if we've already set the default category
|
||||||
@@ -355,13 +303,9 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
refreshMessage,
|
refreshMessage,
|
||||||
setRefreshMessage,
|
|
||||||
refreshProgress,
|
refreshProgress,
|
||||||
setRefreshProgress,
|
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
setIsRefreshing,
|
|
||||||
refreshError,
|
refreshError,
|
||||||
setRefreshError,
|
|
||||||
handleAIRefresh
|
handleAIRefresh
|
||||||
} = useAIRefresh({
|
} = useAIRefresh({
|
||||||
setTransparencyModalOpen,
|
setTransparencyModalOpen,
|
||||||
@@ -374,18 +318,16 @@ const ContentStrategyBuilder: React.FC = () => {
|
|||||||
setError
|
setError
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// getCompletionStats depends on formData, so we include it to recalculate when data changes
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
const completionStats = useMemo(() => getCompletionStats(), [formData]);
|
const completionStats = useMemo(() => getCompletionStats(), [formData]);
|
||||||
const completionPercentage = useMemo(() => calculateCompletionPercentage(), [formData]);
|
|
||||||
|
|
||||||
// Use extracted hooks
|
// Use extracted hooks
|
||||||
const {
|
const {
|
||||||
reviewedCategories,
|
reviewedCategories,
|
||||||
isMarkingReviewed,
|
isMarkingReviewed,
|
||||||
categoryCompletionMessage,
|
categoryCompletionMessage,
|
||||||
handleConfirmCategoryReview,
|
handleConfirmCategoryReview
|
||||||
isCategoryReviewed,
|
|
||||||
getNextUnreviewedCategory,
|
|
||||||
setReviewedCategories
|
|
||||||
} = useCategoryReview({ completionStats, setError, setActiveCategory });
|
} = useCategoryReview({ completionStats, setError, setActiveCategory });
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ export const useCopilotActions = () => {
|
|||||||
updateFormField,
|
updateFormField,
|
||||||
validateFormField,
|
validateFormField,
|
||||||
setError,
|
setError,
|
||||||
autoPopulatedFields,
|
|
||||||
dataSources,
|
|
||||||
calculateCompletionPercentage,
|
calculateCompletionPercentage,
|
||||||
getCompletionStats
|
getCompletionStats
|
||||||
} = useStrategyBuilderStore();
|
} = useStrategyBuilderStore();
|
||||||
@@ -93,7 +91,7 @@ export const useCopilotActions = () => {
|
|||||||
console.log(`📝 Populating field ${fieldId} with value: ${value}`);
|
console.log(`📝 Populating field ${fieldId} with value: ${value}`);
|
||||||
|
|
||||||
// Call backend API for intelligent field population
|
// Call backend API for intelligent field population
|
||||||
const response = await contentPlanningApi.generateCategoryData(
|
await contentPlanningApi.generateCategoryData(
|
||||||
'individual_field',
|
'individual_field',
|
||||||
`Populate ${fieldId} with: ${value}. Reasoning: ${reasoning || 'User request'}`,
|
`Populate ${fieldId} with: ${value}. Reasoning: ${reasoning || 'User request'}`,
|
||||||
formData
|
formData
|
||||||
@@ -188,7 +186,7 @@ export const useCopilotActions = () => {
|
|||||||
setTransparencyGenerating(false);
|
setTransparencyGenerating(false);
|
||||||
return { success: false, message: error.message || 'Unknown error' };
|
return { success: false, message: error.message || 'Unknown error' };
|
||||||
}
|
}
|
||||||
}, [formData, updateFormField, setError, calculateCompletionPercentage, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating]);
|
}, [formData, updateFormField, setError, calculateCompletionPercentage, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating, triggerTransparencyFlow]);
|
||||||
|
|
||||||
// Action 4: Validate field
|
// Action 4: Validate field
|
||||||
const validateStrategyField = useCallback(async ({ fieldId }: any) => {
|
const validateStrategyField = useCallback(async ({ fieldId }: any) => {
|
||||||
@@ -301,11 +299,7 @@ export const useCopilotActions = () => {
|
|||||||
// Start transparency flow (same as Refresh & Autofill button)
|
// Start transparency flow (same as Refresh & Autofill button)
|
||||||
const { transparencyInterval } = await triggerTransparencyFlow('autofill', 'Auto-population from onboarding data');
|
const { transparencyInterval } = await triggerTransparencyFlow('autofill', 'Auto-population from onboarding data');
|
||||||
|
|
||||||
// Get current form data to see what's already filled
|
// Get empty fields that need to be filled
|
||||||
const currentFilledFields = Object.keys(formData).filter(key => {
|
|
||||||
const value = formData[key];
|
|
||||||
return value && typeof value === 'string' && value.trim() !== '';
|
|
||||||
});
|
|
||||||
const emptyFields = Object.keys(formData).filter(key => {
|
const emptyFields = Object.keys(formData).filter(key => {
|
||||||
const value = formData[key];
|
const value = formData[key];
|
||||||
return !value || typeof value !== 'string' || value.trim() === '';
|
return !value || typeof value !== 'string' || value.trim() === '';
|
||||||
@@ -429,7 +423,7 @@ export const useCopilotActions = () => {
|
|||||||
setTransparencyGenerating(false);
|
setTransparencyGenerating(false);
|
||||||
return { success: false, message: error.message || 'Unknown error' };
|
return { success: false, message: error.message || 'Unknown error' };
|
||||||
}
|
}
|
||||||
}, [formData, updateFormField, calculateCompletionPercentage, setError, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating]);
|
}, [formData, updateFormField, calculateCompletionPercentage, setError, setTransparencyModalOpen, setTransparencyGenerating, setTransparencyGenerationProgress, setCurrentPhase, clearTransparencyMessages, addTransparencyMessage, setAIGenerating, triggerTransparencyFlow]);
|
||||||
|
|
||||||
// Call useCopilotAction hooks unconditionally - they will handle context availability internally
|
// Call useCopilotAction hooks unconditionally - they will handle context availability internally
|
||||||
// This is the only way to comply with React hooks rules
|
// This is the only way to comply with React hooks rules
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
Alert,
|
Alert,
|
||||||
AlertTitle,
|
AlertTitle,
|
||||||
CircularProgress,
|
|
||||||
LinearProgress,
|
|
||||||
Divider,
|
Divider,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -24,13 +22,7 @@ import {
|
|||||||
ListItemIcon
|
ListItemIcon
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
TrendingUp as TrendingUpIcon,
|
|
||||||
TrendingDown as TrendingDownIcon,
|
|
||||||
Assessment as AssessmentIcon,
|
Assessment as AssessmentIcon,
|
||||||
Speed as SpeedIcon,
|
|
||||||
Visibility as VisibilityIcon,
|
|
||||||
People as EngagementIcon,
|
|
||||||
MonetizationOn as MonetizationOnIcon,
|
|
||||||
Refresh as RefreshIcon,
|
Refresh as RefreshIcon,
|
||||||
AutoAwesome as AutoAwesomeIcon,
|
AutoAwesome as AutoAwesomeIcon,
|
||||||
CheckCircle as CheckCircleIcon,
|
CheckCircle as CheckCircleIcon,
|
||||||
@@ -38,11 +30,10 @@ import {
|
|||||||
Error as ErrorIcon,
|
Error as ErrorIcon,
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
Lightbulb as LightbulbIcon,
|
Lightbulb as LightbulbIcon,
|
||||||
Timeline as TimelineIcon,
|
|
||||||
Close as CloseIcon
|
Close as CloseIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { safeRenderText, safeRenderArray, hasValidData, getFallbackValue } from '../utils/defensiveRendering';
|
import { safeRenderText } from '../utils/defensiveRendering';
|
||||||
|
|
||||||
// Import our advanced chart components
|
// Import our advanced chart components
|
||||||
import {
|
import {
|
||||||
@@ -56,9 +47,6 @@ import {
|
|||||||
// Import real-time data hook
|
// Import real-time data hook
|
||||||
import { useMockRealTimeData } from '../../../../../hooks/useRealTimeData';
|
import { useMockRealTimeData } from '../../../../../hooks/useRealTimeData';
|
||||||
|
|
||||||
// Import API services
|
|
||||||
import { strategyMonitoringApi } from '../../../../../services/strategyMonitoringApi';
|
|
||||||
|
|
||||||
interface EnhancedPerformanceVisualizationProps {
|
interface EnhancedPerformanceVisualizationProps {
|
||||||
strategyId: number;
|
strategyId: number;
|
||||||
strategyData: any;
|
strategyData: any;
|
||||||
@@ -88,7 +76,7 @@ const EnhancedPerformanceVisualization: React.FC<EnhancedPerformanceVisualizatio
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
// Use real-time data hook
|
// Use real-time data hook
|
||||||
const { data: realTimeData, isConnected, error: realTimeError } = useMockRealTimeData(strategyId);
|
const { data: realTimeData, isConnected } = useMockRealTimeData(strategyId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadQualityAnalysis();
|
loadQualityAnalysis();
|
||||||
|
|||||||
@@ -5,21 +5,16 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Chip,
|
Chip,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Alert,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
Button,
|
Button,
|
||||||
Grid,
|
|
||||||
Paper,
|
|
||||||
LinearProgress,
|
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
Divider,
|
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
@@ -33,14 +28,8 @@ import {
|
|||||||
Help as UnknownIcon,
|
Help as UnknownIcon,
|
||||||
Refresh as RefreshIcon,
|
Refresh as RefreshIcon,
|
||||||
Close as CloseIcon,
|
Close as CloseIcon,
|
||||||
TrendingUp as TrendingUpIcon,
|
|
||||||
TrendingDown as TrendingDownIcon,
|
|
||||||
Speed as SpeedIcon,
|
|
||||||
BugReport as BugReportIcon,
|
BugReport as BugReportIcon,
|
||||||
Storage as StorageIcon,
|
Analytics as AnalyticsIcon
|
||||||
Timeline as TimelineIcon,
|
|
||||||
Analytics as AnalyticsIcon,
|
|
||||||
NetworkCheck as NetworkCheckIcon
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import MonitoringCharts from './MonitoringCharts';
|
import MonitoringCharts from './MonitoringCharts';
|
||||||
@@ -95,10 +84,10 @@ const SystemStatusIndicator: React.FC<SystemStatusIndicatorProps> = ({ className
|
|||||||
const [statusData, setStatusData] = useState<SystemStatusData | null>(null);
|
const [statusData, setStatusData] = useState<SystemStatusData | null>(null);
|
||||||
const [detailedStats, setDetailedStats] = useState<DetailedStatsData | null>(null);
|
const [detailedStats, setDetailedStats] = useState<DetailedStatsData | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [, setError] = useState<string | null>(null);
|
||||||
const [dashboardOpen, setDashboardOpen] = useState(false);
|
const [dashboardOpen, setDashboardOpen] = useState(false);
|
||||||
const [chartData, setChartData] = useState<any[]>([]);
|
const [chartData, setChartData] = useState<any[]>([]);
|
||||||
const [cachePerf, setCachePerf] = useState<{ hits: number; misses: number; hit_rate: number } | null>(null);
|
const [, setCachePerf] = useState<{ hits: number; misses: number; hit_rate: number } | null>(null);
|
||||||
|
|
||||||
const fetchStatus = async () => {
|
const fetchStatus = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -243,10 +232,6 @@ const SystemStatusIndicator: React.FC<SystemStatusIndicatorProps> = ({ className
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const total = statusData?.recent_requests ?? 0;
|
|
||||||
const failed = statusData?.recent_errors ?? 0;
|
|
||||||
const passed = Math.max(0, total - failed);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Tooltip title={tooltipContent} arrow placement="bottom">
|
<Tooltip title={tooltipContent} arrow placement="bottom">
|
||||||
|
|||||||
@@ -29,10 +29,7 @@ import {
|
|||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
Divider,
|
LinearProgress
|
||||||
LinearProgress,
|
|
||||||
Tooltip,
|
|
||||||
Badge
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Add as AddIcon,
|
Add as AddIcon,
|
||||||
@@ -42,27 +39,17 @@ import {
|
|||||||
Event as EventIcon,
|
Event as EventIcon,
|
||||||
Refresh as RefreshIcon,
|
Refresh as RefreshIcon,
|
||||||
TrendingUp as TrendingIcon,
|
TrendingUp as TrendingIcon,
|
||||||
ContentCopy as RepurposeIcon,
|
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
ExpandMore as ExpandMoreIcon,
|
ExpandMore as ExpandMoreIcon,
|
||||||
Schedule as ScheduleIcon,
|
Schedule as ScheduleIcon,
|
||||||
Psychology as PsychologyIcon,
|
Psychology as PsychologyIcon,
|
||||||
Business as BusinessIcon,
|
Business as BusinessIcon,
|
||||||
Group as GroupIcon,
|
|
||||||
Timeline as TimelineIcon,
|
Timeline as TimelineIcon,
|
||||||
Lightbulb as LightbulbIcon,
|
Lightbulb as LightbulbIcon,
|
||||||
CheckCircle as CheckCircleIcon,
|
CheckCircle as CheckCircleIcon,
|
||||||
Warning as WarningIcon,
|
|
||||||
Info as InfoIcon,
|
|
||||||
DataUsage as DataUsageIcon,
|
|
||||||
Insights as InsightsIcon,
|
|
||||||
Assessment as AssessmentIcon,
|
|
||||||
Campaign as CampaignIcon,
|
|
||||||
Speed as SpeedIcon,
|
|
||||||
AutoAwesome as AutoAwesomeIcon
|
AutoAwesome as AutoAwesomeIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
||||||
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -92,15 +79,10 @@ const CalendarTab: React.FC = () => {
|
|||||||
createEvent,
|
createEvent,
|
||||||
updateEvent,
|
updateEvent,
|
||||||
deleteEvent,
|
deleteEvent,
|
||||||
loading,
|
|
||||||
error,
|
error,
|
||||||
loadCalendarEvents,
|
loadCalendarEvents,
|
||||||
updateCalendarEvents,
|
|
||||||
// New calendar generation state
|
// New calendar generation state
|
||||||
generatedCalendar,
|
generatedCalendar,
|
||||||
performancePrediction,
|
|
||||||
contentRepurposing,
|
|
||||||
aiInsights,
|
|
||||||
calendarGenerationError,
|
calendarGenerationError,
|
||||||
dataLoading,
|
dataLoading,
|
||||||
calendarGenerationLoading
|
calendarGenerationLoading
|
||||||
@@ -118,28 +100,15 @@ const CalendarTab: React.FC = () => {
|
|||||||
status: 'draft' as 'draft' | 'scheduled' | 'published'
|
status: 'draft' as 'draft' | 'scheduled' | 'published'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enhanced state for data transparency
|
|
||||||
const [userData, setUserData] = useState<any>({
|
|
||||||
onboardingData: {},
|
|
||||||
gapAnalysis: {},
|
|
||||||
strategyData: {},
|
|
||||||
recommendationsData: [],
|
|
||||||
performanceData: {},
|
|
||||||
aiAnalysisResults: []
|
|
||||||
});
|
|
||||||
|
|
||||||
const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
|
const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadCalendarData();
|
loadCalendarData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadCalendarData = async () => {
|
const loadCalendarData = async () => {
|
||||||
try {
|
try {
|
||||||
// Load comprehensive user data for calendar generation
|
|
||||||
const comprehensiveData = await contentPlanningApi.getComprehensiveUserData(1); // Pass user ID
|
|
||||||
setUserData(comprehensiveData.data); // Extract the data from the response
|
|
||||||
|
|
||||||
// Load existing calendar events
|
// Load existing calendar events
|
||||||
await loadCalendarEvents();
|
await loadCalendarEvents();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -211,45 +180,6 @@ const CalendarTab: React.FC = () => {
|
|||||||
await loadCalendarData();
|
await loadCalendarData();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDataUpdate = (updatedData: any) => {
|
|
||||||
setUserData((prev: any) => ({ ...prev, ...updatedData }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGenerateCalendar = async (calendarConfig: any) => {
|
|
||||||
try {
|
|
||||||
await contentPlanningApi.generateComprehensiveCalendar({
|
|
||||||
...calendarConfig,
|
|
||||||
userData
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error generating calendar:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOptimizeContent = async (contentData: any) => {
|
|
||||||
try {
|
|
||||||
await contentPlanningApi.optimizeContent(contentData);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error optimizing content:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePredictPerformance = async (contentData: any) => {
|
|
||||||
try {
|
|
||||||
await contentPlanningApi.predictPerformance(contentData);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error predicting performance:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGetTrendingTopics = async () => {
|
|
||||||
try {
|
|
||||||
await contentPlanningApi.getTrendingTopics({ user_id: 1, industry: 'technology' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting trending topics:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'draft': return 'default';
|
case 'draft': return 'default';
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import {
|
|||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
ListItemIcon,
|
ListItemIcon
|
||||||
Chip
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
CircularProgress
|
CircularProgress
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
|
||||||
PieChart as PieChartIcon
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
||||||
|
|
||||||
const ContentPillarsTab: React.FC = () => {
|
const ContentPillarsTab: React.FC = () => {
|
||||||
@@ -21,6 +18,7 @@ const ContentPillarsTab: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadContentPillars();
|
loadContentPillars();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [currentStrategy]);
|
}, [currentStrategy]);
|
||||||
|
|
||||||
const loadContentPillars = async () => {
|
const loadContentPillars = async () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Paper,
|
Paper,
|
||||||
@@ -25,25 +25,13 @@ const ContentStrategyTab: React.FC = () => {
|
|||||||
const strategies = useContentPlanningStore(state => state.strategies);
|
const strategies = useContentPlanningStore(state => state.strategies);
|
||||||
const currentStrategy = useContentPlanningStore(state => state.currentStrategy);
|
const currentStrategy = useContentPlanningStore(state => state.currentStrategy);
|
||||||
const latestGeneratedStrategy = useContentPlanningStore(state => state.latestGeneratedStrategy);
|
const latestGeneratedStrategy = useContentPlanningStore(state => state.latestGeneratedStrategy);
|
||||||
const aiInsights = useContentPlanningStore(state => state.aiInsights);
|
|
||||||
const aiRecommendations = useContentPlanningStore(state => state.aiRecommendations);
|
|
||||||
const loading = useContentPlanningStore(state => state.loading);
|
|
||||||
const error = useContentPlanningStore(state => state.error);
|
const error = useContentPlanningStore(state => state.error);
|
||||||
const loadStrategies = useContentPlanningStore(state => state.loadStrategies);
|
const loadStrategies = useContentPlanningStore(state => state.loadStrategies);
|
||||||
const loadAIInsights = useContentPlanningStore(state => state.loadAIInsights);
|
const loadAIInsights = useContentPlanningStore(state => state.loadAIInsights);
|
||||||
const loadAIRecommendations = useContentPlanningStore(state => state.loadAIRecommendations);
|
const loadAIRecommendations = useContentPlanningStore(state => state.loadAIRecommendations);
|
||||||
const setLatestGeneratedStrategy = useContentPlanningStore(state => state.setLatestGeneratedStrategy);
|
const setLatestGeneratedStrategy = useContentPlanningStore(state => state.setLatestGeneratedStrategy);
|
||||||
|
|
||||||
const [strategyForm, setStrategyForm] = useState({
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
industry: '',
|
|
||||||
target_audience: '',
|
|
||||||
content_pillars: []
|
|
||||||
});
|
|
||||||
|
|
||||||
// Real data states
|
// Real data states
|
||||||
const [strategicIntelligence, setStrategicIntelligence] = useState<any>(null);
|
|
||||||
const [strategyData, setStrategyData] = useState<StrategyData | null>(null);
|
const [strategyData, setStrategyData] = useState<StrategyData | null>(null);
|
||||||
const [strategyDataLoading, setStrategyDataLoading] = useState(false);
|
const [strategyDataLoading, setStrategyDataLoading] = useState(false);
|
||||||
const [strategyDataError, setStrategyDataError] = useState<string | null>(null);
|
const [strategyDataError, setStrategyDataError] = useState<string | null>(null);
|
||||||
@@ -65,6 +53,7 @@ const ContentStrategyTab: React.FC = () => {
|
|||||||
// Load data on component mount
|
// Load data on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadInitialData();
|
loadInitialData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Check if coming from strategy builder
|
// Check if coming from strategy builder
|
||||||
@@ -92,7 +81,7 @@ const ContentStrategyTab: React.FC = () => {
|
|||||||
console.log('🧹 Clearing latest generated strategy cache (navigating away from strategy builder)');
|
console.log('🧹 Clearing latest generated strategy cache (navigating away from strategy builder)');
|
||||||
// Note: We don't clear the cache here as it might be needed for the current session
|
// Note: We don't clear the cache here as it might be needed for the current session
|
||||||
}
|
}
|
||||||
}, [location.state]);
|
}, [location.state, latestGeneratedStrategy]);
|
||||||
|
|
||||||
// Track strategy status changes for debugging (with debounce)
|
// Track strategy status changes for debugging (with debounce)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -136,6 +125,7 @@ const ContentStrategyTab: React.FC = () => {
|
|||||||
setShowOnboarding(true);
|
setShowOnboarding(true);
|
||||||
}
|
}
|
||||||
// If strategiesArray.length === 0 and !hasCheckedStrategy, do nothing (wait for data to load)
|
// If strategiesArray.length === 0 and !hasCheckedStrategy, do nothing (wait for data to load)
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [strategies, loadStrategies, isFromStrategyBuilder]);
|
}, [strategies, loadStrategies, isFromStrategyBuilder]);
|
||||||
|
|
||||||
const loadStrategyData = async () => {
|
const loadStrategyData = async () => {
|
||||||
@@ -390,9 +380,6 @@ const ContentStrategyTab: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strategiesArray.length > 0) {
|
if (strategiesArray.length > 0) {
|
||||||
// Find the most recent strategy
|
|
||||||
const latestStrategy = strategiesArray[0]; // Assuming strategies are sorted by date
|
|
||||||
|
|
||||||
// For now, we'll assume strategies are active if they exist
|
// For now, we'll assume strategies are active if they exist
|
||||||
// In a real implementation, you would check a status field from the database
|
// In a real implementation, you would check a status field from the database
|
||||||
setStrategyStatus('active');
|
setStrategyStatus('active');
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab
|
||||||
Button
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
AutoAwesome as AutoAwesomeIcon,
|
AutoAwesome as AutoAwesomeIcon,
|
||||||
@@ -20,7 +19,6 @@ import { apiClient } from '../../../api/client';
|
|||||||
|
|
||||||
// Import hooks and services
|
// Import hooks and services
|
||||||
import { useStrategyCalendarContext } from '../../../contexts/StrategyCalendarContext';
|
import { useStrategyCalendarContext } from '../../../contexts/StrategyCalendarContext';
|
||||||
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
|
||||||
|
|
||||||
// Import types
|
// Import types
|
||||||
import { type CalendarConfig } from '../components/CalendarWizardSteps/types';
|
import { type CalendarConfig } from '../components/CalendarWizardSteps/types';
|
||||||
@@ -51,11 +49,10 @@ const CreateTab: React.FC = () => {
|
|||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [currentCalendarConfig, setCurrentCalendarConfig] = useState<CalendarConfig | null>(null);
|
const [currentCalendarConfig, setCurrentCalendarConfig] = useState<CalendarConfig | null>(null);
|
||||||
const [sessionId, setSessionId] = useState<string>('');
|
const [sessionId, setSessionId] = useState<string>('');
|
||||||
const [isStartingGeneration, setIsStartingGeneration] = useState(false);
|
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { state: { strategyContext }, isFromStrategyActivation } = useStrategyCalendarContext();
|
const { state: { strategyContext }, isFromStrategyActivation } = useStrategyCalendarContext();
|
||||||
const [userData, setUserData] = useState<any>({});
|
const [userData] = useState<any>({});
|
||||||
|
|
||||||
// Handle navigation from strategy activation
|
// Handle navigation from strategy activation
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -74,7 +71,7 @@ const CreateTab: React.FC = () => {
|
|||||||
console.log('🎯 CreateTab: Switching to Calendar Wizard tab (index 1)');
|
console.log('🎯 CreateTab: Switching to Calendar Wizard tab (index 1)');
|
||||||
setTabValue(1); // Switch to Calendar Wizard tab
|
setTabValue(1); // Switch to Calendar Wizard tab
|
||||||
}
|
}
|
||||||
}, [isFromStrategyActivation, strategyContext?.activationStatus]);
|
}, [isFromStrategyActivation, strategyContext?.activationStatus, location.state]);
|
||||||
|
|
||||||
// Also check on mount for immediate navigation state
|
// Also check on mount for immediate navigation state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -109,9 +106,6 @@ const CreateTab: React.FC = () => {
|
|||||||
setCurrentCalendarConfig(calendarConfig);
|
setCurrentCalendarConfig(calendarConfig);
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
|
|
||||||
// Set loading state to prevent multiple clicks
|
|
||||||
setIsStartingGeneration(true);
|
|
||||||
|
|
||||||
// Transform calendarConfig to match backend CalendarGenerationRequest format
|
// Transform calendarConfig to match backend CalendarGenerationRequest format
|
||||||
const requestData = {
|
const requestData = {
|
||||||
user_id: 1, // Default user ID
|
user_id: 1, // Default user ID
|
||||||
@@ -126,20 +120,19 @@ const CreateTab: React.FC = () => {
|
|||||||
|
|
||||||
// Call the new start endpoint to get session ID with retry logic
|
// Call the new start endpoint to get session ID with retry logic
|
||||||
let startResponse;
|
let startResponse;
|
||||||
let retryCount = 0;
|
|
||||||
const maxRetries = 3;
|
const maxRetries = 3;
|
||||||
|
|
||||||
while (retryCount < maxRetries) {
|
for (let retryCount = 0; retryCount < maxRetries; retryCount++) {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.post('/api/content-planning/calendar-generation/start', requestData);
|
const response = await apiClient.post('/api/content-planning/calendar-generation/start', requestData);
|
||||||
startResponse = { ok: true, data: response.data };
|
startResponse = { ok: true, data: response.data };
|
||||||
break; // Success, exit retry loop
|
break; // Success, exit retry loop
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.warn(`⚠️ Attempt ${retryCount + 1} failed with error:`, error);
|
console.warn(`⚠️ Attempt ${retryCount + 1} failed with error:`, error);
|
||||||
retryCount++;
|
if (retryCount < maxRetries - 1) {
|
||||||
if (retryCount < maxRetries) {
|
|
||||||
// Wait before retry (exponential backoff)
|
// Wait before retry (exponential backoff)
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
|
const delay = 1000 * (retryCount + 1);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
} else {
|
} else {
|
||||||
startResponse = { ok: false, data: null };
|
startResponse = { ok: false, data: null };
|
||||||
}
|
}
|
||||||
@@ -175,8 +168,7 @@ const CreateTab: React.FC = () => {
|
|||||||
setCurrentCalendarConfig(null);
|
setCurrentCalendarConfig(null);
|
||||||
setSessionId('');
|
setSessionId('');
|
||||||
} finally {
|
} finally {
|
||||||
// Clear loading state
|
// Cleanup complete
|
||||||
setIsStartingGeneration(false);
|
|
||||||
}
|
}
|
||||||
}, [userData, strategyContext]);
|
}, [userData, strategyContext]);
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab,
|
||||||
Typography,
|
Typography
|
||||||
Alert
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
|
||||||
Typography,
|
Typography,
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -16,10 +15,6 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
Button
|
Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
|
||||||
Search as SearchIcon
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
|
||||||
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
import { contentPlanningApi } from '../../../services/contentPlanningApi';
|
||||||
|
|
||||||
const KeywordResearchTab: React.FC = () => {
|
const KeywordResearchTab: React.FC = () => {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const RefineAnalysisTab: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadGapAnalysisData();
|
loadGapAnalysisData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadGapAnalysisData = async () => {
|
const loadGapAnalysisData = async () => {
|
||||||
|
|||||||
@@ -87,16 +87,16 @@ function diffMarkup(oldText: string, newText: string): string {
|
|||||||
out += a[i];
|
out += a[i];
|
||||||
i++; j++;
|
i++; j++;
|
||||||
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
||||||
out += `<s class=\"fbw-del\">${escapeHtml(a[i])}</s>`;
|
out += `<s class="fbw-del">${escapeHtml(a[i])}</s>`;
|
||||||
i++;
|
i++;
|
||||||
} else {
|
} else {
|
||||||
out += `<em class=\"fbw-add\">${escapeHtml(b[j])}</em>`;
|
out += `<em class="fbw-add">${escapeHtml(b[j])}</em>`;
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (i < n) { out += `<s class=\"fbw-del\">${escapeHtml(a[i++])}</s>`; }
|
while (i < n) { out += `<s class="fbw-del">${escapeHtml(a[i++])}</s>`; }
|
||||||
while (j < m) { out += `<em class=\"fbw-add\">${escapeHtml(b[j++])}</em>`; }
|
while (j < m) { out += `<em class="fbw-add">${escapeHtml(b[j++])}</em>`; }
|
||||||
if (oldText.length > MAX || newText.length > MAX) out += '<span class=\"fbw-more\"> …</span>';
|
if (oldText.length > MAX || newText.length > MAX) out += '<span class="fbw-more"> …</span>';
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ const FacebookWriterContent: React.FC<FacebookWriterProps> = ({ className = '' }
|
|||||||
const [livePreviewHtml, setLivePreviewHtml] = React.useState<string>('');
|
const [livePreviewHtml, setLivePreviewHtml] = React.useState<string>('');
|
||||||
const [isPreviewing, setIsPreviewing] = React.useState<boolean>(false);
|
const [isPreviewing, setIsPreviewing] = React.useState<boolean>(false);
|
||||||
const [pendingEdit, setPendingEdit] = React.useState<{ src: string; target: string } | null>(null);
|
const [pendingEdit, setPendingEdit] = React.useState<{ src: string; target: string } | null>(null);
|
||||||
const [historyVersion, setHistoryVersion] = React.useState<number>(0);
|
const [, setHistoryVersion] = React.useState<number>(0);
|
||||||
const [adVariations, setAdVariations] = React.useState<{
|
const [adVariations, setAdVariations] = React.useState<{
|
||||||
headline_variations: string[];
|
headline_variations: string[];
|
||||||
primary_text_variations: string[];
|
primary_text_variations: string[];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCopilotAction } from '@copilotkit/react-core';
|
import { useCopilotAction } from '@copilotkit/react-core';
|
||||||
import { linkedInWriterApi, LinkedInPostRequest, GroundingLevel } from '../../services/linkedInWriterApi';
|
import { linkedInWriterApi, GroundingLevel } from '../../services/linkedInWriterApi';
|
||||||
import {
|
import {
|
||||||
mapPostType,
|
mapPostType,
|
||||||
mapTone,
|
mapTone,
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
mapSearchEngine,
|
mapSearchEngine,
|
||||||
readPrefs
|
readPrefs
|
||||||
} from './utils/linkedInWriterUtils';
|
} from './utils/linkedInWriterUtils';
|
||||||
import { PostHITL, ArticleHITL, CarouselHITL, VideoScriptHITL, CommentResponseHITL } from './components';
|
|
||||||
import { apiClient } from '../../api/client';
|
import { apiClient } from '../../api/client';
|
||||||
|
|
||||||
const useCopilotActionTyped = useCopilotAction as any;
|
const useCopilotActionTyped = useCopilotAction as any;
|
||||||
@@ -125,17 +124,6 @@ const RegisterLinkedInActions: React.FC = () => {
|
|||||||
]
|
]
|
||||||
}}));
|
}}));
|
||||||
|
|
||||||
// If refining existing content, use the current draft as context
|
|
||||||
let existingContent = '';
|
|
||||||
if (args?.refine_existing) {
|
|
||||||
// Get current draft from the page context
|
|
||||||
const textarea = document.querySelector('textarea') as HTMLTextAreaElement;
|
|
||||||
const currentDraft = textarea?.value || '';
|
|
||||||
if (currentDraft) {
|
|
||||||
existingContent = `\n\nREFINE THIS EXISTING CONTENT:\n${currentDraft}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start detailed progress tracking
|
// Start detailed progress tracking
|
||||||
window.dispatchEvent(new CustomEvent('linkedinwriter:progressStep', {
|
window.dispatchEvent(new CustomEvent('linkedinwriter:progressStep', {
|
||||||
detail: {
|
detail: {
|
||||||
@@ -750,7 +738,7 @@ const RegisterLinkedInActions: React.FC = () => {
|
|||||||
{ name: 'improvement_type', type: 'string', required: false }
|
{ name: 'improvement_type', type: 'string', required: false }
|
||||||
],
|
],
|
||||||
handler: async (args: any) => {
|
handler: async (args: any) => {
|
||||||
const { recommendation, current_content, improvement_type } = args;
|
const { recommendation } = args;
|
||||||
|
|
||||||
// Analyze the recommendation and provide specific improvement guidance
|
// Analyze the recommendation and provide specific improvement guidance
|
||||||
let improvementGuidance = '';
|
let improvementGuidance = '';
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ const RegisterLinkedInEditActions: React.FC = () => {
|
|||||||
],
|
],
|
||||||
handler: async (args: any) => {
|
handler: async (args: any) => {
|
||||||
const content = args?.content || '';
|
const content = args?.content || '';
|
||||||
const industry = args?.industry || 'Technology';
|
|
||||||
|
|
||||||
// Placeholder for hashtag addition
|
// Placeholder for hashtag addition
|
||||||
const hashtags = '#ProfessionalDevelopment #Networking #IndustryInsights #CareerGrowth';
|
const hashtags = '#ProfessionalDevelopment #Networking #IndustryInsights #CareerGrowth';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { usePlatformPersonaContext } from '../../shared/PersonaContext/PlatformPersonaProvider';
|
import { usePlatformPersonaContext } from '../../shared/PersonaContext/PlatformPersonaProvider';
|
||||||
import { apiClient } from '../../../api/client';
|
import { apiClient } from '../../../api/client';
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ interface ContentEditorProps {
|
|||||||
topic?: string;
|
topic?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ContentEditor };
|
|
||||||
|
|
||||||
const ContentEditor: React.FC<ContentEditorProps> = ({
|
const ContentEditor: React.FC<ContentEditorProps> = ({
|
||||||
isPreviewing,
|
isPreviewing,
|
||||||
pendingEdit,
|
pendingEdit,
|
||||||
@@ -62,6 +60,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
|||||||
const ctaCooldownRef = useRef<number | null>(null); // 15s cooldown after dismissing CTA
|
const ctaCooldownRef = useRef<number | null>(null); // 15s cooldown after dismissing CTA
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_WA) console.log('🎯 [ContentEditor] waSuggestion changed:', waSuggestion);
|
if (DEBUG_WA) console.log('🎯 [ContentEditor] waSuggestion changed:', waSuggestion);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [waSuggestion]);
|
}, [waSuggestion]);
|
||||||
const waTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const waTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const hasTriggeredOnceRef = useRef<boolean>(false);
|
const hasTriggeredOnceRef = useRef<boolean>(false);
|
||||||
@@ -228,8 +227,6 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
|||||||
lastWords: words.slice(-5).join(' ')
|
lastWords: words.slice(-5).join(' ')
|
||||||
});
|
});
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
const last = lastSuggestMetaRef.current;
|
|
||||||
const textHash = getStableContextHash(uptoCaret);
|
const textHash = getStableContextHash(uptoCaret);
|
||||||
|
|
||||||
// After first auto-trigger, stop auto-calling API. Show CTA instead.
|
// After first auto-trigger, stop auto-calling API. Show CTA instead.
|
||||||
@@ -271,7 +268,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
|||||||
let userError = "Failed to get writing suggestion";
|
let userError = "Failed to get writing suggestion";
|
||||||
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
|
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
|
||||||
userError = "API quota exceeded. Please try again later or upgrade your plan.";
|
userError = "API quota exceeded. Please try again later or upgrade your plan.";
|
||||||
const match = msg.match(/\"retryDelay\"\s*:\s*\"(\d+)s\"/);
|
const match = msg.match(/"retryDelay"\s*:\s*"(\d+)s"/);
|
||||||
const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000;
|
const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000;
|
||||||
coolDownUntilRef.current = Date.now() + retryMs;
|
coolDownUntilRef.current = Date.now() + retryMs;
|
||||||
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
|
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
|
||||||
@@ -316,7 +313,7 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
|||||||
let userError = "Failed to get writing suggestion";
|
let userError = "Failed to get writing suggestion";
|
||||||
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
|
if (msg.includes('429') || msg.includes('RESOURCE_EXHAUSTED')) {
|
||||||
userError = "API quota exceeded. Please try again later or upgrade your plan.";
|
userError = "API quota exceeded. Please try again later or upgrade your plan.";
|
||||||
const match = msg.match(/\"retryDelay\"\s*:\s*\"(\d+)s\"/);
|
const match = msg.match(/"retryDelay"\s*:\s*"(\d+)s"/);
|
||||||
const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000;
|
const retryMs = match ? parseInt(match[1], 10) * 1000 : 40000;
|
||||||
coolDownUntilRef.current = Date.now() + retryMs;
|
coolDownUntilRef.current = Date.now() + retryMs;
|
||||||
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
|
console.warn('✍️ [ContentEditor] Entering suggestion cooldown for ms:', retryMs);
|
||||||
@@ -422,3 +419,5 @@ const ContentEditor: React.FC<ContentEditorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { ContentEditor };
|
||||||
@@ -3,29 +3,12 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
Typography,
|
Typography,
|
||||||
Card,
|
|
||||||
CardContent,
|
CardContent,
|
||||||
useTheme,
|
|
||||||
useMediaQuery,
|
|
||||||
Chip,
|
Chip,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Paper,
|
Paper
|
||||||
Modal,
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
Divider,
|
|
||||||
LinearProgress,
|
|
||||||
Avatar,
|
|
||||||
Stack
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
|
||||||
Close as CloseIcon,
|
|
||||||
Settings as SettingsIcon,
|
|
||||||
CheckCircle as CheckIcon,
|
|
||||||
RadioButtonUnchecked as UncheckedIcon,
|
|
||||||
TrendingUp as TrendingUpIcon
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
import GeneratePillarChips from './components/GeneratePillarChips';
|
import GeneratePillarChips from './components/GeneratePillarChips';
|
||||||
import PublishPillarChips from './components/PublishPillarChips';
|
import PublishPillarChips from './components/PublishPillarChips';
|
||||||
import AnalyzePillarChips from './components/AnalyzePillarChips';
|
import AnalyzePillarChips from './components/AnalyzePillarChips';
|
||||||
@@ -484,14 +467,11 @@ const PillarCard: React.FC<{
|
|||||||
|
|
||||||
// Main Content Lifecycle Pillars Component
|
// Main Content Lifecycle Pillars Component
|
||||||
const ContentLifecyclePillars: React.FC = () => {
|
const ContentLifecyclePillars: React.FC = () => {
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
|
||||||
const [onboardingModalOpen, setOnboardingModalOpen] = useState(false);
|
const [onboardingModalOpen, setOnboardingModalOpen] = useState(false);
|
||||||
|
|
||||||
// Workflow store hooks
|
// Workflow store hooks
|
||||||
const {
|
const {
|
||||||
currentWorkflow,
|
currentWorkflow,
|
||||||
workflowProgress,
|
|
||||||
isLoading: workflowLoading,
|
isLoading: workflowLoading,
|
||||||
startWorkflow,
|
startWorkflow,
|
||||||
} = useWorkflowStore();
|
} = useWorkflowStore();
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import CompactSidebar from './components/CompactSidebar';
|
|||||||
|
|
||||||
// Shared types and utilities
|
// Shared types and utilities
|
||||||
import { Tool } from '../shared/types';
|
import { Tool } from '../shared/types';
|
||||||
import { getFilteredCategories, getToolsForCategory } from '../shared/utils';
|
import { getToolsForCategory } from '../shared/utils';
|
||||||
|
|
||||||
// Zustand stores
|
// Zustand stores
|
||||||
import { useDashboardStore } from '../../stores/dashboardStore';
|
import { useDashboardStore } from '../../stores/dashboardStore';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Chip, useTheme } from '@mui/material';
|
import { Box, Chip } from '@mui/material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Paper,
|
Paper,
|
||||||
Typography,
|
Typography,
|
||||||
Chip,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Divider,
|
Divider,
|
||||||
@@ -14,8 +13,6 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Activity,
|
|
||||||
Zap,
|
|
||||||
Star
|
Star
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
@@ -78,26 +75,16 @@ const CompactSidebar: React.FC<CompactSidebarProps> = ({
|
|||||||
// State for search expansion on hover
|
// State for search expansion on hover
|
||||||
const [isSearchExpanded, setIsSearchExpanded] = useState(false);
|
const [isSearchExpanded, setIsSearchExpanded] = useState(false);
|
||||||
// State for sidebar hover expansion
|
// State for sidebar hover expansion
|
||||||
const [isSidebarHovered, setIsSidebarHovered] = useState(false);
|
const [, setIsSidebarHovered] = useState(false);
|
||||||
// State for favorites expansion on hover
|
// State for favorites expansion on hover
|
||||||
const [isFavoritesExpanded, setIsFavoritesExpanded] = useState(false);
|
const [isFavoritesExpanded, setIsFavoritesExpanded] = useState(false);
|
||||||
// Track original collapsed state for hover behavior
|
// Track original collapsed state for hover behavior
|
||||||
const [wasOriginallyCollapsed, setWasOriginallyCollapsed] = useState(false);
|
const [wasOriginallyCollapsed, setWasOriginallyCollapsed] = useState(false);
|
||||||
const [isAnimating, setIsAnimating] = useState(false);
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
const [rippleIndex, setRippleIndex] = useState(-1);
|
const [, setRippleIndex] = useState(-1);
|
||||||
const [shouldAutoExpand, setShouldAutoExpand] = useState(false);
|
const [shouldAutoExpand, setShouldAutoExpand] = useState(false);
|
||||||
const [userHasInteracted, setUserHasInteracted] = useState(false);
|
const [userHasInteracted, setUserHasInteracted] = useState(false);
|
||||||
|
|
||||||
// Calculate total tools count
|
|
||||||
const totalTools = Object.values(toolCategories).reduce((sum, category) => {
|
|
||||||
if ('tools' in category) {
|
|
||||||
return sum + category.tools.length;
|
|
||||||
} else if ('subCategories' in category) {
|
|
||||||
return sum + Object.values(category.subCategories).reduce((subSum, subCat) => subSum + subCat.tools.length, 0);
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
// Ripple effect for chips
|
// Ripple effect for chips
|
||||||
const startRippleEffect = useCallback(() => {
|
const startRippleEffect = useCallback(() => {
|
||||||
const categoryEntries = Object.entries(toolCategories).slice(0, 5);
|
const categoryEntries = Object.entries(toolCategories).slice(0, 5);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Chip, useTheme } from '@mui/material';
|
import { Box, Chip } from '@mui/material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Avatar,
|
Avatar,
|
||||||
Stack,
|
Stack,
|
||||||
LinearProgress,
|
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Card,
|
Card,
|
||||||
CardContent
|
CardContent
|
||||||
@@ -57,13 +56,10 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
|
|||||||
navigationState,
|
navigationState,
|
||||||
completeTask,
|
completeTask,
|
||||||
skipTask,
|
skipTask,
|
||||||
moveToNextTask,
|
|
||||||
isLoading,
|
isLoading,
|
||||||
isWorkflowComplete
|
isWorkflowComplete
|
||||||
} = useWorkflowStore();
|
} = useWorkflowStore();
|
||||||
|
|
||||||
const [selectedTask, setSelectedTask] = useState<TodayTask | null>(null);
|
|
||||||
|
|
||||||
// Prefer live workflow tasks (to reflect updated statuses), fallback to props
|
// Prefer live workflow tasks (to reflect updated statuses), fallback to props
|
||||||
const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0
|
const liveTasks = currentWorkflow?.tasks && Array.isArray(currentWorkflow.tasks) && currentWorkflow.tasks.length > 0
|
||||||
? currentWorkflow.tasks
|
? currentWorkflow.tasks
|
||||||
@@ -100,12 +96,6 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStartWorkflow = async () => {
|
|
||||||
if (currentWorkflow) {
|
|
||||||
await moveToNextTask();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNextPillar = async () => {
|
const handleNextPillar = async () => {
|
||||||
// Close current modal
|
// Close current modal
|
||||||
onClose();
|
onClose();
|
||||||
@@ -158,9 +148,6 @@ const EnhancedTodayModal: React.FC<EnhancedTodayModalProps> = ({
|
|||||||
task.status === 'completed' || task.status === 'skipped'
|
task.status === 'completed' || task.status === 'skipped'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if this is the Plan pillar
|
|
||||||
const isPlanPillar = pillarId === 'plan';
|
|
||||||
|
|
||||||
// Define pillar order for navigation
|
// Define pillar order for navigation
|
||||||
const pillarOrder = ['plan', 'generate', 'publish', 'analyze', 'engage', 'remarket'];
|
const pillarOrder = ['plan', 'generate', 'publish', 'analyze', 'engage', 'remarket'];
|
||||||
const currentPillarIndex = pillarOrder.indexOf(pillarId);
|
const currentPillarIndex = pillarOrder.indexOf(pillarId);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
@@ -7,10 +7,6 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Button,
|
Button,
|
||||||
Typography,
|
Typography,
|
||||||
Stepper,
|
|
||||||
Step,
|
|
||||||
StepLabel,
|
|
||||||
StepConnector,
|
|
||||||
Fade,
|
Fade,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
@@ -25,7 +21,6 @@ import {
|
|||||||
Key,
|
Key,
|
||||||
ContentPasteRounded,
|
ContentPasteRounded,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
|
|
||||||
interface ApiKeyCarouselProps {
|
interface ApiKeyCarouselProps {
|
||||||
providers: Array<{
|
providers: Array<{
|
||||||
@@ -40,40 +35,19 @@ interface ApiKeyCarouselProps {
|
|||||||
link: string;
|
link: string;
|
||||||
free: boolean;
|
free: boolean;
|
||||||
recommended: boolean;
|
recommended: boolean;
|
||||||
benefits: string[];
|
benefits: string[];
|
||||||
}>;
|
}>;
|
||||||
currentProvider: number;
|
currentProvider: number;
|
||||||
setCurrentProvider: (index: number) => void;
|
setCurrentProvider: (index: number) => void;
|
||||||
onProviderFocus: (provider: any) => void;
|
onProviderFocus: (provider: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomStepConnector = styled(StepConnector)(({ theme }) => ({
|
|
||||||
'&.MuiStepConnector-alternativeLabel': {
|
|
||||||
top: 10,
|
|
||||||
left: 'calc(-50% + 16px)',
|
|
||||||
right: 'calc(50% + 16px)',
|
|
||||||
},
|
|
||||||
'& .MuiStepConnector-line': {
|
|
||||||
height: 3,
|
|
||||||
border: 0,
|
|
||||||
background: 'linear-gradient(90deg, #E2E8F0 0%, #CBD5E1 100%)',
|
|
||||||
borderRadius: 2,
|
|
||||||
},
|
|
||||||
'&.MuiStepConnector-active .MuiStepConnector-line': {
|
|
||||||
background: 'linear-gradient(90deg, #3B82F6 0%, #1D4ED8 100%)',
|
|
||||||
},
|
|
||||||
'&.MuiStepConnector-completed .MuiStepConnector-line': {
|
|
||||||
background: 'linear-gradient(90deg, #10B981 0%, #059669 100%)',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const ApiKeyCarousel: React.FC<ApiKeyCarouselProps> = ({
|
const ApiKeyCarousel: React.FC<ApiKeyCarouselProps> = ({
|
||||||
providers,
|
providers,
|
||||||
currentProvider,
|
currentProvider,
|
||||||
setCurrentProvider,
|
setCurrentProvider,
|
||||||
onProviderFocus,
|
onProviderFocus,
|
||||||
}) => {
|
}) => {
|
||||||
const [autoProgress, setAutoProgress] = useState(false);
|
|
||||||
const provider = providers[currentProvider];
|
const provider = providers[currentProvider];
|
||||||
|
|
||||||
const getAccentColor = (name: string) => {
|
const getAccentColor = (name: string) => {
|
||||||
@@ -117,14 +91,6 @@ const ApiKeyCarousel: React.FC<ApiKeyCarouselProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStepIcon = (index: number) => {
|
|
||||||
const stepProvider = providers[index];
|
|
||||||
if (stepProvider.status === 'valid') {
|
|
||||||
return <CheckCircle sx={{ color: 'success.main' }} />;
|
|
||||||
}
|
|
||||||
return <Key sx={{ color: stepProvider === provider ? 'primary.main' : 'text.disabled' }} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ width: '100%', maxWidth: 600, mx: 'auto' }}>
|
<Box sx={{ width: '100%', maxWidth: 600, mx: 'auto' }}>
|
||||||
{/* Progress Stepper - Hidden as requested */}
|
{/* Progress Stepper - Hidden as requested */}
|
||||||
|
|||||||
@@ -10,13 +10,10 @@ import {
|
|||||||
ListItemText,
|
ListItemText,
|
||||||
Chip,
|
Chip,
|
||||||
Divider,
|
Divider,
|
||||||
Alert,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Star,
|
Star,
|
||||||
Security,
|
|
||||||
Speed,
|
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
Insights,
|
Insights,
|
||||||
Search,
|
Search,
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useAuth } from '@clerk/clerk-react';
|
|
||||||
import { getApiKeysForOnboarding, getStep1ApiKeysFromProgress, saveApiKey } from '../../../../api/onboarding';
|
import { getApiKeysForOnboarding, getStep1ApiKeysFromProgress, saveApiKey } from '../../../../api/onboarding';
|
||||||
import { getKeyStatus, formatErrorMessage } from '../../common/onboardingUtils';
|
import { getKeyStatus, formatErrorMessage } from '../../common/onboardingUtils';
|
||||||
import { Provider } from './ProviderCard';
|
import { Provider } from './ProviderCard';
|
||||||
import { apiClient } from '../../../../api/client';
|
import { apiClient } from '../../../../api/client';
|
||||||
|
|
||||||
export const useApiKeyStep = (onContinue: (stepData?: any) => void) => {
|
export const useApiKeyStep = (onContinue: (stepData?: any) => void) => {
|
||||||
const { getToken } = useAuth();
|
|
||||||
const [geminiKey, setGeminiKey] = useState('');
|
const [geminiKey, setGeminiKey] = useState('');
|
||||||
const [exaKey, setExaKey] = useState('');
|
const [exaKey, setExaKey] = useState('');
|
||||||
const [copilotkitKey, setCopilotkitKey] = useState('');
|
const [copilotkitKey, setCopilotkitKey] = useState('');
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
|
|||||||
const [analysisStep, setAnalysisStep] = useState('');
|
const [analysisStep, setAnalysisStep] = useState('');
|
||||||
const [competitors, setCompetitors] = useState<Competitor[]>([]);
|
const [competitors, setCompetitors] = useState<Competitor[]>([]);
|
||||||
const [socialMediaAccounts, setSocialMediaAccounts] = useState<any>({});
|
const [socialMediaAccounts, setSocialMediaAccounts] = useState<any>({});
|
||||||
const [socialMediaCitations, setSocialMediaCitations] = useState<any[]>([]);
|
const [, setSocialMediaCitations] = useState<any[]>([]);
|
||||||
const [researchSummary, setResearchSummary] = useState<ResearchSummary | null>(null);
|
const [researchSummary, setResearchSummary] = useState<ResearchSummary | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [showProgressModal, setShowProgressModal] = useState(false);
|
const [showProgressModal, setShowProgressModal] = useState(false);
|
||||||
@@ -260,6 +260,7 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
|
|||||||
} finally {
|
} finally {
|
||||||
setIsAnalyzingSitemap(false);
|
setIsAnalyzingSitemap(false);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [userUrl, competitors, industryContext, isAnalyzingSitemap]);
|
}, [userUrl, competitors, industryContext, isAnalyzingSitemap]);
|
||||||
|
|
||||||
// Initialize: Check cache first, then run analysis if needed
|
// Initialize: Check cache first, then run analysis if needed
|
||||||
@@ -313,36 +314,6 @@ const CompetitorAnalysisStep: React.FC<CompetitorAnalysisStepProps> = ({
|
|||||||
};
|
};
|
||||||
}, [competitors, researchSummary, sitemapAnalysis, userUrl, industryContext]);
|
}, [competitors, researchSummary, sitemapAnalysis, userUrl, industryContext]);
|
||||||
|
|
||||||
const handleContinue = async () => {
|
|
||||||
// Save research preferences to backend before continuing
|
|
||||||
try {
|
|
||||||
const researchData = getResearchData();
|
|
||||||
|
|
||||||
// Extract research preferences for saving (use defaults if not available)
|
|
||||||
const researchPreferences = {
|
|
||||||
research_depth: 'Comprehensive',
|
|
||||||
content_types: ['blog_posts', 'social_media'],
|
|
||||||
auto_research: true,
|
|
||||||
factual_content: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save research preferences to backend
|
|
||||||
await aiApiClient.post('/api/ai-research/configure-preferences', {
|
|
||||||
research_depth: researchPreferences.research_depth,
|
|
||||||
content_types: researchPreferences.content_types,
|
|
||||||
auto_research: researchPreferences.auto_research,
|
|
||||||
factual_content: researchPreferences.factual_content
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('Research preferences saved to backend');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving research preferences:', error);
|
|
||||||
// Continue anyway - don't block user progress for save errors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue with wizard navigation
|
|
||||||
onContinue(getResearchData());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Expose data collection function to parent (only when onDataReady changes)
|
// Expose data collection function to parent (only when onDataReady changes)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -15,22 +15,13 @@ import {
|
|||||||
ListItem,
|
ListItem,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Alert,
|
Alert
|
||||||
LinearProgress
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Search as SearchIcon,
|
Search as SearchIcon,
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
TrendingUp as TrendingIcon,
|
|
||||||
Speed as SpeedIcon,
|
|
||||||
Security as SecurityIcon,
|
|
||||||
CheckCircle as CheckIcon,
|
CheckCircle as CheckIcon,
|
||||||
Schedule as ScheduleIcon,
|
Insights as InsightsIcon
|
||||||
Rocket as RocketIcon,
|
|
||||||
DataUsage as DataIcon,
|
|
||||||
Compare as CompareIcon,
|
|
||||||
Insights as InsightsIcon,
|
|
||||||
Assessment as AssessmentIcon
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
export const ComingSoonSection: React.FC = () => {
|
export const ComingSoonSection: React.FC = () => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useAuth } from '@clerk/clerk-react';
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Fade,
|
Fade,
|
||||||
@@ -17,19 +16,7 @@ import {
|
|||||||
// Platform Icons
|
// Platform Icons
|
||||||
Web as WordPressIcon,
|
Web as WordPressIcon,
|
||||||
Web as WixIcon,
|
Web as WixIcon,
|
||||||
Google as GoogleIcon,
|
Google as GoogleIcon
|
||||||
// Status Icons
|
|
||||||
CheckCircle as CheckIcon,
|
|
||||||
Error as ErrorIcon,
|
|
||||||
Info as InfoIcon,
|
|
||||||
Launch as LaunchIcon,
|
|
||||||
Security as SecurityIcon,
|
|
||||||
Verified as VerifiedIcon,
|
|
||||||
Schedule as ScheduleIcon,
|
|
||||||
TrendingUp as TrendingUpIcon,
|
|
||||||
Email as EmailIcon,
|
|
||||||
Business as BusinessIcon,
|
|
||||||
Notifications as NotificationsIcon
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
// Import refactored components
|
// Import refactored components
|
||||||
@@ -59,12 +46,11 @@ interface IntegrationPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateHeaderContent }) => {
|
const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateHeaderContent }) => {
|
||||||
const { getToken } = useAuth();
|
|
||||||
const [email, setEmail] = useState<string>('');
|
const [email, setEmail] = useState<string>('');
|
||||||
|
|
||||||
// Use custom hooks
|
// Use custom hooks
|
||||||
const { gscSites, connectedPlatforms, setConnectedPlatforms, setGscSites, handleGSCConnect } = useGSCConnection();
|
const { gscSites, connectedPlatforms, setConnectedPlatforms, handleGSCConnect } = useGSCConnection();
|
||||||
const { isLoading, showToast, setShowToast, toastMessage, setToastMessage, handleConnect } = usePlatformConnections();
|
const { isLoading, showToast, setShowToast, toastMessage, handleConnect } = usePlatformConnections();
|
||||||
|
|
||||||
// Initialize integrations data
|
// Initialize integrations data
|
||||||
const [integrations] = useState<IntegrationPlatform[]>([
|
const [integrations] = useState<IntegrationPlatform[]>([
|
||||||
@@ -211,6 +197,7 @@ const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateH
|
|||||||
// Remove query parameters from URL
|
// Remove query parameters from URL
|
||||||
window.history.replaceState({}, document.title, window.location.pathname);
|
window.history.replaceState({}, document.title, window.location.pathname);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Get user email from Clerk
|
// Get user email from Clerk
|
||||||
@@ -255,6 +242,7 @@ const IntegrationsStep: React.FC<IntegrationsStepProps> = ({ onContinue, updateH
|
|||||||
|
|
||||||
const userEmail = getUserEmail();
|
const userEmail = getUserEmail();
|
||||||
setEmail(userEmail);
|
setEmail(userEmail);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handlePlatformConnect = async (platformId: string) => {
|
const handlePlatformConnect = async (platformId: string) => {
|
||||||
|
|||||||
@@ -15,22 +15,12 @@ import {
|
|||||||
ListItem,
|
ListItem,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Divider,
|
Alert
|
||||||
Alert,
|
|
||||||
LinearProgress
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
AutoAwesome as AutoAwesomeIcon,
|
|
||||||
ContentPaste as ContentIcon,
|
|
||||||
Psychology as PsychologyIcon,
|
Psychology as PsychologyIcon,
|
||||||
TrendingUp as TrendingIcon,
|
|
||||||
Security as SecurityIcon,
|
|
||||||
Speed as SpeedIcon,
|
|
||||||
CheckCircle as CheckIcon,
|
CheckCircle as CheckIcon,
|
||||||
Schedule as ScheduleIcon,
|
|
||||||
Rocket as RocketIcon,
|
|
||||||
DataUsage as DataIcon,
|
DataUsage as DataIcon,
|
||||||
Tune as TuneIcon,
|
|
||||||
SmartToy as SmartToyIcon
|
SmartToy as SmartToyIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Typography,
|
Typography,
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
Stepper,
|
Stepper,
|
||||||
Step,
|
Step,
|
||||||
|
|||||||
@@ -17,26 +17,18 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
Slide,
|
Slide,
|
||||||
Zoom,
|
Zoom
|
||||||
Divider
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useTheme, alpha } from '@mui/material/styles';
|
import { useTheme, alpha } from '@mui/material/styles';
|
||||||
import {
|
import {
|
||||||
ExpandMore as ExpandMoreIcon,
|
ExpandMore as ExpandMoreIcon,
|
||||||
CheckCircle as CheckIcon,
|
CheckCircle as CheckIcon,
|
||||||
Info as InfoIcon,
|
|
||||||
Psychology as PsychologyIcon,
|
Psychology as PsychologyIcon,
|
||||||
TrendingUp as TrendingUpIcon,
|
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
Business as BusinessIcon,
|
Business as BusinessIcon,
|
||||||
AutoAwesome as AutoAwesomeIcon,
|
AutoAwesome as AutoAwesomeIcon,
|
||||||
Star as StarIcon,
|
Star as StarIcon,
|
||||||
Warning as WarningIcon,
|
Warning as WarningIcon
|
||||||
Language as LanguageIcon,
|
|
||||||
Web as WebIcon,
|
|
||||||
Palette as PaletteIcon,
|
|
||||||
Speed as SpeedIcon,
|
|
||||||
Group as GroupIcon
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ const Wizard: React.FC<WizardProps> = ({ onComplete }) => {
|
|||||||
// Use refs to avoid dependency cycles
|
// Use refs to avoid dependency cycles
|
||||||
const stepDataRef = useRef(stepData);
|
const stepDataRef = useRef(stepData);
|
||||||
const competitorDataCollectorRef = useRef(competitorDataCollector);
|
const competitorDataCollectorRef = useRef(competitorDataCollector);
|
||||||
const personaStepRef = useRef<{ handleContinue: () => void } | null>(null);
|
|
||||||
|
|
||||||
// Keep refs in sync with state
|
// Keep refs in sync with state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Google as GoogleIcon,
|
|
||||||
Refresh as RefreshIcon
|
Refresh as RefreshIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { gscAPI, type GSCSite } from '../../../api/gsc';
|
import { type GSCSite } from '../../../api/gsc';
|
||||||
|
|
||||||
interface GSCPlatformCardProps {
|
interface GSCPlatformCardProps {
|
||||||
platform: {
|
platform: {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Web as WixIcon,
|
Web as WixIcon,
|
||||||
Add as AddIcon,
|
|
||||||
CheckCircle as CheckCircleIcon,
|
CheckCircle as CheckCircleIcon,
|
||||||
Error as ErrorIcon,
|
Error as ErrorIcon,
|
||||||
Refresh as RefreshIcon
|
Refresh as RefreshIcon
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
Alert,
|
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -27,12 +26,10 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Web as WordPressIcon,
|
Web as WordPressIcon,
|
||||||
Add as AddIcon,
|
|
||||||
Delete as DeleteIcon,
|
Delete as DeleteIcon,
|
||||||
CheckCircle as CheckCircleIcon,
|
CheckCircle as CheckCircleIcon,
|
||||||
Error as ErrorIcon,
|
Error as ErrorIcon,
|
||||||
Refresh as RefreshIcon,
|
Refresh as RefreshIcon
|
||||||
Launch as LaunchIcon
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useWordPressOAuth } from '../../../hooks/useWordPressOAuth';
|
import { useWordPressOAuth } from '../../../hooks/useWordPressOAuth';
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ const SEOCopilotContext: React.FC<{ children: React.ReactNode }> = ({ children }
|
|||||||
isLoading,
|
isLoading,
|
||||||
isAnalyzing,
|
isAnalyzing,
|
||||||
isGenerating,
|
isGenerating,
|
||||||
error,
|
error
|
||||||
loadPersonalizationData
|
|
||||||
} = useSEOCopilotStore();
|
} = useSEOCopilotStore();
|
||||||
|
|
||||||
const hasLoadedPersonalization = useRef(false);
|
const hasLoadedPersonalization = useRef(false);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// SEO CopilotKit Test Component
|
// SEO CopilotKit Test Component
|
||||||
// Simple test to verify CopilotKit sidebar functionality
|
// Simple test to verify CopilotKit sidebar functionality
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Button, Typography, Paper, Alert } from '@mui/material';
|
import { Box, Button, Typography, Paper, Alert } from '@mui/material';
|
||||||
import { useCopilotAction } from '@copilotkit/react-core';
|
import { useCopilotAction } from '@copilotkit/react-core';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Alert,
|
Alert,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
useTheme,
|
|
||||||
Chip,
|
Chip,
|
||||||
Button
|
Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
@@ -31,8 +30,6 @@ import { userDataAPI } from '../../api/userData';
|
|||||||
|
|
||||||
// SEO Dashboard component
|
// SEO Dashboard component
|
||||||
const SEODashboard: React.FC = () => {
|
const SEODashboard: React.FC = () => {
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
// Clerk authentication hooks
|
// Clerk authentication hooks
|
||||||
const { isSignedIn, isLoaded } = useAuth();
|
const { isSignedIn, isLoaded } = useAuth();
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ const GSCLoginButton: React.FC<GSCLoginButtonProps> = ({ onStatusChange }) => {
|
|||||||
// Check GSC connection status on component mount
|
// Check GSC connection status on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkGSCStatus();
|
checkGSCStatus();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const checkGSCStatus = async () => {
|
const checkGSCStatus = async () => {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import {
|
|||||||
} from './seoUtils';
|
} from './seoUtils';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import CategoryCard from './CategoryCard';
|
|
||||||
import CriticalIssueCard from './CriticalIssueCard';
|
import CriticalIssueCard from './CriticalIssueCard';
|
||||||
import AnalysisTabs from './AnalysisTabs';
|
import AnalysisTabs from './AnalysisTabs';
|
||||||
import IssueDetailsDialog from './IssueDetailsDialog';
|
import IssueDetailsDialog from './IssueDetailsDialog';
|
||||||
|
|||||||
@@ -80,8 +80,6 @@ const CitationHoverHandler: React.FC<CitationHoverHandlerProps> = ({ researchSou
|
|||||||
modal.style.padding = '18px 20px';
|
modal.style.padding = '18px 20px';
|
||||||
|
|
||||||
const title = (src.title || 'Untitled').replace(/</g, '<');
|
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 =
|
modal.innerHTML =
|
||||||
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">' +
|
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">' +
|
||||||
@@ -163,8 +161,6 @@ const CitationHoverHandler: React.FC<CitationHoverHandlerProps> = ({ researchSou
|
|||||||
tip.style.backdropFilter = 'blur(5px)';
|
tip.style.backdropFilter = 'blur(5px)';
|
||||||
|
|
||||||
const title = (src.title || 'Untitled').replace(/</g, '<');
|
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 =
|
tip.innerHTML =
|
||||||
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">' +
|
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">' +
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const ContentDisplayArea: React.FC<ContentDisplayAreaProps> = ({
|
|||||||
if (draft !== localDraft) {
|
if (draft !== localDraft) {
|
||||||
setLocalDraft(draft);
|
setLocalDraft(draft);
|
||||||
}
|
}
|
||||||
}, [draft]);
|
}, [draft, localDraft]);
|
||||||
|
|
||||||
// Cleanup debounced saver
|
// Cleanup debounced saver
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -356,6 +356,7 @@ const ContentPreviewHeaderWithModals: React.FC<ContentPreviewHeaderProps> = (pro
|
|||||||
window.removeEventListener('showCitationsModal', handleShowCitationsModal as EventListener);
|
window.removeEventListener('showCitationsModal', handleShowCitationsModal as EventListener);
|
||||||
window.removeEventListener('showSearchQueriesModal', handleShowSearchQueriesModal as EventListener);
|
window.removeEventListener('showSearchQueriesModal', handleShowSearchQueriesModal as EventListener);
|
||||||
};
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ const MainContentPreviewHeader: React.FC<MainContentPreviewHeaderProps> = ({
|
|||||||
onAssistantToggle,
|
onAssistantToggle,
|
||||||
topic
|
topic
|
||||||
}) => {
|
}) => {
|
||||||
const formatPercent = (v?: number) => typeof v === 'number' ? `${Math.round(v * 100)}%` : '—';
|
|
||||||
const getChipColor = (v?: number) => {
|
const getChipColor = (v?: number) => {
|
||||||
if (typeof v !== 'number') return '#6b7280';
|
if (typeof v !== 'number') return '#6b7280';
|
||||||
if (v >= 0.8) return '#10b981';
|
if (v >= 0.8) return '#10b981';
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ const PersonaChip: React.FC<PersonaChipProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchPersonaData();
|
fetchPersonaData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [platform, userId]);
|
}, [platform, userId]);
|
||||||
|
|
||||||
const handleSavePersona = async (data: PersonaData, saveToDatabase: boolean) => {
|
const handleSavePersona = async (data: PersonaData, saveToDatabase: boolean) => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const QuickEditToolbar: React.FC<QuickEditToolbarProps> = ({ draft, isPreviewing
|
|||||||
const lines = draft.split('\n');
|
const lines = draft.split('\n');
|
||||||
if (lines.length > 0) {
|
if (lines.length > 0) {
|
||||||
const first = lines[0].trim();
|
const first = lines[0].trim();
|
||||||
lines[0] = first.replace(/^(.*?)([\.!?])?$/, '👉 $1$2');
|
lines[0] = first.replace(/^(.*?)([.!?])?$/, '👉 $1$2');
|
||||||
}
|
}
|
||||||
const target = lines.join('\n');
|
const target = lines.join('\n');
|
||||||
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
|
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
|
||||||
@@ -62,7 +62,7 @@ const QuickEditToolbar: React.FC<QuickEditToolbarProps> = ({ draft, isPreviewing
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const target = `[Professionalized]` + '\n\n' + draft;
|
const target = '[Professionalized]\n\n' + draft;
|
||||||
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
|
window.dispatchEvent(new CustomEvent('linkedinwriter:applyEdit', { detail: { target } }));
|
||||||
}}
|
}}
|
||||||
style={{ padding: '6px 10px', border: '1px solid #ddd', borderRadius: 6, background: '#fff', cursor: 'pointer' }}
|
style={{ padding: '6px 10px', border: '1px solid #ddd', borderRadius: 6, background: '#fff', cursor: 'pointer' }}
|
||||||
|
|||||||
@@ -2,10 +2,6 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||||||
import { hallucinationDetectorService, HallucinationDetectionResponse } from '../../services/hallucinationDetectorService';
|
import { hallucinationDetectorService, HallucinationDetectionResponse } from '../../services/hallucinationDetectorService';
|
||||||
import FactCheckResults from '../LinkedInWriter/components/FactCheckResults';
|
import FactCheckResults from '../LinkedInWriter/components/FactCheckResults';
|
||||||
|
|
||||||
interface TextSelectionHandlerProps {
|
|
||||||
contentRef: React.RefObject<HTMLDivElement>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) => {
|
const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) => {
|
||||||
const [selectionMenu, setSelectionMenu] = useState<{ x: number; y: number; text: string } | null>(null);
|
const [selectionMenu, setSelectionMenu] = useState<{ x: number; y: number; text: string } | null>(null);
|
||||||
const [factCheckResults, setFactCheckResults] = useState<HallucinationDetectionResponse | null>(null);
|
const [factCheckResults, setFactCheckResults] = useState<HallucinationDetectionResponse | null>(null);
|
||||||
@@ -112,7 +108,7 @@ const useTextSelectionHandler = (contentRef: React.RefObject<HTMLDivElement>) =>
|
|||||||
switch (editType) {
|
switch (editType) {
|
||||||
case 'tighten':
|
case 'tighten':
|
||||||
// Add hook emoji to the beginning
|
// Add hook emoji to the beginning
|
||||||
editedText = selectedText.replace(/^(.*?)([\.!?])?$/, '👉 $1$2');
|
editedText = selectedText.replace(/^(.*?)([.!?])?$/, '👉 $1$2');
|
||||||
break;
|
break;
|
||||||
case 'add-cta':
|
case 'add-cta':
|
||||||
// Add call-to-action
|
// Add call-to-action
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Chip,
|
Chip,
|
||||||
Divider,
|
Divider
|
||||||
Link
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { apiClient } from '../../api/client';
|
import { apiClient } from '../../api/client';
|
||||||
import { createClient, OAuthStrategy } from '@wix/sdk';
|
import { createClient, OAuthStrategy } from '@wix/sdk';
|
||||||
import { categories as blogCategoriesModule, tags as blogTagsModule, posts as blogPostsModule, draftPosts as blogDraftPostsModule } from '@wix/blog';
|
import { categories as blogCategoriesModule, tags as blogTagsModule } from '@wix/blog';
|
||||||
|
|
||||||
interface WixConnectionStatus {
|
interface WixConnectionStatus {
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
@@ -49,7 +48,6 @@ const WixTestPage: React.FC = () => {
|
|||||||
const [publishing, setPublishing] = useState(false);
|
const [publishing, setPublishing] = useState(false);
|
||||||
const [categories, setCategories] = useState<BlogCategories | null>(null);
|
const [categories, setCategories] = useState<BlogCategories | null>(null);
|
||||||
const [tags, setTags] = useState<BlogTags | null>(null);
|
const [tags, setTags] = useState<BlogTags | null>(null);
|
||||||
const [authUrl, setAuthUrl] = useState<string>('');
|
|
||||||
|
|
||||||
// Blog post form state
|
// Blog post form state
|
||||||
const [blogTitle, setBlogTitle] = useState('Test Blog Post from ALwrity');
|
const [blogTitle, setBlogTitle] = useState('Test Blog Post from ALwrity');
|
||||||
@@ -120,7 +118,6 @@ This integration opens up new possibilities for content creators who want to lev
|
|||||||
// Use sessionStorage to ensure data is scoped to this tab/session
|
// Use sessionStorage to ensure data is scoped to this tab/session
|
||||||
sessionStorage.setItem('wix_oauth_data', JSON.stringify(oauthData));
|
sessionStorage.setItem('wix_oauth_data', JSON.stringify(oauthData));
|
||||||
const { authUrl } = await wixClient.auth.getAuthUrl(oauthData);
|
const { authUrl } = await wixClient.auth.getAuthUrl(oauthData);
|
||||||
setAuthUrl(authUrl);
|
|
||||||
window.location.href = authUrl;
|
window.location.href = authUrl;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to start Wix OAuth flow:', error);
|
console.error('Failed to start Wix OAuth flow:', error);
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ import {
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
DollarSign,
|
DollarSign,
|
||||||
TrendingUp,
|
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
AlertTriangle,
|
|
||||||
CheckCircle,
|
|
||||||
XCircle,
|
|
||||||
Info
|
Info
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
@@ -28,7 +24,6 @@ import {
|
|||||||
formatCurrency,
|
formatCurrency,
|
||||||
formatNumber,
|
formatNumber,
|
||||||
formatPercentage,
|
formatPercentage,
|
||||||
getUsageStatusColor,
|
|
||||||
getUsageStatusIcon,
|
getUsageStatusIcon,
|
||||||
calculateUsagePercentage
|
calculateUsagePercentage
|
||||||
} from '../../services/billingService';
|
} from '../../services/billingService';
|
||||||
@@ -53,7 +48,6 @@ const BillingOverview: React.FC<BillingOverviewProps> = ({
|
|||||||
|
|
||||||
const getStatusChip = () => {
|
const getStatusChip = () => {
|
||||||
const status = usageStats.usage_status;
|
const status = usageStats.usage_status;
|
||||||
const color = getUsageStatusColor(status);
|
|
||||||
const icon = getUsageStatusIcon(status);
|
const icon = getUsageStatusIcon(status);
|
||||||
|
|
||||||
let chipColor: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default';
|
let chipColor: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -26,9 +26,6 @@ import { billingService } from '../../services/billingService';
|
|||||||
import { monitoringService } from '../../services/monitoringService';
|
import { monitoringService } from '../../services/monitoringService';
|
||||||
import { onApiEvent } from '../../utils/apiEvents';
|
import { onApiEvent } from '../../utils/apiEvents';
|
||||||
|
|
||||||
// Components
|
|
||||||
import ComprehensiveAPIBreakdown from './ComprehensiveAPIBreakdown';
|
|
||||||
|
|
||||||
interface CompactBillingDashboardProps {
|
interface CompactBillingDashboardProps {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
}
|
}
|
||||||
@@ -38,7 +35,6 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
|||||||
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
|
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -52,7 +48,6 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
|||||||
|
|
||||||
setDashboardData(billingData);
|
setDashboardData(billingData);
|
||||||
setSystemHealth(healthData);
|
setSystemHealth(healthData);
|
||||||
setLastUpdated(new Date());
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch data');
|
setError(err instanceof Error ? err.message : 'Failed to fetch data');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -62,6 +57,7 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [userId]);
|
}, [userId]);
|
||||||
|
|
||||||
// Event-driven refresh
|
// Event-driven refresh
|
||||||
@@ -77,11 +73,11 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
|||||||
fetchData();
|
fetchData();
|
||||||
});
|
});
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const formatCurrency = (amount: number) => `$${amount.toFixed(4)}`;
|
const formatCurrency = (amount: number) => `$${amount.toFixed(4)}`;
|
||||||
const formatNumber = (num: number) => num.toLocaleString();
|
const formatNumber = (num: number) => num.toLocaleString();
|
||||||
const formatPercentage = (num: number) => `${num.toFixed(1)}%`;
|
|
||||||
|
|
||||||
if (loading && !dashboardData) {
|
if (loading && !dashboardData) {
|
||||||
return (
|
return (
|
||||||
@@ -118,9 +114,7 @@ const CompactBillingDashboard: React.FC<CompactBillingDashboardProps> = ({ userI
|
|||||||
|
|
||||||
if (!dashboardData) return null;
|
if (!dashboardData) return null;
|
||||||
|
|
||||||
const { current_usage, trends, limits, alerts } = dashboardData;
|
const { current_usage, limits, alerts } = dashboardData;
|
||||||
const activeProviders = Object.entries(current_usage.provider_breakdown)
|
|
||||||
.filter(([_, data]) => data.cost > 0);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|||||||
@@ -10,26 +10,15 @@ import {
|
|||||||
Accordion,
|
Accordion,
|
||||||
AccordionSummary,
|
AccordionSummary,
|
||||||
AccordionDetails,
|
AccordionDetails,
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Paper,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { ExpandMore } from '@mui/icons-material';
|
import { ExpandMore } from '@mui/icons-material';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
Info,
|
Info,
|
||||||
DollarSign,
|
|
||||||
Activity,
|
|
||||||
Zap,
|
|
||||||
Search,
|
Search,
|
||||||
Image,
|
Image,
|
||||||
Code,
|
Code,
|
||||||
Database,
|
Database,
|
||||||
Globe,
|
|
||||||
FileText,
|
FileText,
|
||||||
BarChart3
|
BarChart3
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|||||||
@@ -8,11 +8,8 @@ import {
|
|||||||
Chip,
|
Chip,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts';
|
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';
|
||||||
import {
|
import {
|
||||||
DollarSign,
|
|
||||||
TrendingUp,
|
|
||||||
BarChart3,
|
|
||||||
PieChart as PieChartIcon
|
PieChart as PieChartIcon
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,9 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
Grid,
|
Grid,
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
Typography,
|
||||||
Alert,
|
Alert,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
useTheme,
|
|
||||||
useMediaQuery,
|
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
ToggleButtonGroup,
|
ToggleButtonGroup,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -18,14 +14,6 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
DollarSign,
|
|
||||||
TrendingUp,
|
|
||||||
AlertTriangle,
|
|
||||||
Activity,
|
|
||||||
Zap,
|
|
||||||
BarChart3,
|
|
||||||
PieChart,
|
|
||||||
Clock,
|
|
||||||
Grid3X3,
|
Grid3X3,
|
||||||
List,
|
List,
|
||||||
Info,
|
Info,
|
||||||
@@ -61,12 +49,8 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
|
|||||||
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
|
|
||||||
const [viewMode, setViewMode] = useState<ViewMode>('compact');
|
const [viewMode, setViewMode] = useState<ViewMode>('compact');
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
|
||||||
|
|
||||||
const fetchDashboardData = async () => {
|
const fetchDashboardData = async () => {
|
||||||
try {
|
try {
|
||||||
const [billingData, healthData] = await Promise.all([
|
const [billingData, healthData] = await Promise.all([
|
||||||
@@ -75,7 +59,6 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
|
|||||||
]);
|
]);
|
||||||
setDashboardData(billingData);
|
setDashboardData(billingData);
|
||||||
setSystemHealth(healthData);
|
setSystemHealth(healthData);
|
||||||
setLastUpdated(new Date());
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(error instanceof Error ? error.message : 'Failed to fetch dashboard data');
|
setError(error instanceof Error ? error.message : 'Failed to fetch dashboard data');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -95,7 +78,6 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
|
|||||||
.then(([billingData, health]) => {
|
.then(([billingData, health]) => {
|
||||||
setDashboardData(billingData);
|
setDashboardData(billingData);
|
||||||
setSystemHealth(health);
|
setSystemHealth(health);
|
||||||
setLastUpdated(new Date());
|
|
||||||
})
|
})
|
||||||
.catch(() => {/* ignore */});
|
.catch(() => {/* ignore */});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Collapse,
|
Collapse,
|
||||||
Alert,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
BarChart3,
|
|
||||||
Calendar
|
Calendar
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ import { motion } from 'framer-motion';
|
|||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
AlertTriangle,
|
|
||||||
CheckCircle,
|
|
||||||
XCircle,
|
|
||||||
Clock,
|
Clock,
|
||||||
Zap
|
Zap
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
@@ -27,7 +24,6 @@ import { SystemHealth } from '../../types/monitoring';
|
|||||||
import {
|
import {
|
||||||
getHealthStatusColor,
|
getHealthStatusColor,
|
||||||
getHealthStatusIcon,
|
getHealthStatusIcon,
|
||||||
formatResponseTime,
|
|
||||||
formatErrorRate,
|
formatErrorRate,
|
||||||
getPerformanceStatus
|
getPerformanceStatus
|
||||||
} from '../../services/monitoringService';
|
} from '../../services/monitoringService';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
||||||
import { Box, Typography, Button, Alert, Stack } from '@mui/material';
|
import { Typography, Button, Alert, Stack } from '@mui/material';
|
||||||
import { Refresh as RefreshIcon } from '@mui/icons-material';
|
import { Refresh as RefreshIcon } from '@mui/icons-material';
|
||||||
|
|
||||||
interface ComponentErrorBoundaryProps {
|
interface ComponentErrorBoundaryProps {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Box, Typography, Chip, Button, CircularProgress, Tooltip } from '@mui/material';
|
import { Box, Typography, Chip, Button, Tooltip } from '@mui/material';
|
||||||
import { PlayArrow, Pause, Stop } from '@mui/icons-material';
|
import { PlayArrow } from '@mui/icons-material';
|
||||||
import { ShimmerHeader } from './styled';
|
import { ShimmerHeader } from './styled';
|
||||||
import UserBadge from './UserBadge';
|
import UserBadge from './UserBadge';
|
||||||
import { DashboardHeaderProps } from './types';
|
import { DashboardHeaderProps } from './types';
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Alert,
|
Alert,
|
||||||
Collapse,
|
Collapse,
|
||||||
IconButton,
|
|
||||||
Divider
|
Divider
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Container, Alert, Button } from '@mui/material';
|
import { Container, Alert, Button } from '@mui/material';
|
||||||
import { DashboardContainer } from './styled';
|
import { DashboardContainer } from './styled';
|
||||||
import { ErrorDisplayProps } from './types';
|
import { ErrorDisplayProps } from './types';
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import { useCopilotReadable } from '@copilotkit/react-core';
|
|||||||
import {
|
import {
|
||||||
WritingPersona,
|
WritingPersona,
|
||||||
PlatformAdaptation,
|
PlatformAdaptation,
|
||||||
PlatformType,
|
PlatformType
|
||||||
UserPersonasResponse
|
|
||||||
} from '../../../types/PlatformPersonaTypes';
|
} from '../../../types/PlatformPersonaTypes';
|
||||||
import {
|
import {
|
||||||
getUserPersonas,
|
getUserPersonas,
|
||||||
@@ -37,6 +36,9 @@ interface PlatformPersonaProviderProps {
|
|||||||
userId?: number; // Default to 1 for now, can be enhanced with auth context later
|
userId?: number; // Default to 1 for now, can be enhanced with auth context later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache duration: 5 minutes (constant outside component to avoid dependency issues)
|
||||||
|
const CACHE_DURATION = 5 * 60 * 1000;
|
||||||
|
|
||||||
// Provider component
|
// Provider component
|
||||||
export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = ({
|
export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = ({
|
||||||
children,
|
children,
|
||||||
@@ -54,9 +56,6 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
|||||||
const requestInProgress = useRef<boolean>(false);
|
const requestInProgress = useRef<boolean>(false);
|
||||||
const dataCacheTime = useRef<number>(0);
|
const dataCacheTime = useRef<number>(0);
|
||||||
|
|
||||||
// Cache duration: 5 minutes
|
|
||||||
const CACHE_DURATION = 5 * 60 * 1000;
|
|
||||||
|
|
||||||
// Fetch persona data function
|
// Fetch persona data function
|
||||||
const fetchPersonas = useCallback(async () => {
|
const fetchPersonas = useCallback(async () => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -253,7 +252,7 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
|||||||
dataCacheTime.current = Date.now();
|
dataCacheTime.current = Date.now();
|
||||||
requestInProgress.current = false;
|
requestInProgress.current = false;
|
||||||
}
|
}
|
||||||
}, [userId, platform, corePersona]);
|
}, [userId, platform, corePersona, platformPersona]);
|
||||||
|
|
||||||
// Initial data fetch
|
// Initial data fetch
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Avatar, Box, Button, Menu, MenuItem, Typography, Tooltip } from '@mui/material';
|
import { Avatar, Box, Menu, MenuItem, Typography, Tooltip } from '@mui/material';
|
||||||
import { useUser, useClerk } from '@clerk/clerk-react';
|
import { useUser, useClerk } from '@clerk/clerk-react';
|
||||||
|
|
||||||
interface UserBadgeProps {
|
interface UserBadgeProps {
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
LineChart,
|
|
||||||
Line,
|
Line,
|
||||||
BarChart,
|
BarChart,
|
||||||
Bar,
|
Bar,
|
||||||
PieChart,
|
PieChart,
|
||||||
Pie,
|
Pie,
|
||||||
Cell,
|
Cell,
|
||||||
AreaChart,
|
|
||||||
Area,
|
Area,
|
||||||
XAxis,
|
XAxis,
|
||||||
YAxis,
|
YAxis,
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ function validateContextIntegrity(state: StrategyCalendarState): boolean {
|
|||||||
|
|
||||||
// Check if calendar context is valid when it exists
|
// Check if calendar context is valid when it exists
|
||||||
if (state.calendarContext) {
|
if (state.calendarContext) {
|
||||||
const { strategyContext, autoPopulatedData } = state.calendarContext;
|
const { strategyContext } = state.calendarContext;
|
||||||
if (strategyContext && !validateContextIntegrity({ ...state, strategyContext })) {
|
if (strategyContext && !validateContextIntegrity({ ...state, strategyContext })) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ import React from 'react';
|
|||||||
import {
|
import {
|
||||||
Article as ArticleIcon,
|
Article as ArticleIcon,
|
||||||
Search as SearchIcon,
|
Search as SearchIcon,
|
||||||
Campaign as CampaignIcon,
|
|
||||||
Analytics as AnalyticsIcon,
|
Analytics as AnalyticsIcon,
|
||||||
Psychology as PsychologyIcon,
|
Psychology as PsychologyIcon,
|
||||||
AutoAwesome as AutoAwesomeIcon,
|
AutoAwesome as AutoAwesomeIcon,
|
||||||
Speed as SpeedIcon,
|
Speed as SpeedIcon,
|
||||||
Business as BusinessIcon,
|
Business as BusinessIcon,
|
||||||
SocialDistance as SocialIcon,
|
SocialDistance as SocialIcon,
|
||||||
Create as CreateIcon,
|
|
||||||
Dashboard as DashboardIcon,
|
Dashboard as DashboardIcon,
|
||||||
Facebook as FacebookIcon,
|
Facebook as FacebookIcon,
|
||||||
LinkedIn as LinkedInIcon,
|
LinkedIn as LinkedInIcon,
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export interface UsePersonaPollingReturn {
|
|||||||
export function usePersonaPolling(options: UsePersonaPollingOptions = {}): UsePersonaPollingReturn {
|
export function usePersonaPolling(options: UsePersonaPollingOptions = {}): UsePersonaPollingReturn {
|
||||||
const {
|
const {
|
||||||
interval = 2000, // 2 seconds default
|
interval = 2000, // 2 seconds default
|
||||||
maxAttempts = 0, // No timeout - poll until backend says done
|
|
||||||
onProgress,
|
onProgress,
|
||||||
onComplete,
|
onComplete,
|
||||||
onError
|
onError
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export function usePolling(
|
|||||||
): UsePollingReturn {
|
): UsePollingReturn {
|
||||||
const {
|
const {
|
||||||
interval = 2000, // 2 seconds default
|
interval = 2000, // 2 seconds default
|
||||||
maxAttempts = 0, // No timeout - poll until backend says done
|
|
||||||
onProgress,
|
onProgress,
|
||||||
onComplete,
|
onComplete,
|
||||||
onError
|
onError
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ export const useWixConnection = () => {
|
|||||||
const tokensRaw = sessionStorage.getItem('wix_tokens');
|
const tokensRaw = sessionStorage.getItem('wix_tokens');
|
||||||
|
|
||||||
if (connectedFlag && tokensRaw) {
|
if (connectedFlag && tokensRaw) {
|
||||||
const tokens = JSON.parse(tokensRaw);
|
|
||||||
|
|
||||||
// Set connected status with site information from tokens
|
// Set connected status with site information from tokens
|
||||||
setStatus({
|
setStatus({
|
||||||
connected: true,
|
connected: true,
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
|
|||||||
|
|
||||||
loadOnboardingIntegration: async (strategyId) => {
|
loadOnboardingIntegration: async (strategyId) => {
|
||||||
try {
|
try {
|
||||||
const integration = await contentPlanningApi.getOnboardingIntegration(strategyId);
|
await contentPlanningApi.getOnboardingIntegration(strategyId);
|
||||||
// Handle onboarding integration data
|
// Handle onboarding integration data
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to load onboarding integration:', error);
|
console.error('Failed to load onboarding integration:', error);
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"builds": [
|
"buildCommand": "npm run build",
|
||||||
{
|
"outputDirectory": "build",
|
||||||
"src": "package.json",
|
|
||||||
"use": "@vercel/static-build",
|
|
||||||
"config": {
|
|
||||||
"distDir": "build"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"src": "/(.*)",
|
"src": "/(.*)",
|
||||||
|
|||||||
Reference in New Issue
Block a user