Files
wikiplus/components/related-articles.tsx
T
2026-01-24 16:52:19 -06:00

120 lines
3.7 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { ArrowRight, Info } from 'lucide-react';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import type { SearchResult } from '@/app/api/search/route';
interface RelatedArticlesProps {
title: string;
currentSlug: string;
}
export function RelatedArticles({ title, currentSlug }: RelatedArticlesProps) {
const [articles, setArticles] = useState<SearchResult[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchRelatedArticles = async () => {
try {
const response = await fetch(`/api/search?query=${encodeURIComponent(title)}`);
if (!response.ok) throw new Error('Failed to fetch related articles');
const data = await response.json();
const results = data.results || [];
// Filter out the current article and take top 5
const filtered = results
.filter((article: SearchResult) =>
article.title.replace(/ /g, '_').toLowerCase() !==
currentSlug.toLowerCase()
)
.slice(0, 5);
setArticles(filtered);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
fetchRelatedArticles();
}, [title, currentSlug]);
if (loading) {
return (
<aside className="space-y-4">
<h3 className="font-semibold text-foreground flex items-center gap-1.5">
Related articles
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-3 h-3 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>This is currently in beta and may be inaccurate.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</h3>
<div className="space-y-3">
{[...Array(3)].map((_, i) => (
<div key={i} className="animate-pulse">
<div className="h-4 bg-muted rounded w-3/4 mb-2"></div>
<div className="h-3 bg-muted rounded w-full"></div>
</div>
))}
</div>
</aside>
);
}
if (error || articles.length === 0) {
return null;
}
return (
<aside className="space-y-4">
<h3 className="font-semibold text-foreground flex items-center gap-1.5">
Related articles
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-3 h-3 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>This is currently in beta and may be inaccurate.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</h3>
<ul className="space-y-3">
{articles.map((article) => {
const slug = article.title.replace(/ /g, '_').replace(/%20/g, '_');
return (
<li key={article.title}>
<Link
href={`/article/${slug}`}
className="group flex items-start gap-2 text-sm hover:opacity-80 transition-opacity"
>
<ArrowRight className="w-4 h-4 text-muted-foreground mt-0.5 flex-shrink-0 group-hover:text-blue-500 transition-colors" />
<span className="text-muted-foreground group-hover:text-foreground transition-colors">
{article.title}
</span>
</Link>
</li>
);
})}
</ul>
</aside>
);
}