Next.js & Node.js (Phần 5): Xây Dựng Trang Quản Trị (Admin) Và Xử Lý Upload Ảnh Với Multer
Chào các bạn, một hệ thống thương mại điện tử hoàn chỉnh không thể thiếu trang Admin. Đây là nơi chúng ta thực hiện các thao tác CRUD (Create, Read, Update, Delete). Trong bài viết này, mình sẽ hướng dẫn các bạn cách thiết lập giao diện Admin và chức năng Thêm sản phẩm kèm tải ảnh lên Server.
Bài 1: Thiết Kế Layout Admin Chuyên Nghiệp
Trang Admin thường có cấu trúc Sidebar (Thanh bên) cố định và nội dung thay đổi bên phải. Với Next.js, chúng ta tận dụng file layout.jsx để tạo khung sườn này cho toàn bộ các trang quản trị.
Chúng ta sử dụng Bootstrap Icons để làm cho menu sinh động hơn:
Quản lý thống kê: bi-pie-chart-fill
Quản lý sản phẩm: bi-box-seam
Quản lý đơn hàng: bi-cart-fill
Bài 2: Hiển Thị Danh Sách Sản Phẩm (Management Table)
Tại trang quản trị sản phẩm, thay vì dùng Card như trang chủ, chúng ta sử dụng Bảng (Table) để dễ dàng quan sát thông tin chi tiết và các nút thao tác nhanh.
Kỹ thuật lưu ý: Sử dụng { cache: 'no-store' } trong hàm fetch. Điều này cực kỳ quan trọng đối với trang Admin để đảm bảo dữ liệu luôn mới nhất mỗi khi chúng ta thêm hoặc xóa sản phẩm.
const res = await fetch("http://localhost:3000/products", { cache: 'no-store' });
Bài 3: Chức Năng Thêm Sản Phẩm & Upload Ảnh
Đây là phần "khó nhằn" nhất nhưng cũng thú vị nhất. Chúng ta cần xử lý dữ liệu nhị phân (hình ảnh).
1. Phía Backend: Cấu hình Multer
Chúng ta sử dụng thư viện multer để định nghĩa nơi lưu trữ ảnh (/public/img) và kiểm tra định dạng file (chỉ cho phép .jpg, .png...).
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, './public/img'),
filename: (req, file, cb) => cb(null, file.originalname)
});
const upload = multer({ storage: storage });
router.post('/addproduct', upload.single('image'), async (req, res) => {
// Logic lưu vào MongoDB...
});
2. Phía Frontend: Sử dụng FormData
Khi gửi form có chứa file, chúng ta không thể gửi JSON thông thường mà phải sử dụng đối tượng FormData.
const data = new FormData();
data.append('name', name.current.value);
data.append('image', image.current.files[0]); // Lấy file từ input
Chúng ta cũng sử dụng useRef để lấy giá trị từ các ô nhập liệu mà không cần render lại component quá nhiều lần, giúp tối ưu hiệu năng.
Bài 4: Nâng Cấp Với Formik & Yup (Dành cho điểm 10)
Để trang Admin trở nên chuyên nghiệp, việc Validate dữ liệu là bắt buộc. Bạn không thể để người dùng thêm một sản phẩm không có tên hoặc giá bằng 0.
Formik: Giúp quản lý vòng đời của Form (khởi tạo, thay đổi, gửi đi).
Yup: Giúp định nghĩa các quy tắc kiểm tra (Validation Schema) một cách ngắn gọn.
Ví dụ một Schema cơ bản:
const ProductSchema = Yup.object().shape({
name: Yup.string().required('Tên sản phẩm là bắt buộc'),
price: Yup.number().positive('Giá phải là số dương').required('Vui lòng nhập giá'),
});
Tổng kết Lab 5
Việc xây dựng trang Admin đòi hỏi sự chính xác cao trong việc kết nối API và xử lý dữ liệu Form. Qua bài này, bạn đã nắm được:
Cách xây dựng Layout Dashboard.
Cách fetch dữ liệu không dùng cache để cập nhật tức thì.
Cách upload file từ Client lên Server thông qua Multer.
Hẹn gặp lại các bạn ở Lab 6, nơi chúng ta sẽ hoàn thiện các chức năng Sửa và Xóa sản phẩm!
All rights reserved