'use client'; import React, { useState, useEffect, useMemo } from 'react'; import Link from 'next/link'; import { FaTimes, FaTh, FaList } from 'react-icons/fa'; // Interface should match the structure from the API interface ProcessedProject { id: string; owner: string; repo: string; name: string; repo_type: string; submittedAt: number; language: string; } interface ProcessedProjectsProps { showHeader?: boolean; maxItems?: number; className?: string; messages?: Record>; // Translation messages with proper typing } export default function ProcessedProjects({ showHeader = true, maxItems, className = "", messages }: ProcessedProjectsProps) { const [projects, setProjects] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [viewMode, setViewMode] = useState<'card' | 'list'>('card'); // Default messages fallback const defaultMessages = { title: 'Processed Wiki Projects', searchPlaceholder: 'Search projects by name, owner, or repository...', noProjects: 'No projects found in the server cache. The cache might be empty or the server encountered an issue.', noSearchResults: 'No projects match your search criteria.', processedOn: 'Processed on:', loadingProjects: 'Loading projects...', errorLoading: 'Error loading projects:', backToHome: 'Back to Home' }; const t = (key: string) => { if (messages?.projects?.[key]) { return messages.projects[key]; } return defaultMessages[key as keyof typeof defaultMessages] || key; }; useEffect(() => { const fetchProjects = async () => { setIsLoading(true); setError(null); try { const response = await fetch('/api/wiki/projects'); if (!response.ok) { throw new Error(`Failed to fetch projects: ${response.statusText}`); } const data = await response.json(); if (data.error) { throw new Error(data.error); } setProjects(data as ProcessedProject[]); } catch (e: unknown) { console.error("Failed to load projects from API:", e); const message = e instanceof Error ? e.message : "An unknown error occurred."; setError(message); setProjects([]); } finally { setIsLoading(false); } }; fetchProjects(); }, []); // Filter projects based on search query const filteredProjects = useMemo(() => { if (!searchQuery.trim()) { return maxItems ? projects.slice(0, maxItems) : projects; } const query = searchQuery.toLowerCase(); const filtered = projects.filter(project => project.name.toLowerCase().includes(query) || project.owner.toLowerCase().includes(query) || project.repo.toLowerCase().includes(query) || project.repo_type.toLowerCase().includes(query) ); return maxItems ? filtered.slice(0, maxItems) : filtered; }, [projects, searchQuery, maxItems]); const clearSearch = () => { setSearchQuery(''); }; const handleDelete = async (project: ProcessedProject) => { if (!confirm(`Are you sure you want to delete project ${project.name}?`)) { return; } try { const response = await fetch('/api/wiki/projects', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ owner: project.owner, repo: project.repo, repo_type: project.repo_type, language: project.language, }), }); if (!response.ok) { const errorBody = await response.json().catch(() => ({ error: response.statusText })); throw new Error(errorBody.error || response.statusText); } setProjects(prev => prev.filter(p => p.id !== project.id)); } catch (e: unknown) { console.error('Failed to delete project:', e); alert(`Failed to delete project: ${e instanceof Error ? e.message : 'Unknown error'}`); } }; return (
{showHeader && (

{t('title')}

{t('backToHome')}
)} {/* Search Bar and View Toggle */}
{/* Search Bar */}
setSearchQuery(e.target.value)} placeholder={t('searchPlaceholder')} className="input-japanese block w-full pl-4 pr-12 py-2.5 border border-[var(--border-color)] rounded-lg bg-[var(--background)] text-[var(--foreground)] placeholder:text-[var(--muted)] focus:outline-none focus:border-[var(--accent-primary)] focus:ring-1 focus:ring-[var(--accent-primary)]" /> {searchQuery && ( )}
{/* View Toggle */}
{isLoading &&

{t('loadingProjects')}

} {error &&

{t('errorLoading')} {error}

} {!isLoading && !error && filteredProjects.length > 0 && (
{filteredProjects.map((project) => ( viewMode === 'card' ? (

{project.name}

{project.repo_type} {project.language}

{t('processedOn')} {new Date(project.submittedAt).toLocaleDateString()}

) : (

{project.name}

{t('processedOn')} {new Date(project.submittedAt).toLocaleDateString()} • {project.repo_type} • {project.language}

{project.repo_type}
) ))}
)} {!isLoading && !error && projects.length > 0 && filteredProjects.length === 0 && searchQuery && (

{t('noSearchResults')}

)} {!isLoading && !error && projects.length === 0 && (

{t('noProjects')}

)}
); }