import { useEffect, useState } from "react"; interface FeedItem { id: string; title: string; link: string; pubDate: string; contentSnippet?: string; source?: { title?: string; url?: string; }; category?: string; } interface FeedListProps { searchTerm?: string; categoryFilter?: string; } export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedListProps = {}) { const [feeds, setFeeds] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [sortBy, setSortBy] = useState<'date' | 'title'>('date'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); useEffect(() => { fetchFeeds(); }, []); const fetchFeeds = async () => { try { setLoading(true); const response = await fetch("/api/feeds"); if (!response.ok) { throw new Error("フィードの取得に失敗しました"); } const data = await response.json(); setFeeds(data); setError(null); } catch (err) { setError(err instanceof Error ? err.message : "エラーが発生しました"); } finally { setLoading(false); } }; const filteredAndSortedFeeds = feeds .filter(feed => { const matchesSearch = !searchTerm || feed.title.toLowerCase().includes(searchTerm.toLowerCase()) || feed.contentSnippet?.toLowerCase().includes(searchTerm.toLowerCase()) || feed.source?.title?.toLowerCase().includes(searchTerm.toLowerCase()); const matchesCategory = !categoryFilter || feed.category === categoryFilter; return matchesSearch && matchesCategory; }) .sort((a, b) => { const multiplier = sortOrder === 'asc' ? 1 : -1; if (sortBy === 'date') { return (new Date(a.pubDate).getTime() - new Date(b.pubDate).getTime()) * multiplier; } else { return a.title.localeCompare(b.title) * multiplier; } }); const handleSort = (field: 'date' | 'title') => { if (sortBy === field) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortBy(field); setSortOrder('desc'); } }; const formatDate = (dateString: string) => { try { return new Date(dateString).toLocaleDateString('ja-JP', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch { return dateString; } }; if (loading) { return (
{[...Array(5)].map((_, i) => (
))}
); } if (error) { return (
⚠️

エラーが発生しました

{error}

); } return (
{/* Sort Controls */}
並び替え:
{filteredAndSortedFeeds.length} / {feeds.length} 件表示中
{/* Feed Cards */} {filteredAndSortedFeeds.length === 0 ? (

{searchTerm || categoryFilter ? '検索結果がありません' : 'フィードがありません'}

{searchTerm || categoryFilter ? '別のキーワードやカテゴリで検索してみてください' : 'RSSフィードを追加してバッチ処理を実行してください' }

) : (
{filteredAndSortedFeeds.map((feed, index) => (
{/* Article Icon */}
{/* Article Content */}
{/* Header */}

{feed.title}

{/* Meta Info */}
{feed.source?.title && ( {feed.source.title} )} {formatDate(feed.pubDate)}
{/* Category Badge */} {feed.category && ( {feed.category} )}
{/* Content Snippet */} {feed.contentSnippet && (

{feed.contentSnippet}

)} {/* Actions */}
))}
)}
); }