Files
stack/apps/web/src/components/knowledge/EntryList.tsx
T
2026-02-23 04:12:14 +00:00

127 lines
4.0 KiB
TypeScript

import type { KnowledgeEntryWithTags } from "@mosaic/shared";
import { EntryCard } from "./EntryCard";
import { BookOpen } from "lucide-react";
import { MosaicSpinner } from "@/components/ui/MosaicSpinner";
interface EntryListProps {
entries: KnowledgeEntryWithTags[];
isLoading: boolean;
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
}
export function EntryList({
entries,
isLoading,
currentPage,
totalPages,
onPageChange,
}: EntryListProps): React.JSX.Element {
if (isLoading) {
return (
<div className="flex justify-center items-center p-12">
<MosaicSpinner size={36} label="Loading entries..." />
</div>
);
}
if (entries.length === 0) {
return (
<div
className="text-center p-12 rounded-lg border"
style={{ background: "var(--surface)", borderColor: "var(--border)" }}
>
<BookOpen className="w-12 h-12 mx-auto mb-3" style={{ color: "var(--text-muted)" }} />
<p className="text-lg font-medium" style={{ color: "var(--text-muted)" }}>
No entries found
</p>
<p className="text-sm mt-2" style={{ color: "var(--text-muted)" }}>
Try adjusting your filters or create a new entry
</p>
</div>
);
}
return (
<div className="space-y-6">
{/* Entry cards */}
<div className="space-y-4">
{entries.map((entry) => (
<EntryCard key={entry.id} entry={entry} />
))}
</div>
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-2 pt-6">
<button
onClick={() => {
onPageChange(currentPage - 1);
}}
disabled={currentPage === 1}
className="px-4 py-2 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
Previous
</button>
<div className="flex items-center gap-1">
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => {
// Show first, last, current, and pages around current
const shouldShow =
page === 1 || page === totalPages || Math.abs(page - currentPage) <= 1;
// Show ellipsis
const showEllipsisBefore = page === currentPage - 2 && currentPage > 3;
const showEllipsisAfter = page === currentPage + 2 && currentPage < totalPages - 2;
if (!shouldShow && !showEllipsisBefore && !showEllipsisAfter) {
return null;
}
if (showEllipsisBefore || showEllipsisAfter) {
return (
<span key={`ellipsis-${String(page)}`} className="px-2 text-gray-500">
...
</span>
);
}
return (
<button
key={page}
onClick={() => {
onPageChange(page);
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
page === currentPage
? "bg-blue-600 text-white"
: "text-gray-700 hover:bg-gray-100"
}`}
>
{page}
</button>
);
})}
</div>
<button
onClick={() => {
onPageChange(currentPage + 1);
}}
disabled={currentPage === totalPages}
className="px-4 py-2 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
Next
</button>
</div>
)}
{/* Results info */}
<div className="text-center text-sm text-gray-500">
Page {currentPage} of {totalPages} ({entries.length} entries)
</div>
</div>
);
}