import React, { useState, useCallback } from "react"; import type { LoaderFunctionArgs } from "@remix-run/node"; import { useLoaderData, useRevalidator } from "@remix-run/react"; import { getProducts } from "@repo/db/index"; import { ProductGrid } from "@repo/ui/components/products/index"; import { ProductCreate } from "@repo/ui/components/products/create"; import { PaginatedResponse } from "@repo/ui/components/template/table-show"; import { Button } from "@repo/ui/components/button"; interface Product { id: string; name: string; description: string; price: number; imageUrl?: string; isPublished?: boolean; created_at?: string; updated_at?: string; } interface LoaderData { products: PaginatedResponse; error: string | null; } export async function loader({ request, }: LoaderFunctionArgs): Promise { try { const url = new URL(request.url); const page = parseInt(url.searchParams.get("page") || "1"); const limit = parseInt(url.searchParams.get("limit") || "10"); const result = await getProducts(page, limit); const products: PaginatedResponse = { data: result.data.map((product: any) => ({ ...product, id: String(product.id), description: product.description ?? "", imageUrl: product.imageUrl ?? undefined, isPublished: product.isPublished ?? false, created_at: product.createdAt ? product.createdAt.toISOString() : undefined, updated_at: product.updatedAt ? product.updatedAt.toISOString() : undefined, })), pagination: result.pagination, }; // Return raw object with Single Fetch return { products, error: null }; } catch (error: any) { console.error("Failed to fetch products:", error); // Return raw object with Single Fetch return { products: { data: [], pagination: { page: 1, limit: 10, totalCount: 0, totalPages: 0, hasNextPage: false, hasPreviousPage: false, }, }, error: error.message || "Failed to load products", }; } } export default function ProductPage() { const { products, error } = useLoaderData(); const revalidator = useRevalidator(); const [loading, setLoading] = useState(false); const [showCreateForm, setShowCreateForm] = useState(false); const handlePageChange = (page: number) => { // Use Remix navigation for better UX const url = new URL(window.location.href); url.searchParams.set("page", page.toString()); window.history.pushState({}, "", url); revalidator.revalidate(); }; const handleEdit = (product: Product) => { // TODO: Implement edit functionality console.log("Edit product:", product); }; const handleDelete = async (productId: string) => { if (!confirm("Are you sure you want to delete this product?")) { return; } try { const response = await fetch(`/api/products/${productId}`, { method: "DELETE", }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || "Failed to delete product"); } // Refresh products list revalidator.revalidate(); } catch (err: any) { console.error("Delete error:", err); alert(`Failed to delete product: ${err.message}`); } }; const handleProductCreated = useCallback(() => { // Refresh the entire page data using Remix revalidator revalidator.revalidate(); setShowCreateForm(false); }, [revalidator]); const handleFormCancel = () => { setShowCreateForm(false); }; // Show loading state when revalidating if (revalidator.state === "loading" && products.data.length === 0) { return (
Loading products...
); } // Show error state if (error && products.data.length === 0) { return (

Error Loading Products

{error}

); } return (
{/* Header Section */}

Products

{products.pagination.totalCount} product {products.pagination.totalCount !== 1 ? "s" : ""} found

{/* Create Product Form */} {showCreateForm && (
)} {/* Error Banner */} {error && products.data.length > 0 && (

Warning: {error}

)} {/* Products Grid */}
); }