Nextjs show list sản phẩm và show modal khi ấn vào 1 sản phẩm để hiển thị
Đầu tiên chúng ta tạo List product có cản search :
'use client';
import Pagination from '@/components/list/Pagination';
import ListProductItem from '@/components/list/productitem';
import { ProductType } from '@/type/ItemType';
import React, { useEffect, useState } from 'react';
import { Button, Form, InputGroup } from 'react-bootstrap';
const ProductListPage = () => {
const [products, setProducts] = useState<ProductType[]>([]);
const [total, setTotal] = useState(0);
const [skip, setSkip] = useState(0);
const [localSearch, setLocalSearch] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const limit = 10;
useEffect(() => {
const fetchProducts = async () => {
let url = '';
if (searchTerm.trim()) {
// Gọi API tìm kiếm
url = `https://dummyjson.com/products/search?q=${encodeURIComponent(searchTerm)}`;
} else {
// Gọi API phân trang mặc định
url = `https://dummyjson.com/products?limit=${limit}&skip=${skip}`;
}
const res = await fetch(url);
const data = await res.json();
setProducts(data.products);
setTotal(data.total);
};
fetchProducts();
}, [skip, searchTerm]);
//const totalPages = Math.ceil(total / limit);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setSkip(0); // reset về trang đầu khi tìm kiếm
setSearchTerm(localSearch);
};
return (
<div className="container mt-4">
<h2>Product List (Pagination + Search)</h2>
<Form onSubmit={handleSubmit} className="mb-3">
<InputGroup>
<Form.Control
type="text"
placeholder="Search..."
value={localSearch}
onChange={(e) => setLocalSearch(e.target.value)}
/>
<Button type="submit" variant="primary">
Search
</Button>
</InputGroup>
</Form>
<ul className="list-group mb-3">
{products.map(product => (
<li key={product.id} className="list-group-item d-flex justify-content-between">
<ListProductItem data={product} />
</li>
))}
</ul>
{!searchTerm && (
<Pagination
skip={skip}
limit={limit}
total={total}
onPageChange={setSkip}
/>
)}
</div>
);
};
export default ProductListPage;
Ở đây chúng ta có ListproductItem :
'use client';
import { ProductType } from '@/type/ItemType';
import React, { useState } from 'react';
import { Modal, Button, Spinner } from 'react-bootstrap'; // Nếu dùng Bootstrap
import { ProductDetailType } from './item';
const ListProductItem = ({ data }: { data: ProductType }) => {
const [showModal, setShowModal] = useState<boolean>(false);
const [productDetail, setProductDetail] = useState<ProductDetailType | null>(null);
const [loading, setLoading] = useState(false);
const handleOpen = async () => {
setShowModal(true);
setLoading(true)
try {
const res = await fetch(`https://dummyjson.com/products/${data.id}`);
const detail = await res.json();
setProductDetail(detail)
} catch (error) {
console.error('Failed to fetch product details:', error);
} finally {
setLoading(false);
}
};
const handleClose = () => {
setShowModal(false)
setProductDetail(null);
};
return (
<>
<div
className="d-flex justify-content-between align-items-center p-2 border-bottom cursor-pointer"
onClick={handleOpen}
style={{ cursor: 'pointer' }}
>
<div className="title">{data.title}</div>
<div className="price">{data.price}$</div>
</div>
<Modal show={showModal} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>{data.title}</Modal.Title>
</Modal.Header>
<Modal.Body>
{loading ? (
<div className="text-center">
<Spinner animation="border" />
</div>
) : productDetail ? (
<>
<p><strong>Price:</strong> {productDetail.price}$</p>
<p><strong>Description:</strong> {productDetail.description}</p>
{productDetail.images && productDetail.images.length > 0 && (
<img
src={productDetail.images[0]}
alt={productDetail.title}
style={{ width: '100%', maxHeight: '200px', objectFit: 'cover' }}
/>
)}
</>
) : (
<p>Error loading product details.</p>
)}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
};
export default ListProductItem
Trong component Paginate như sau :
'use client';
import React from 'react';
import { Button } from 'react-bootstrap';
type PaginationProps = {
skip: number;
limit: number;
total: number;
onPageChange: (newSkip: number) => void;
};
const Pagination = ({ skip, limit, total, onPageChange }: PaginationProps) => {
const totalPages = Math.ceil(total / limit);
const currentPage = skip / limit + 1;
const handlePrev = () => {
onPageChange(Math.max(0, skip - limit));
};
const handleNext = () => {
onPageChange(skip + limit);
};
return (
<div className="d-flex justify-content-between align-items-center py-3">
<Button disabled={skip === 0} onClick={handlePrev}>
Previous
</Button>
<div>
Page {currentPage} of {totalPages}
</div>
<Button disabled={skip + limit >= total} onClick={handleNext}>
Next
</Button>
</div>
);
};
export default Pagination;
truy cập sau khi chạy : http://localhost:3000/product
Bạn có thể download code về : https://drive.google.com/file/d/1DeWk-3zqHZTE5cCtEX5A_r92w1vV8_DT/view?usp=sharing