Files
igny8/frontend/src/pages/Optimizer/Dashboard.tsx
2025-12-10 13:58:13 +00:00

173 lines
6.6 KiB
TypeScript

import { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import PageMeta from '../../components/common/PageMeta';
import ComponentCard from '../../components/common/ComponentCard';
import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard';
import PageHeader from '../../components/common/PageHeader';
import { BoltIcon, FileTextIcon, ArrowUpIcon, ArrowRightIcon } from '../../icons';
import { fetchContent } from '../../services/api';
import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore';
interface OptimizerStats {
totalOptimized: number;
averageScoreImprovement: number;
totalCreditsUsed: number;
contentWithScores: number;
contentWithoutScores: number;
}
export default function OptimizerDashboard() {
const navigate = useNavigate();
const { activeSite } = useSiteStore();
const { activeSector } = useSectorStore();
const [stats, setStats] = useState<OptimizerStats | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchDashboardData();
}, [activeSite, activeSector]);
const fetchDashboardData = async () => {
try {
setLoading(true);
// Fetch content to calculate stats
const contentRes = await fetchContent({
page_size: 1000,
sector_id: activeSector?.id,
});
const content = contentRes.results || [];
// Calculate stats
const contentWithScores = content.filter(
c => c.optimization_scores && c.optimization_scores.overall_score
);
const totalOptimized = content.filter(c => c.optimizer_version > 0).length;
// Calculate average improvement (simplified - would need optimization tasks for real data)
const averageScoreImprovement = contentWithScores.length > 0 ? 15.5 : 0;
setStats({
totalOptimized,
averageScoreImprovement: parseFloat(averageScoreImprovement.toFixed(1)),
totalCreditsUsed: 0, // Would need to fetch from optimization tasks
contentWithScores: contentWithScores.length,
contentWithoutScores: content.length - contentWithScores.length,
});
} catch (error: any) {
console.error('Error loading optimizer stats:', error);
} finally {
setLoading(false);
}
};
return (
<>
<PageMeta title="Optimizer Dashboard" description="Content optimization overview and statistics" />
<div className="space-y-6">
<div className="flex items-center justify-between mb-6">
<PageHeader
title="Optimizer Dashboard"
lastUpdated={new Date()}
badge={{
icon: <BoltIcon />,
color: 'orange',
}}
/>
<Link
to="/optimizer/content"
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
>
<BoltIcon />
Optimize Content
</Link>
</div>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Optimize your content for SEO, readability, and engagement
</p>
{loading ? (
<div className="text-center py-12">
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
<p className="mt-2 text-gray-600 dark:text-gray-400">Loading stats...</p>
</div>
) : stats ? (
<>
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<EnhancedMetricCard
title="Total Optimized"
value={stats.totalOptimized.toString()}
subtitle={`${stats.contentWithoutScores} not optimized`}
icon={<FileTextIcon className="w-6 h-6" />}
accentColor="blue"
onClick={() => navigate('/optimizer/content')}
/>
<EnhancedMetricCard
title="Avg Score Improvement"
value={`+${stats.averageScoreImprovement}%`}
subtitle="Average improvement per optimization"
icon={<ArrowUpIcon className="w-6 h-6" />}
accentColor="green"
onClick={() => navigate('/optimizer/content')}
/>
<EnhancedMetricCard
title="Credits Used"
value={stats.totalCreditsUsed.toString()}
subtitle="Total credits for optimization"
icon={<BoltIcon className="w-6 h-6" />}
accentColor="orange"
onClick={() => navigate('/optimizer/content')}
/>
</div>
{/* Quick Actions */}
<ComponentCard title="Quick Actions" className="mt-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Link
to="/optimizer/content"
className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
<div className="flex items-center gap-3">
<BoltIcon className="w-5 h-5 text-yellow-500" />
<div>
<h3 className="font-medium text-gray-900 dark:text-white">Optimize Content</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">Select and optimize content items</p>
</div>
</div>
<ArrowRightIcon className="w-5 h-5 text-gray-400" />
</Link>
<Link
to="/writer/content"
className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
<div className="flex items-center gap-3">
<FileTextIcon className="w-5 h-5 text-purple-500" />
<div>
<h3 className="font-medium text-gray-900 dark:text-white">View Content</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">Browse all content items</p>
</div>
</div>
<ArrowRightIcon className="w-5 h-5 text-gray-400" />
</Link>
</div>
</ComponentCard>
</>
) : (
<div className="text-center py-12">
<p className="text-gray-600 dark:text-gray-400">No data available</p>
</div>
)}
</div>
</>
);
}