TanStackAdvanced
Complete CRUD — এক জায়গায়
সব টুকরো সংযুক্ত — Create, Read, Update, Delete-এর সম্পূর্ণ flow সংক্ষেপে — বাংলায়।
5টি অংশবাংলা ভাষায়
এখন সব অংশকে এক পেজে জড়িয়ে ফেলি। নিচের dashboard page এটাই project-এর heart — ডেটা collection, query composition, dialog orchestration সব।
src/app/products/page.tsx (client)tsx
tsx
"use client";
import { useEffect, useState } from "react";
import { ProductFilters } from "@/components/products/product-filters";
import { ProductList } from "@/components/products/product-list";
import { ProductPagination } from "@/components/products/product-pagination";
import { CreateEditDialog } from "@/components/products/create-edit-dialog";
import { DeleteDialog } from "@/components/products/delete-alert-dialog";
import { Button } from "@/components/ui/button";
import { Plus } from "lucide-react";
import { useCreateProduct, useUpdateProduct, useDeleteProduct } from "@/lib/hooks/use-product-mutations";
import { useDebounce } from "@/lib/hooks/use-debounce";
export default function ProductsPage() {
// reading states
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [searchInput, setSearchInput] = useState("");
const search = useDebounce(searchInput, 400);
const [category, setCategory] = useState<string | undefined>();
const [status, setStatus] = useState<"active" | "inactive" | undefined>();
const [sortBy, setSortBy] = useState<"createdAt" | "price" | "name">("createdAt");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
// dialog states
const [dialogOpen, setDialogOpen] = useState(false);
const [editingProduct, setEditingProduct] = useState<Product | null>(null);
const [deletingProduct, setDeletingProduct] = useState<Product | null>(null);
// mutations
const createProduct = useCreateProduct();
const updateProduct = useUpdateProduct();
const deleteProduct = useDeleteProduct();
// filter/search বদল → ১ম পেজ
useEffect(() => {
setPage(1);
}, [search, category, status, sortBy, sortOrder]);
const params: ProductQueryParams = {
page,
pageSize,
search: search || undefined,
category: category || undefined,
status: status || undefined,
sortBy,
sortOrder,
};
return (
<main className="container space-y-4 py-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-semibold">Products</h1>
<Button onClick={() => {
setEditingProduct(null);
setDialogOpen(true);
}}>
<Plus className="size-4" /> নতুন
</Button>
</div>
<ProductFilters
searchInput={searchInput}
onSearchChange={setSearchInput}
category={category}
onCategoryChange={setCategory}
status={status}
onStatusChange={setStatus}
/>
<ProductList
params={params}
onEdit={(p) => {
setEditingProduct(p);
setDialogOpen(true);
}}
onDelete={setDeletingProduct}
/>
<ProductPagination
page={page}
pageSize={pageSize}
onPageChange={setPage}
onPageSizeChange={(size) => {
setPageSize(size);
setPage(1);
}}
/>
<CreateEditDialog
open={dialogOpen}
onOpenChange={setDialogOpen}
editingProduct={editingProduct}
createPending={createProduct.isPending}
updatePending={updateProduct.isPending}
onCreate={(v) => createProduct.mutate(v, {
onSuccess: () => setDialogOpen(false),
})}
onUpdate={(v) => updateProduct.mutate(
{ id: editingProduct!.id, input: v },
{ onSuccess: () => setDialogOpen(false) },
)}
/>
<DeleteDialog
product={deletingProduct}
open={deletingProduct !== null}
onOpenChange={(open) => !open && !deleteProduct.isPending && setDeletingProduct(null)}
isDeleting={deleteProduct.isPending}
onConfirm={() => deleteProduct.mutate(deletingProduct!.id, {
onSuccess: () => setDeletingProduct(null),
})}
/>
</main>
);
}ডেটা flow সংক্ষেপ
- User search/filter বদলায় →
search/category/statusstate paramsobject-এর ভেতরে →useProducts-এর queryKey- TanStack Query ডেটা fetch →
ProductListrender - Create/Edit/Delete → mutation → invalidate → table refresh
আরেক্ধা দৈর্ঘ্য
এই পেজে state-গুলো ছড়িয়েছি demonstration-এর জন্য। scale দরকার হলে useReducer বা URL state (searchParams) — পরে production-architecture-এ আলোচনা করব।
শেষ প্যারায় নিজে practice করুন — কোড কপি করে নিজের এডিটরে চালিয়ে দেখুন।