AwsLinker/app/components/SitemapInfo.tsx

228 lines
9.7 KiB
TypeScript
Raw Permalink Normal View History

2025-09-16 17:19:58 +08:00
'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 (&lt;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>
);
}