228 lines
9.7 KiB
TypeScript
228 lines
9.7 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
interface SitemapStats {
|
|
totalUrls: number;
|
|
baseUrl: string;
|
|
lastGenerated: string;
|
|
routesByPriority: {
|
|
high: number;
|
|
medium: number;
|
|
low: number;
|
|
};
|
|
routesByChangeFreq: Record<string, number>;
|
|
}
|
|
|
|
interface ValidationResult {
|
|
valid: boolean;
|
|
errors: string[];
|
|
}
|
|
|
|
export default function SitemapInfo() {
|
|
const [stats, setStats] = useState<SitemapStats | null>(null);
|
|
const [validation, setValidation] = useState<ValidationResult | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const fetchStats = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const response = await fetch('/api/sitemap?action=stats');
|
|
const data = await response.json();
|
|
setStats(data);
|
|
} catch (error) {
|
|
console.error('Failed to fetch sitemap stats:', error);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
const fetchValidation = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const response = await fetch('/api/sitemap?action=validate');
|
|
const data = await response.json();
|
|
setValidation(data);
|
|
} catch (error) {
|
|
console.error('Failed to validate sitemap:', error);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchStats();
|
|
fetchValidation();
|
|
}, []);
|
|
|
|
if (loading && !stats) {
|
|
return (
|
|
<div className="p-6 bg-white rounded-lg shadow">
|
|
<div className="animate-pulse">
|
|
<div className="h-4 bg-gray-200 rounded w-1/4 mb-4"></div>
|
|
<div className="space-y-2">
|
|
<div className="h-3 bg-gray-200 rounded"></div>
|
|
<div className="h-3 bg-gray-200 rounded w-5/6"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Sitemap Stats */}
|
|
{stats && (
|
|
<div className="p-6 bg-white rounded-lg shadow">
|
|
<h3 className="text-lg font-semibold mb-4">Sitemap Statistics</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-600">Total URLs</p>
|
|
<p className="text-2xl font-bold text-blue-600">{stats.totalUrls}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-600">Base URL</p>
|
|
<p className="text-sm font-mono bg-gray-100 p-2 rounded">
|
|
{stats.baseUrl}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6">
|
|
<h4 className="font-medium mb-2">Priority Distribution</h4>
|
|
<div className="grid grid-cols-3 gap-4 text-center">
|
|
<div className="p-3 bg-green-50 rounded">
|
|
<p className="text-green-600 font-semibold">
|
|
{stats.routesByPriority.high}
|
|
</p>
|
|
<p className="text-xs text-gray-600">High (≥0.8)</p>
|
|
</div>
|
|
<div className="p-3 bg-yellow-50 rounded">
|
|
<p className="text-yellow-600 font-semibold">
|
|
{stats.routesByPriority.medium}
|
|
</p>
|
|
<p className="text-xs text-gray-600">Medium (0.5-0.8)</p>
|
|
</div>
|
|
<div className="p-3 bg-gray-50 rounded">
|
|
<p className="text-gray-600 font-semibold">
|
|
{stats.routesByPriority.low}
|
|
</p>
|
|
<p className="text-xs text-gray-600">Low (<0.5)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6">
|
|
<h4 className="font-medium mb-2">Change Frequency</h4>
|
|
<div className="space-y-1">
|
|
{Object.entries(stats.routesByChangeFreq).map(([freq, count]) => (
|
|
<div key={freq} className="flex justify-between text-sm">
|
|
<span className="capitalize">{freq}</span>
|
|
<span className="font-medium">{count}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Validation Results */}
|
|
{validation && (
|
|
<div className="p-6 bg-white rounded-lg shadow">
|
|
<h3 className="text-lg font-semibold mb-4">Validation Results</h3>
|
|
<div
|
|
className={`p-4 rounded-lg ${validation.valid ? 'bg-green-50 text-green-800' : 'bg-red-50 text-red-800'}`}
|
|
>
|
|
<div className="flex items-center">
|
|
{validation.valid ? (
|
|
<svg
|
|
className="w-5 h-5 mr-2"
|
|
fill="currentColor"
|
|
viewBox="0 0 20 20"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
) : (
|
|
<svg
|
|
className="w-5 h-5 mr-2"
|
|
fill="currentColor"
|
|
viewBox="0 0 20 20"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
)}
|
|
<span className="font-medium">
|
|
{validation.valid ? 'Sitemap is valid' : 'Sitemap has errors'}
|
|
</span>
|
|
</div>
|
|
{!validation.valid && validation.errors.length > 0 && (
|
|
<ul className="mt-2 space-y-1">
|
|
{validation.errors.map((error, index) => (
|
|
<li key={index} className="text-sm">
|
|
• {error}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Quick Actions */}
|
|
<div className="p-6 bg-white rounded-lg shadow">
|
|
<h3 className="text-lg font-semibold mb-4">Quick Actions</h3>
|
|
<div className="space-y-2">
|
|
<a
|
|
href="/sitemap.xml"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
|
|
>
|
|
<svg
|
|
className="w-4 h-4 mr-2"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
/>
|
|
</svg>
|
|
View Sitemap XML
|
|
</a>
|
|
<a
|
|
href="/robots.txt"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 transition-colors ml-2"
|
|
>
|
|
<svg
|
|
className="w-4 h-4 mr-2"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
/>
|
|
</svg>
|
|
View Robots.txt
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|