diff --git a/client/src/pages/shop.tsx b/client/src/pages/shop.tsx
new file mode 100644
index 0000000..6bd873a
--- /dev/null
+++ b/client/src/pages/shop.tsx
@@ -0,0 +1,150 @@
+import { useState } from "react";
+import { useQuery } from "@tanstack/react-query";
+import { Link } from "wouter";
+import { Search, SlidersHorizontal } from "lucide-react";
+import { useCart } from "../hooks/use-cart";
+import { toast } from "../hooks/use-toast";
+
+interface Product {
+ id: number;
+ handle: string;
+ title: string;
+ description?: string;
+ price: string;
+ compareAtPrice?: string;
+ images?: string[];
+ status: string;
+ tags?: string[];
+ hsaFsa?: boolean;
+}
+
+function ProductCard({ product }: { product: Product }) {
+ const { addItem } = useCart();
+ const image = product.images?.[0];
+
+ const handleAddToCart = async (e: React.MouseEvent) => {
+ e.preventDefault();
+ try {
+ await addItem(product.id);
+ toast({ title: "Added to cart", description: product.title });
+ } catch {
+ toast({ title: "Error", description: "Could not add to cart", variant: "destructive" });
+ }
+ };
+
+ return (
+
+
+
+ {image ? (
+

+ ) : (
+
📦
+ )}
+ {product.hsaFsa && (
+
HSA/FSA
+ )}
+
+
+
{product.title}
+
+ ${parseFloat(product.price).toFixed(2)}
+ {product.compareAtPrice && parseFloat(product.compareAtPrice) > parseFloat(product.price) && (
+ ${parseFloat(product.compareAtPrice).toFixed(2)}
+ )}
+
+
+
+
+
+ );
+}
+
+export default function ShopPage() {
+ const [q, setQ] = useState("");
+ const [search, setSearch] = useState("");
+ const [page, setPage] = useState(1);
+
+ const { data, isLoading } = useQuery<{ products: Product[]; total: number; pageSize: number }>({
+ queryKey: ["/api/products", { q: search, page }],
+ queryFn: async () => {
+ const params = new URLSearchParams({ page: String(page), limit: "24" });
+ if (search) params.set("q", search);
+ const res = await fetch(`/api/products?${params}`);
+ return res.json();
+ },
+ });
+
+ const handleSearch = (e: React.FormEvent) => {
+ e.preventDefault();
+ setSearch(q);
+ setPage(1);
+ };
+
+ return (
+
+
+
+ {isLoading ? (
+
+ {Array.from({ length: 8 }).map((_, i) => (
+
+ ))}
+
+ ) : data?.products.length === 0 ? (
+
+
No products found
+ {search &&
}
+
+ ) : (
+ <>
+
+ {data?.products.map((p) =>
)}
+
+ {data && data.total > data.pageSize && (
+
+
+ Page {page}
+
+
+ )}
+ >
+ )}
+
+ );
+}