Admin: Product Management (CRUD)
Relevant source files
The following files were used as context for generating this wiki page:- app/admin/products/page.tsx
- app/admin/products/new/page.tsx
- app/admin/products/[id]/edit/page.tsx
- features/products/services/admin-products.service.ts
- features/products/components/admin/product-form.tsx
- features/products/hooks/use-admin-products.ts
- types/product.types.ts
- features/products/components/admin/product-list.tsx
- features/products/schemas/product-form.schema.ts
The Admin Product Management module provides a comprehensive interface for administrators to perform Create, Read, Update, and Delete (CRUD) operations on the store's products. This feature is a core part of the admin panel, enabling full control over the product catalog, including details, pricing, inventory, and images.
The system is built using Next.js App Router for page structure, @tanstack/react-query for server state management, and a dedicated service layer for API communication. It supports file uploads for product images and AR try-on assets. The main entry point for this feature is the /admin/products page, accessible from the main Admin Dashboard.
Architecture Overview
The product management feature follows a layered architecture that separates UI components, state management hooks, and the API service layer. This promotes a clean separation of concerns and enhances maintainability.
The diagram below illustrates the high-level interaction between these layers when an admin performs an action, such as creating a new product.
Core Components
The UI is composed of several key React components, each with a specific responsibility.
| Component | File Path | Description |
|---|---|---|
AdminProductsPage | app/admin/products/page.tsx | The main page for listing, filtering, and managing products. It orchestrates the various child components. |
NewProductPage | app/admin/products/new/page.tsx | Page for creating a new product. It primarily renders the ProductForm in "create" mode. |
EditProductPage | app/admin/products/[id]/edit/page.tsx | Page for editing an existing product. It fetches product data and renders the ProductForm in "edit" mode. |
ProductForm | features/products/components/admin/product-form.tsx | A reusable form for both creating and editing products. It uses react-hook-form and zodResolver for validation. |
ProductsListCard | features/products/components/admin/products-list-card.tsx | A wrapper component that displays the ProductList table and handles interactions like deletion. |
ProductList | features/products/components/admin/product-list.tsx | Renders the data table of products with columns for image, name, price, stock, and actions (edit/delete). |
AdminProductFilters | features/products/components/admin/product-filters.tsx | Provides UI for filtering the product list by search term and category. |
DeleteProductDialog | features/products/components/admin/delete-product-dialog.tsx | A confirmation dialog shown before a product is deleted. |
Sources: app/admin/products/page.tsx, app/admin/products/new/page.tsx, app/admin/products/[id]/edit/page.tsx, features/products/components/admin/product-form.tsx, features/products/components/admin/product-list.tsx
Data Flow and State Management
Server state, including fetching, caching, and mutating product data, is managed by @tanstack/react-query. A set of custom hooks abstracts the React Query logic, providing a simple API for components.
Sources: features/products/hooks/use-admin-products.ts
Custom Hooks
useAdminProducts(params): Fetches a paginated and filtered list of products. The query key includes the filterparamsto ensure data is re-fetched when filters change.useAdminProduct(productId): Fetches a single product by its ID.useCreateProduct(): Returns a mutation function to create a new product. On success, it invalidates the product list query to refresh the data and displays a success toast.useUpdateProduct(): Returns a mutation function to update a product. On success, it invalidates both the product list and the specific product detail query.useDeleteProduct(): Returns a mutation function to delete a product. On success, it invalidates the product list query.
All mutation hooks include integrated success and error handling using sonner for toast notifications.
Sources: features/products/hooks/use-admin-products.ts:18-118
Sequence Diagram: Creating a Product
This diagram shows the sequence of events when an admin creates a new product.
API Service Layer
The AdminProductsService class is responsible for all communication with the backend product endpoints. It abstracts away the details of axios and data transformation.
Sources: features/products/services/admin-products.service.ts
Class Diagram
This diagram shows the structure of the service, which implements the IAdminProductsService interface.
File Uploads
A key responsibility of this service is handling file uploads for product images. The create and update methods use private helper functions (buildCreateFormData, buildUpdateFormData) to construct a FormData object. This allows sending product data along with binary file data (image, tryOnImage) in a single multipart/form-data request.
Sources: features/products/services/admin-products.service.ts:159-224
API Endpoints
The service interacts with the following admin-specific API endpoints:
| Method | Endpoint | Description | Service Method |
|---|---|---|---|
GET | /products | Lists all products with pagination and filtering. | list() |
GET | /products/{id} | Retrieves a single product by its ID. | getById() |
POST | /products/admin | Creates a new product with image uploads. | create() |
PUT | /products/admin/{id} | Updates an existing product. | update() |
DELETE | /products/admin/{id} | Soft-deletes a product. | delete() |
Sources: features/products/services/admin-products.service.ts
Data Models and Validation
Type safety and data validation are enforced through TypeScript types, Zod schemas, and a clear separation of request/response models.
Data Models
The core data structures for admin operations are defined in types/product.types.ts.
Admin Request Payloads
For create and update operations, special request types are used to accommodate file uploads.
| Field | Type | Description |
|---|---|---|
name | string | The product name. Required for creation. |
priceInt | number | The price in VND. Required for creation. |
description | string (optional) | Product description. |
categoryId | string (optional) | The ID of the associated category. |
image | File (optional) | The main product image file for upload. |
tryOnImage | File (optional) | A PNG file used for the AR virtual try-on feature. |
sku | string (optional) | Stock Keeping Unit. |
stock | number (optional) | Available quantity in stock. |
model3dUrl | string (optional) | URL to a 3D model of the product. |
Sources: types/product.types.ts:200-221
Admin List Query Parameters
When fetching the product list, the following query parameters can be used.
| Parameter | Type | Description |
|---|---|---|
page | number | The page number for pagination. |
pageSize | number | The number of items per page. Note: the backend expects pageSize, not limit. |
search | string | A search term to filter products by name or SKU. |
categorySlug | string | The slug of a category to filter products. |
Sources: types/product.types.ts:227-233, features/products/services/admin-products.service.ts:81-96
Form Validation
Client-side validation for the ProductForm is handled by Zod. The productFormSchema defines rules for each field, such as minimum length for the name, number type for the price, and file constraints for images. This schema is passed to react-hook-form's zodResolver to automatically validate form inputs and display error messages.
Sources: features/products/schemas/product-form.schema.ts, features/products/components/admin/product-form.tsx:32-47