• DataTable component.

    Renders a paginated, scrollable table of Kenmei manga items with status badges and load more functionality.

    Columns are dynamically shown based on available data (title, status, chapters, volumes, score, last read date). Includes "Load More" support and an empty state.

    Returns Element

    React.ReactElement

    <DataTable data={myMangaList} itemsPerPage={25} />
    
    export function DataTable({ data, itemsPerPage = 50 }: DataTableProps) {
    const [visibleData, setVisibleData] = useState<KenmeiMangaItem[]>([]);
    const [displayCount, setDisplayCount] = useState(itemsPerPage);
    const [isLoading, setIsLoading] = useState(false);

    // Update visible data when display count changes
    useEffect(() => {
    setVisibleData(data.slice(0, displayCount));
    }, [data, displayCount]);

    // Reset when data changes
    useEffect(() => {
    setDisplayCount(itemsPerPage);
    setVisibleData(data.slice(0, itemsPerPage));
    }, [data, itemsPerPage]);

    // Load more items
    const handleLoadMore = () => {
    if (displayCount < data.length) {
    setIsLoading(true);

    // Use setTimeout to give a small delay for better UX
    setTimeout(() => {
    // Don't exceed the original data length
    setDisplayCount((prev) => Math.min(prev + itemsPerPage, data.length));
    setIsLoading(false);
    }, 300);
    }
    };

    // Determine which columns to show based on data
    const hasScore = data.some(
    (item) => item.score !== undefined && item.score > 0,
    );
    const hasChapters = data.some(
    (item) => item.chapters_read !== undefined && item.chapters_read > 0,
    );
    const hasVolumes = data.some(
    (item) => item.volumes_read !== undefined && item.volumes_read > 0,
    );
    const hasLastRead = data.some((item) => item.updated_at || item.created_at);

    // Format date to a readable format
    const formatDate = (dateString: string | undefined) => {
    if (!dateString) return "-";
    try {
    const date = new Date(dateString);
    return date.toLocaleDateString();
    } catch {
    return "-";
    }
    };

    // Get status badge styling
    const getStatusBadge = (status: string) => {
    switch (status) {
    case "reading":
    return (
    <Badge
    variant="outline"
    className="border-green-200 bg-green-100 text-green-800 hover:bg-green-100 dark:border-green-800 dark:bg-green-900/30 dark:text-green-400"
    >
    {status.replace(/_/g, " ")}
    </Badge>
    );
    case "completed":
    return (
    <Badge
    variant="outline"
    className="border-blue-200 bg-blue-100 text-blue-800 hover:bg-blue-100 dark:border-blue-800 dark:bg-blue-900/30 dark:text-blue-400"
    >
    {status.replace(/_/g, " ")}
    </Badge>
    );
    case "on_hold":
    return (
    <Badge
    variant="outline"
    className="border-amber-200 bg-amber-100 text-amber-800 hover:bg-amber-100 dark:border-amber-800 dark:bg-amber-900/30 dark:text-amber-400"
    >
    {status.replace(/_/g, " ")}
    </Badge>
    );
    case "dropped":
    return (
    <Badge
    variant="outline"
    className="border-red-200 bg-red-100 text-red-800 hover:bg-red-100 dark:border-red-800 dark:bg-red-900/30 dark:text-red-400"
    >
    {status.replace(/_/g, " ")}
    </Badge>
    );
    case "plan_to_read":
    return (
    <Badge
    variant="outline"
    className="border-purple-200 bg-purple-100 text-purple-800 hover:bg-purple-100 dark:border-purple-800 dark:bg-purple-900/30 dark:text-purple-400"
    >
    {status.replace(/_/g, " ")}
    </Badge>
    );
    default:
    return (
    <Badge
    variant="outline"
    className="border-gray-200 bg-gray-100 text-gray-800 hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-900/30 dark:text-gray-400"
    >
    {status.replace(/_/g, " ")}
    </Badge>
    );
    }
    };

    return (
    <div className="rounded-md border">
    <ScrollArea className="h-[500px] rounded-md">
    <Table>
    <TableCaption>
    Showing {visibleData.length} of {data.length} entries
    </TableCaption>

    <TableHeader className="bg-muted/50 sticky top-0 backdrop-blur-sm">
    <TableRow>
    <TableHead className="w-[45%] min-w-[200px]">Title</TableHead>
    <TableHead>Status</TableHead>

    {hasChapters && <TableHead className="w-[80px]">Ch</TableHead>}
    {hasVolumes && <TableHead className="w-[80px]">Vol</TableHead>}
    {hasScore && <TableHead className="w-[80px]">Score</TableHead>}

    {hasLastRead && (
    <TableHead className="w-[120px]">Last Read</TableHead>
    )}
    </TableRow>
    </TableHeader>

    <TableBody>
    {visibleData.map((item, index) => (
    <TableRow key={index} className="hover:bg-muted/40">
    <TableCell
    className="max-w-[300px] truncate font-medium"
    title={item.title}
    >
    {item.title}
    </TableCell>

    <TableCell>{getStatusBadge(item.status)}</TableCell>

    {hasChapters && (
    <TableCell className="text-muted-foreground">
    {item.chapters_read || "-"}
    </TableCell>
    )}

    {hasVolumes && (
    <TableCell className="text-muted-foreground">
    {item.volumes_read || "-"}
    </TableCell>
    )}

    {hasScore && (
    <TableCell className="text-muted-foreground">
    {item.score ? item.score.toFixed(1) : "-"}
    </TableCell>
    )}

    {hasLastRead && (
    <TableCell
    className="text-muted-foreground"
    title={item.updated_at || item.created_at}
    >
    {formatDate(item.updated_at || item.created_at)}
    </TableCell>
    )}
    </TableRow>
    ))}

    {/* Empty state if no items */}
    {visibleData.length === 0 && !isLoading && (
    <TableRow>
    <TableCell
    colSpan={
    2 +
    (hasChapters ? 1 : 0) +
    (hasVolumes ? 1 : 0) +
    (hasScore ? 1 : 0) +
    (hasLastRead ? 1 : 0)
    }
    className="h-24 text-center"
    >
    No manga entries found
    </TableCell>
    </TableRow>
    )}
    </TableBody>
    </Table>
    </ScrollArea>

    {/* Load more button */}
    {visibleData.length < data.length && (
    <div className="flex justify-center border-t p-4">
    <Button
    variant="outline"
    onClick={handleLoadMore}
    disabled={isLoading}
    className="w-full max-w-xs"
    >
    {isLoading ? (
    <>
    <Loader2 className="mr-2 h-4 w-4 animate-spin" />
    Loading...
    </>
    ) : (
    <>
    <ChevronDown className="mr-2 h-4 w-4" />
    Load More ({data.length - visibleData.length} remaining)
    </>
    )}
    </Button>
    </div>
    )}
    </div>
    );
    }