Files
igny8/frontend/src/pages/Optimizer/AnalysisPreview.tsx
IGNY8 VPS (Salman) ee56f9bbac Refactor frontend components to use new icon imports and improve default values
- Updated `EnhancedMetricCard` to set a default accent color to blue.
- Replaced `lucide-react` icons with custom icons in `LinkResults`, `OptimizationScores`, and various pages in the Automation and Optimizer sections.
- Enhanced button layouts in `AutomationRules`, `Tasks`, and `ContentSelector` for better alignment and user experience.
- Improved loading indicators across components for a more consistent UI experience.
2025-11-17 21:38:08 +00:00

156 lines
5.9 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router';
import PageMeta from '../../components/common/PageMeta';
import PageHeader from '../../components/common/PageHeader';
import { optimizerApi } from '../../api/optimizer.api';
import { fetchContent, Content as ContentType } from '../../services/api';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { OptimizationScores } from '../../components/optimizer/OptimizationScores';
import { ArrowLeftIcon, BoltIcon } from '../../icons';
export default function AnalysisPreview() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const toast = useToast();
const [content, setContent] = useState<ContentType | null>(null);
const [scores, setScores] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [analyzing, setAnalyzing] = useState(false);
useEffect(() => {
if (id) {
loadContent();
analyzeContent();
}
}, [id]);
const loadContent = async () => {
try {
setLoading(true);
// Note: fetchContent by ID would need to be implemented or use a different endpoint
// For now, we'll fetch and filter
const data = await fetchContent({ page_size: 1000 });
const found = data.results?.find((c: ContentType) => c.id === parseInt(id || '0'));
if (found) {
setContent(found);
}
} catch (error: any) {
console.error('Error loading content:', error);
toast.error(`Failed to load content: ${error.message}`);
} finally {
setLoading(false);
}
};
const analyzeContent = async () => {
if (!id) return;
try {
setAnalyzing(true);
const result = await optimizerApi.analyze(parseInt(id));
setScores(result.scores);
} catch (error: any) {
console.error('Error analyzing content:', error);
toast.error(`Failed to analyze content: ${error.message}`);
} finally {
setAnalyzing(false);
}
};
return (
<>
<PageMeta title="Content Analysis" description="Preview content optimization scores" />
<div className="space-y-6">
<div className="flex items-center justify-between mb-6">
<PageHeader
title="Content Analysis"
lastUpdated={new Date()}
badge={{
icon: <BoltIcon />,
color: 'orange',
}}
/>
<button
onClick={() => navigate(-1)}
className="inline-flex items-center gap-2 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4" />
Back
</button>
</div>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Preview optimization scores without optimizing
</p>
{loading || analyzing ? (
<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 ? 'Loading content...' : 'Analyzing content...'}
</p>
</div>
) : content && scores ? (
<div className="space-y-6">
{/* Content Info */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">
{content.title || 'Untitled'}
</h2>
<p className="text-sm text-gray-600 dark:text-gray-400">
Word Count: {content.word_count || 0} |
Source: {content.source} |
Status: {content.sync_status}
</p>
</div>
{/* Scores */}
<OptimizationScores scores={scores} />
{/* Score Details */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Score Details</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<span className="text-sm text-gray-600 dark:text-gray-400">Word Count:</span>
<span className="ml-2 font-medium text-gray-900 dark:text-white">{scores.word_count || 0}</span>
</div>
<div>
<span className="text-sm text-gray-600 dark:text-gray-400">Has Meta Title:</span>
<span className="ml-2 font-medium text-gray-900 dark:text-white">
{scores.has_meta_title ? 'Yes' : 'No'}
</span>
</div>
<div>
<span className="text-sm text-gray-600 dark:text-gray-400">Has Meta Description:</span>
<span className="ml-2 font-medium text-gray-900 dark:text-white">
{scores.has_meta_description ? 'Yes' : 'No'}
</span>
</div>
<div>
<span className="text-sm text-gray-600 dark:text-gray-400">Has Primary Keyword:</span>
<span className="ml-2 font-medium text-gray-900 dark:text-white">
{scores.has_primary_keyword ? 'Yes' : 'No'}
</span>
</div>
<div>
<span className="text-sm text-gray-600 dark:text-gray-400">Internal Links:</span>
<span className="ml-2 font-medium text-gray-900 dark:text-white">
{scores.internal_links_count || 0}
</span>
</div>
</div>
</div>
</div>
) : (
<div className="text-center py-12">
<p className="text-gray-600 dark:text-gray-400">Content not found</p>
</div>
)}
</div>
</>
);
}