diff --git a/src/Router/all_routes.jsx b/src/Router/all_routes.jsx
index cf3160d..fb70916 100644
--- a/src/Router/all_routes.jsx
+++ b/src/Router/all_routes.jsx
@@ -1,6 +1,8 @@
export const all_routes = {
dashboard: "/",
productlist: "/product-list",
+ productlist2: "/product-list-2",
+ productlist3: "/product-list-3",
addproduct: "/add-product",
salesdashboard: "/sales-dashboard",
brandlist: "/brand-list",
diff --git a/src/Router/router.link.jsx b/src/Router/router.link.jsx
index 3bb1758..b299828 100644
--- a/src/Router/router.link.jsx
+++ b/src/Router/router.link.jsx
@@ -199,6 +199,8 @@ import ProjectTracker from "../feature-module/projects/projecttracker";
import CreateProject from "../feature-module/projects/createproject";
import EnhancedLoaders from "../feature-module/uiinterface/enhanced-loaders";
import WeddingGuestList from "../feature-module/inventory/weddingGuestList";
+import ProductList2 from "../feature-module/inventory/productlist2";
+import ProductList3 from "../feature-module/inventory/productlist3";
import { all_routes } from "./all_routes";
export const publicRoutes = [
{
@@ -215,6 +217,20 @@ export const publicRoutes = [
element: ,
route: Route,
},
+ {
+ id: 2.1,
+ path: routes.productlist2,
+ name: "products2",
+ element: ,
+ route: Route,
+ },
+ {
+ id: 2.2,
+ path: routes.productlist3,
+ name: "products3",
+ element: ,
+ route: Route,
+ },
{
id: 3,
path: routes.addproduct,
diff --git a/src/core/json/siderbar_data.jsx b/src/core/json/siderbar_data.jsx
index ade97db..e59ed4d 100644
--- a/src/core/json/siderbar_data.jsx
+++ b/src/core/json/siderbar_data.jsx
@@ -62,9 +62,11 @@ export const SidebarData = [
submenuItems: [
{ label: "Tiến độ dự án", link: "/project-tracker",icon: ,showSubRoute: false},
{ label: "Sản phẩm", link: "/product-list", icon:,showSubRoute: false,submenu: false },
+ { label: "Nhập kho", link: "/product-list-2", icon:,showSubRoute: false,submenu: false },
+ { label: "Tồn kho", link: "/product-list-3", icon:,showSubRoute: false,submenu: false },
{ label: "Create Product", link: "/add-product", icon: ,showSubRoute: false, submenu: false },
- { label: "Expired Products", link: "/expired-products", icon: ,showSubRoute: false,submenu: false },
- { label: "Low Stocks", link: "/low-stocks", icon: ,showSubRoute: false,submenu: false },
+ { label: "Sản phẩm hết hạn", link: "/expired-products", icon: ,showSubRoute: false,submenu: false },
+ { label: "Hàng tồn kho", link: "/low-stocks", icon: ,showSubRoute: false,submenu: false },
{ label: "Danh mục", link: "/category-list", icon: ,showSubRoute: false,submenu: false },
{ label: "Sub Category", link: "/sub-categories", icon: ,showSubRoute: false,submenu: false },
{ label: "Thương hiệu", link: "/brand-list", icon: ,showSubRoute: false,submenu: false },
diff --git a/src/feature-module/inventory/expiredproduct.jsx b/src/feature-module/inventory/expiredproduct.jsx
index 9adb2e0..d9cb0c6 100644
--- a/src/feature-module/inventory/expiredproduct.jsx
+++ b/src/feature-module/inventory/expiredproduct.jsx
@@ -28,20 +28,20 @@ const ExpiredProduct = () => {
};
const oldandlatestvalue = [
- { value: 'date', label: 'Sort by Date' },
- { value: 'newest', label: 'Newest' },
- { value: 'oldest', label: 'Oldest' },
+ { value: 'date', label: 'Sắp xếp theo ngày' },
+ { value: 'newest', label: 'Mới nhất' },
+ { value: 'oldest', label: 'Cũ nhất' },
];
const brands = [
- { value: 'chooseType', label: 'Choose Type' },
- { value: 'lenovo3rdGen', label: 'Lenovo 3rd Generation' },
+ { value: 'chooseType', label: 'Chọn loại' },
+ { value: 'lenovo3rdGen', label: 'Lenovo thế hệ 3' },
{ value: 'nikeJordan', label: 'Nike Jordan' },
];
const renderTooltip = (props) => (
- Pdf
+ PDF
);
const renderExcelTooltip = (props) => (
@@ -51,23 +51,23 @@ const ExpiredProduct = () => {
);
const renderPrinterTooltip = (props) => (
- Printer
+ In ấn
);
const renderRefreshTooltip = (props) => (
- Refresh
+ Làm mới
);
const renderCollapseTooltip = (props) => (
- Collapse
+ Thu gọn
);
const columns = [
{
- title: "Product",
+ title: "Sản phẩm",
dataIndex: "product",
render: (text, record) => (
@@ -81,23 +81,23 @@ const ExpiredProduct = () => {
width: "5%"
},
{
- title: "SKU",
+ title: "Mã IMEI",
dataIndex: "sku",
sorter: (a, b) => a.sku.length - b.sku.length,
},
{
- title: "Manufactured Date",
+ title: "Ngày sản xuất",
dataIndex: "manufactureddate",
sorter: (a, b) => a.manufactureddate.length - b.manufactureddate.length,
},
{
- title: "Expired Date",
+ title: "Ngày hết hạn",
dataIndex: "expireddate",
sorter: (a, b) => a.expireddate.length - b.expireddate.length,
},
-
+
{
- title: 'Actions',
+ title: 'Thao tác',
dataIndex: 'actions',
key: 'actions',
render: () => (
@@ -118,19 +118,19 @@ const ExpiredProduct = () => {
const showConfirmationAlert = () => {
MySwal.fire({
- title: 'Are you sure?',
- text: 'You won\'t be able to revert this!',
+ title: 'Bạn có chắc chắn?',
+ text: 'Bạn sẽ không thể hoàn tác hành động này!',
showCancelButton: true,
confirmButtonColor: '#00ff00',
- confirmButtonText: 'Yes, delete it!',
+ confirmButtonText: 'Có, xóa nó!',
cancelButtonColor: '#ff0000',
- cancelButtonText: 'Cancel',
+ cancelButtonText: 'Hủy',
}).then((result) => {
if (result.isConfirmed) {
MySwal.fire({
- title: 'Deleted!',
- text: 'Your file has been deleted.',
+ title: 'Đã xóa!',
+ text: 'Tệp của bạn đã được xóa.',
className: "btn btn-success",
confirmButtonText: 'OK',
customClass: {
@@ -150,8 +150,8 @@ const ExpiredProduct = () => {
-
Expired Products
- Manage your expired products
+ Sản phẩm hết hạn
+ Quản lý các sản phẩm hết hạn của bạn
@@ -251,7 +251,7 @@ const ExpiredProduct = () => {
-
+
@@ -264,7 +264,7 @@ const ExpiredProduct = () => {
type="date"
className="filterdatepicker"
dateFormat="dd-MM-yyyy"
- placeholder='Choose Date'
+ placeholder='Chọn ngày'
/>
@@ -274,7 +274,7 @@ const ExpiredProduct = () => {
{" "}
{" "}
- Search{" "}
+ Tìm kiếm{" "}
diff --git a/src/feature-module/inventory/productlist2.jsx b/src/feature-module/inventory/productlist2.jsx
new file mode 100644
index 0000000..a22aab0
--- /dev/null
+++ b/src/feature-module/inventory/productlist2.jsx
@@ -0,0 +1,784 @@
+import {
+ Box,
+ ChevronUp,
+ Edit,
+ Eye,
+ Filter,
+ GitMerge,
+ PlusCircle,
+ RotateCcw,
+ Sliders,
+ StopCircle,
+ Trash2,
+} from "feather-icons-react/build/IconComponents";
+import React, { useState, useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { Link } from "react-router-dom";
+import Select from "react-select";
+import ImageWithBasePath from "../../core/img/imagewithbasebath";
+import Brand from "../../core/modals/inventory/brand";
+import withReactContent from "sweetalert2-react-content";
+import Swal from "sweetalert2";
+import { all_routes } from "../../Router/all_routes";
+import { OverlayTrigger, Tooltip } from "react-bootstrap";
+import Table from "../../core/pagination/datatable";
+import {
+ fetchProducts,
+ fetchProduct,
+ deleteProduct,
+ searchProducts
+} from "../../core/redux/actions/productActions";
+import { setToogleHeader } from "../../core/redux/action";
+import { Download } from "react-feather";
+import CustomPagination from "../../components/CustomPagination";
+
+const MySwal = withReactContent(Swal);
+const route = all_routes;
+
+// Add CSS to hide Ant Design pagination and checkboxes
+function hideAntElements() {
+ const styleSheet = document.createElement("style");
+ styleSheet.type = "text/css";
+ styleSheet.innerText = `
+ .ant-pagination {
+ display: none !important;
+ visibility: hidden !important;
+ width: 0 !important;
+ height: 0 !important;
+ overflow: hidden !important;
+ pointer-events: none !important;
+ }
+
+ /* Hide all Ant Design checkboxes */
+ .ant-checkbox,
+ .ant-checkbox-input,
+ .ant-checkbox-wrapper,
+ .ant-table-selection-column,
+ .ant-table-selection,
+ .ant-table-thead > tr > th.ant-table-selection-column,
+ .ant-table-tbody > tr > td.ant-table-selection-column {
+ display: none !important;
+ visibility: hidden !important;
+ width: 0 !important;
+ height: 0 !important;
+ overflow: hidden !important;
+ pointer-events: none !important;
+ }
+ `;
+ document.head.appendChild(styleSheet);
+}
+
+const ProductList2 = () => {
+ // Use new Redux structure for API data, fallback to legacy for existing functionality
+ const {
+ products: apiProducts,
+ loading,
+ error,
+ totalProducts,
+ pageSize: reduxPageSize,
+ currentPage: reduxCurrentPage
+ } = useSelector((state) => state.products);
+
+ // Fallback to legacy data if API data is not available
+ const legacyProducts = useSelector((state) => state.legacy?.product_list || []);
+
+ // Sample import data for demonstration
+ const sampleImportData = [
+ {
+ key: 1,
+ product: "MacBook Pro 13 inch",
+ sku: "MBP13-001",
+ importPrice: 25000000,
+ importQuantity: 5,
+ productImage: "assets/img/products/macbook.jpg",
+ supplier: "Apple Vietnam",
+ createdDate: "2024-01-15",
+ importDate: "2024-01-15"
+ },
+ {
+ key: 2,
+ product: "iPhone 15 Pro",
+ sku: "IP15P-001",
+ importPrice: 28000000,
+ importQuantity: 10,
+ productImage: "assets/img/products/iphone.jpg",
+ supplier: "Apple Vietnam",
+ createdDate: "2024-01-14",
+ importDate: "2024-01-14"
+ },
+ {
+ key: 3,
+ product: "Samsung Galaxy S24",
+ sku: "SGS24-001",
+ importPrice: 22000000,
+ importQuantity: 8,
+ productImage: "assets/img/products/samsung.jpg",
+ supplier: "Samsung Vietnam",
+ createdDate: "2024-01-13",
+ importDate: "2024-01-13"
+ }
+ ];
+
+ const dataSource = apiProducts.length > 0 ? apiProducts : (legacyProducts.length > 0 ? legacyProducts : sampleImportData);
+
+ const dispatch = useDispatch();
+ const data = useSelector((state) => state.legacy?.toggle_header || false);
+
+ const [isFilterVisible, setIsFilterVisible] = useState(false);
+ const [searchTerm, setSearchTerm] = useState("");
+
+ // State for pagination - sync with Redux
+ const [currentPage, setCurrentPage] = useState(reduxCurrentPage || 1);
+ const [pageSize, setPageSize] = useState(reduxPageSize || 20);
+
+ // State for filter values
+ const [filterValues, setFilterValues] = useState({
+ product: '',
+ supplier: '',
+ importDate: '',
+ priceRange: '',
+ quantityRange: ''
+ });
+
+ // Calculate total records and pages
+ const totalRecords = totalProducts || dataSource.length;
+ const actualTotalPages = Math.ceil(totalRecords / pageSize);
+
+ useEffect(() => {
+ hideAntElements();
+
+ // Fetch products on component mount
+ if (apiProducts.length === 0) {
+ dispatch(fetchProducts({ page: currentPage, pageSize }));
+ }
+ }, [dispatch, apiProducts.length]);
+
+ // Sync local state with Redux state
+ useEffect(() => {
+ if (reduxCurrentPage && reduxCurrentPage !== currentPage) {
+ setCurrentPage(reduxCurrentPage);
+ }
+ if (reduxPageSize && reduxPageSize !== pageSize) {
+ setPageSize(reduxPageSize);
+ }
+ }, [reduxCurrentPage, reduxPageSize, currentPage, pageSize]);
+
+ const handleSearch = (e) => {
+ const value = e.target.value;
+ setSearchTerm(value);
+
+ if (value.trim()) {
+ dispatch(searchProducts(value));
+ } else {
+ dispatch(fetchProducts({ page: 1, pageSize }));
+ setCurrentPage(1);
+ }
+ };
+
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ dispatch(fetchProducts({ page, pageSize, search: searchTerm }));
+ };
+
+ const handlePageSizeChange = (newPageSize) => {
+ setPageSize(newPageSize);
+ setCurrentPage(1);
+ dispatch(fetchProducts({ page: 1, pageSize: newPageSize, search: searchTerm }));
+ };
+
+
+
+ const handleDelete = async (productId) => {
+ try {
+ const result = await MySwal.fire({
+ title: "Are you sure?",
+ text: "You won't be able to revert this!",
+ icon: "warning",
+ showCancelButton: true,
+ confirmButtonColor: "#dc3545",
+ cancelButtonColor: "#6c757d",
+ confirmButtonText: "Yes, delete it!",
+ cancelButtonText: "Cancel",
+ });
+
+ if (result.isConfirmed) {
+ await dispatch(deleteProduct(productId));
+
+ MySwal.fire({
+ title: "Deleted!",
+ text: "Product has been deleted successfully.",
+ icon: "success",
+ confirmButtonColor: "#28a745",
+ });
+
+ // Refresh the product list
+ dispatch(fetchProducts({ page: currentPage, pageSize, search: searchTerm }));
+ }
+ } catch (error) {
+ MySwal.fire({
+ title: "Error!",
+ text: "Failed to delete product. Please try again.",
+ icon: "error",
+ confirmButtonColor: "#dc3545",
+ });
+ }
+ };
+
+ const oldandlatestvalue = [
+ { value: "date", label: "Sắp xếp theo ngày" },
+ { value: "newest", label: "Mới nhất" },
+ { value: "oldest", label: "Cũ nhất" },
+ ];
+
+ const supplierOptions = [
+ { value: "", label: "Chọn nhà cung cấp" },
+ { value: "supplier1", label: "Nhà cung cấp A" },
+ { value: "supplier2", label: "Nhà cung cấp B" },
+ ];
+
+ const importDateOptions = [
+ { value: "", label: "Chọn thời gian nhập" },
+ { value: "today", label: "Hôm nay" },
+ { value: "week", label: "Tuần này" },
+ { value: "month", label: "Tháng này" },
+ ];
+
+ const priceRangeOptions = [
+ { value: "", label: "Chọn khoảng giá" },
+ { value: "0-100000", label: "0 - 100,000 ₫" },
+ { value: "100000-500000", label: "100,000 - 500,000 ₫" },
+ { value: "500000-1000000", label: "500,000 - 1,000,000 ₫" },
+ ];
+
+ const quantityRangeOptions = [
+ { value: "", label: "Chọn số lượng" },
+ { value: "1-10", label: "1 - 10" },
+ { value: "11-50", label: "11 - 50" },
+ { value: "51-100", label: "51 - 100" },
+ { value: "100+", label: "Trên 100" },
+ ];
+
+ const handleMenuToggle = () => {
+ dispatch(setToogleHeader(!data));
+ };
+
+ const columns = [
+ {
+ title: "Sản phẩm",
+ dataIndex: "product",
+ render: (text, record) => (
+ {text || record.name || record.productName}
+ ),
+ sorter: (a, b) => a.product.length - b.product.length,
+ },
+ {
+ title: "Mã",
+ dataIndex: "sku",
+ render: (_, record) => {
+ const sku = record.sku || record.code || record.productCode || '-';
+ return {sku};
+ },
+ sorter: (a, b) => {
+ const skuA = a.sku || a.code || a.productCode || '';
+ const skuB = b.sku || b.code || b.productCode || '';
+ return skuA.length - skuB.length;
+ },
+ },
+ {
+ title: "Giá nhập",
+ dataIndex: "importPrice",
+ render: (_, record) => {
+ const price = record.price || record.price || record.price || 0;
+ return (
+ {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ >
+ {price.toLocaleString('vi-VN')} ₫
+
+ );
+ },
+ sorter: (a, b) => {
+ const priceA = a.importPrice || a.purchasePrice || a.costPrice || 0;
+ const priceB = b.importPrice || b.purchasePrice || b.costPrice || 0;
+ return priceA - priceB;
+ },
+ },
+ {
+ title: "Số lượng nhập",
+ dataIndex: "importQuantity",
+ render: (_, record) => {
+ const quantity = record.qty || record.quantity || record.inboundQuantity || 0;
+ return (
+ 0 ? '#e8f5e8' : '#f8f9fa',
+ color: quantity > 0 ? '#28a745' : '#6c757d',
+ borderRadius: '20px',
+ fontSize: '13px',
+ fontWeight: '600',
+ border: `1px solid ${quantity > 0 ? '#c3e6cb' : '#dee2e6'}`,
+ minWidth: '80px',
+ textAlign: 'center',
+ boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
+ transition: 'all 0.2s ease'
+ }}
+ onMouseEnter={(e) => {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ >
+ {quantity.toLocaleString('vi-VN')}
+
+ );
+ },
+ sorter: (a, b) => {
+ const qtyA = a.importQuantity || a.receivedQuantity || a.inboundQuantity || 0;
+ const qtyB = b.importQuantity || b.receivedQuantity || b.inboundQuantity || 0;
+ return qtyA - qtyB;
+ },
+ },
+ {
+ title: "Ngày nhập",
+ dataIndex: "importDate",
+ render: (_, record) => {
+ const importDate = record.createdDate || record.importDate || record.receivedDate || '';
+ if (!importDate) return -;
+
+ // Format date to Vietnamese format
+ const date = new Date(importDate);
+ const formattedDate = date.toLocaleDateString('vi-VN', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric'
+ });
+
+ // Calculate days ago
+ const today = new Date();
+ const diffTime = Math.abs(today - date);
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+
+ let badgeColor = '#d1ecf1';
+ let textColor = '#0c5460';
+ let borderColor = '#bee5eb';
+
+ if (diffDays <= 1) {
+ badgeColor = '#d4edda';
+ textColor = '#155724';
+ borderColor = '#c3e6cb';
+ } else if (diffDays <= 7) {
+ badgeColor = '#fff3cd';
+ textColor = '#856404';
+ borderColor = '#ffeaa7';
+ } else if (diffDays > 30) {
+ badgeColor = '#f8d7da';
+ textColor = '#721c24';
+ borderColor = '#f5c6cb';
+ }
+
+ return (
+ {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ title={`${diffDays} ngày trước`}
+ >
+ {formattedDate}
+
+ );
+ },
+ sorter: (a, b) => {
+ const dateA = new Date(a.createdDate || a.importDate || a.receivedDate || 0);
+ const dateB = new Date(b.createdDate || b.importDate || b.receivedDate || 0);
+ return dateA - dateB;
+ },
+ },
+ {
+ title: "Thao tác",
+ dataIndex: "action",
+ render: (_, record) => (
+
+
+
+
+
+ {
+ // Pre-fetch product details for editing
+ if (record.id || record.key) {
+ dispatch(fetchProduct(record.id || record.key));
+ }
+ }}
+ >
+
+
+ {
+ e.preventDefault();
+ handleDelete(record.id || record.key);
+ }}
+ >
+
+
+
+ |
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
Nhập kho
+ Quản lý thông tin nhập kho sản phẩm
+
+
+
+ -
+ Pdf}
+ >
+
+
+
+
+
+ -
+ Excel}
+ >
+
+
+
+
+
+ -
+ Print}
+ >
+
+
+
+
+
+ -
+ Refresh}
+ >
+ dispatch(fetchProducts({ page: currentPage, pageSize }))}
+ >
+
+
+
+
+ -
+ Collapse}
+ >
+
+
+
+
+
+
+
+
+
+
+ Import Excel
+
+
+
+ {/* /product list */}
+
+
+
+
+
+ setIsFilterVisible(!isFilterVisible)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ {/* /Filter */}
+
+ {/* /Filter */}
+
+ {loading ? (
+
+
+ Loading...
+
+
Đang tải dữ liệu nhập kho...
+
+ ) : error ? (
+
+ Error: {error}
+
+
+ ) : (
+ <>
+
+
+ {/* Reusable Custom Pagination Component */}
+
+ >
+ )}
+
+
+
+ {/* /product list */}
+
+
+
+ );
+};
+
+export default ProductList2;
diff --git a/src/feature-module/inventory/productlist3.jsx b/src/feature-module/inventory/productlist3.jsx
new file mode 100644
index 0000000..cb68f19
--- /dev/null
+++ b/src/feature-module/inventory/productlist3.jsx
@@ -0,0 +1,759 @@
+import {
+ Box,
+ Edit,
+ Eye,
+ Filter,
+ GitMerge,
+ Sliders,
+ StopCircle,
+ Trash2,
+} from "feather-icons-react/build/IconComponents";
+import React, { useState, useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { Link } from "react-router-dom";
+import Select from "react-select";
+import ImageWithBasePath from "../../core/img/imagewithbasebath";
+import Brand from "../../core/modals/inventory/brand";
+import withReactContent from "sweetalert2-react-content";
+import Swal from "sweetalert2";
+import { all_routes } from "../../Router/all_routes";
+import { OverlayTrigger, Tooltip } from "react-bootstrap";
+import Table from "../../core/pagination/datatable";
+import {
+ fetchProducts,
+ fetchProduct,
+ deleteProduct,
+ searchProducts
+} from "../../core/redux/actions/productActions";
+
+import CustomPagination from "../../components/CustomPagination";
+
+const MySwal = withReactContent(Swal);
+const route = all_routes;
+
+// Add CSS to hide Ant Design pagination and checkboxes
+function hideAntElements() {
+ const styleSheet = document.createElement("style");
+ styleSheet.type = "text/css";
+ styleSheet.innerText = `
+ .ant-pagination {
+ display: none !important;
+ visibility: hidden !important;
+ width: 0 !important;
+ height: 0 !important;
+ overflow: hidden !important;
+ pointer-events: none !important;
+ }
+
+ /* Hide all Ant Design checkboxes */
+ .ant-checkbox,
+ .ant-checkbox-input,
+ .ant-checkbox-wrapper,
+ .ant-table-selection-column,
+ .ant-table-selection,
+ .ant-table-thead > tr > th.ant-table-selection-column,
+ .ant-table-tbody > tr > td.ant-table-selection-column {
+ display: none !important;
+ visibility: hidden !important;
+ width: 0 !important;
+ height: 0 !important;
+ overflow: hidden !important;
+ pointer-events: none !important;
+ }
+ `;
+ document.head.appendChild(styleSheet);
+}
+
+const ProductList3 = () => {
+ // Use new Redux structure for API data, fallback to legacy for existing functionality
+ const {
+ products: apiProducts,
+ loading,
+ error,
+ totalProducts,
+ pageSize: reduxPageSize,
+ currentPage: reduxCurrentPage
+ } = useSelector((state) => state.products);
+
+ // Fallback to legacy data if API data is not available
+ const legacyProducts = useSelector((state) => state.legacy?.product_list || []);
+
+ // Sample inventory data for demonstration
+ const sampleInventoryData = [
+ {
+ key: 1,
+ product: "MacBook Pro 13 inch",
+ sku: "MBP13-001",
+ totalImported: 50,
+ totalExported: 35,
+ currentStock: 15,
+ minStock: 5,
+ maxStock: 50,
+ unitPrice: 25000000,
+ totalValue: 375000000,
+ productImage: "assets/img/products/macbook.jpg",
+ createdDate: "2024-01-15",
+ lastUpdated: "2024-01-20"
+ },
+ {
+ key: 2,
+ product: "iPhone 15 Pro",
+ sku: "IP15P-001",
+ totalImported: 80,
+ totalExported: 55,
+ currentStock: 25,
+ minStock: 10,
+ maxStock: 100,
+ unitPrice: 28000000,
+ totalValue: 700000000,
+ productImage: "assets/img/products/iphone.jpg",
+ createdDate: "2024-01-14",
+ lastUpdated: "2024-01-19"
+ },
+ {
+ key: 3,
+ product: "Samsung Galaxy S24",
+ sku: "SGS24-001",
+ totalImported: 30,
+ totalExported: 22,
+ currentStock: 8,
+ minStock: 5,
+ maxStock: 30,
+ unitPrice: 22000000,
+ totalValue: 176000000,
+ productImage: "assets/img/products/samsung.jpg",
+ createdDate: "2024-01-13",
+ lastUpdated: "2024-01-18"
+ },
+ {
+ key: 4,
+ product: "Dell XPS 13",
+ sku: "DXP13-001",
+ totalImported: 40,
+ totalExported: 28,
+ currentStock: 12,
+ minStock: 8,
+ maxStock: 40,
+ unitPrice: 23000000,
+ totalValue: 276000000,
+ productImage: "assets/img/products/dell.jpg",
+ createdDate: "2024-01-12",
+ lastUpdated: "2024-01-17"
+ },
+ {
+ key: 5,
+ product: "iPad Pro 11 inch",
+ sku: "IPD11-001",
+ totalImported: 60,
+ totalExported: 45,
+ currentStock: 15,
+ minStock: 10,
+ maxStock: 60,
+ unitPrice: 20000000,
+ totalValue: 300000000,
+ productImage: "assets/img/products/ipad.jpg",
+ createdDate: "2024-01-11",
+ lastUpdated: "2024-01-16"
+ }
+ ];
+
+ const dataSource = apiProducts.length > 0 ? apiProducts : (legacyProducts.length > 0 ? legacyProducts : sampleInventoryData);
+
+ const dispatch = useDispatch();
+
+ const [isFilterVisible, setIsFilterVisible] = useState(false);
+ const [searchTerm, setSearchTerm] = useState("");
+
+ // State for pagination - sync with Redux
+ const [currentPage, setCurrentPage] = useState(reduxCurrentPage || 1);
+ const [pageSize, setPageSize] = useState(reduxPageSize || 20);
+
+ // State for filter values
+ const [filterValues, setFilterValues] = useState({
+ product: '',
+ stockLevel: '',
+ quantityRange: '',
+ stockStatus: ''
+ });
+
+ // Calculate total records and pages
+ const totalRecords = totalProducts || dataSource.length;
+ const actualTotalPages = Math.ceil(totalRecords / pageSize);
+
+ useEffect(() => {
+ hideAntElements();
+
+ // Fetch products on component mount
+ if (apiProducts.length === 0) {
+ dispatch(fetchProducts({ page: currentPage, pageSize }));
+ }
+ }, [dispatch, apiProducts.length]);
+
+ // Sync local state with Redux state
+ useEffect(() => {
+ if (reduxCurrentPage && reduxCurrentPage !== currentPage) {
+ setCurrentPage(reduxCurrentPage);
+ }
+ if (reduxPageSize && reduxPageSize !== pageSize) {
+ setPageSize(reduxPageSize);
+ }
+ }, [reduxCurrentPage, reduxPageSize, currentPage, pageSize]);
+
+ const handleSearch = (e) => {
+ const value = e.target.value;
+ setSearchTerm(value);
+
+ if (value.trim()) {
+ dispatch(searchProducts(value));
+ } else {
+ dispatch(fetchProducts({ page: 1, pageSize }));
+ setCurrentPage(1);
+ }
+ };
+
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ dispatch(fetchProducts({ page, pageSize, search: searchTerm }));
+ };
+
+ const handlePageSizeChange = (newPageSize) => {
+ setPageSize(newPageSize);
+ setCurrentPage(1);
+ dispatch(fetchProducts({ page: 1, pageSize: newPageSize, search: searchTerm }));
+ };
+
+ const handleDelete = async (productId) => {
+ try {
+ const result = await MySwal.fire({
+ title: "Are you sure?",
+ text: "You won't be able to revert this!",
+ icon: "warning",
+ showCancelButton: true,
+ confirmButtonColor: "#dc3545",
+ cancelButtonColor: "#6c757d",
+ confirmButtonText: "Yes, delete it!",
+ cancelButtonText: "Cancel",
+ });
+
+ if (result.isConfirmed) {
+ await dispatch(deleteProduct(productId));
+
+ MySwal.fire({
+ title: "Deleted!",
+ text: "Product has been deleted successfully.",
+ icon: "success",
+ confirmButtonColor: "#28a745",
+ });
+
+ // Refresh the product list
+ dispatch(fetchProducts({ page: currentPage, pageSize, search: searchTerm }));
+ }
+ } catch (error) {
+ MySwal.fire({
+ title: "Error!",
+ text: "Failed to delete product. Please try again.",
+ icon: "error",
+ confirmButtonColor: "#dc3545",
+ });
+ }
+ };
+
+ const oldandlatestvalue = [
+ { value: "date", label: "Sắp xếp theo ngày" },
+ { value: "newest", label: "Mới nhất" },
+ { value: "oldest", label: "Cũ nhất" },
+ ];
+
+ const stockLevelOptions = [
+ { value: "", label: "Chọn mức tồn kho" },
+ { value: "low", label: "Tồn kho thấp" },
+ { value: "normal", label: "Tồn kho bình thường" },
+ { value: "high", label: "Tồn kho cao" },
+ ];
+
+ const stockStatusOptions = [
+ { value: "", label: "Chọn trạng thái" },
+ { value: "in-stock", label: "Còn hàng" },
+ { value: "low-stock", label: "Sắp hết hàng" },
+ { value: "out-of-stock", label: "Hết hàng" },
+ ];
+
+ const quantityRangeOptions = [
+ { value: "", label: "Chọn khoảng số lượng" },
+ { value: "0-10", label: "0 - 10" },
+ { value: "11-50", label: "11 - 50" },
+ { value: "51-100", label: "51 - 100" },
+ { value: "100+", label: "Trên 100" },
+ ];
+
+ // Handle filter value changes
+ const handleFilterChange = (filterType, value) => {
+ setFilterValues(prev => ({
+ ...prev,
+ [filterType]: value
+ }));
+ };
+
+ const columns = [
+ {
+ title: "Sản phẩm",
+ dataIndex: "product",
+ render: (text, record) => (
+ {text || record.name || record.productName}
+ ),
+ sorter: (a, b) => a.product.length - b.product.length,
+ },
+ {
+ title: "Mã",
+ dataIndex: "sku",
+ render: (_, record) => {
+ const sku = record.sku || record.code || record.productCode || '-';
+ return {sku};
+ },
+ sorter: (a, b) => {
+ const skuA = a.sku || a.code || a.productCode || '';
+ const skuB = b.sku || b.code || b.productCode || '';
+ return skuA.length - skuB.length;
+ },
+ },
+ {
+ title: "Tổng nhập kho",
+ dataIndex: "totalImported",
+ render: (_, record) => {
+ const totalImported = record.totalImported || 0;
+
+ return (
+ {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ >
+ {totalImported.toLocaleString('vi-VN')}
+
+ );
+ },
+ sorter: (a, b) => {
+ const importedA = a.totalImported || 0;
+ const importedB = b.totalImported || 0;
+ return importedA - importedB;
+ },
+ },
+ {
+ title: "Tổng xuất kho",
+ dataIndex: "totalExported",
+ render: (_, record) => {
+ const totalExported = record.totalExported || 0;
+
+ return (
+ {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ >
+ {totalExported.toLocaleString('vi-VN')}
+
+ );
+ },
+ sorter: (a, b) => {
+ const exportedA = a.totalExported || 0;
+ const exportedB = b.totalExported || 0;
+ return exportedA - exportedB;
+ },
+ },
+ {
+ title: "Tồn kho hiện tại",
+ dataIndex: "currentStock",
+ render: (_, record) => {
+ const stock = record.currentStock || record.qty || record.quantity || 0;
+ const minStock = record.minStock || 5;
+
+ let badgeColor = '#e3f2fd';
+ let textColor = '#1565c0';
+ let borderColor = '#bbdefb';
+
+ if (stock <= 0) {
+ badgeColor = '#f8d7da';
+ textColor = '#721c24';
+ borderColor = '#f5c6cb';
+ } else if (stock <= minStock) {
+ badgeColor = '#fff3cd';
+ textColor = '#856404';
+ borderColor = '#ffeaa7';
+ }
+
+ return (
+ {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ >
+ {stock.toLocaleString('vi-VN')}
+
+ );
+ },
+ sorter: (a, b) => {
+ const stockA = a.currentStock || a.qty || a.quantity || 0;
+ const stockB = b.currentStock || b.qty || b.quantity || 0;
+ return stockA - stockB;
+ },
+ },
+ {
+ title: "Giá trị tồn kho",
+ dataIndex: "totalValue",
+ render: (_, record) => {
+ const stock = record.currentStock || record.qty || record.quantity || 0;
+ const price = record.unitPrice || record.price || record.sellPrice || 0;
+ const totalValue = stock * price;
+
+ return (
+ {
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
+ }}
+ >
+ {totalValue.toLocaleString('vi-VN')} ₫
+
+ );
+ },
+ sorter: (a, b) => {
+ const valueA = (a.currentStock || 0) * (a.unitPrice || 0);
+ const valueB = (b.currentStock || 0) * (b.unitPrice || 0);
+ return valueA - valueB;
+ },
+ },
+ {
+ title: "Thao tác",
+ dataIndex: "action",
+ render: (_, record) => (
+
+
+
+
+
+ {
+ // Pre-fetch product details for editing
+ if (record.id || record.key) {
+ dispatch(fetchProduct(record.id || record.key));
+ }
+ }}
+ >
+
+
+ {
+ e.preventDefault();
+ handleDelete(record.id || record.key);
+ }}
+ >
+
+
+
+ |
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
Tồn kho
+ Quản lý thông tin tồn kho sản phẩm
+
+
+
+ -
+ Pdf}
+ >
+
+
+
+
+
+ -
+ Excel}
+ >
+
+
+
+
+
+ -
+ Print}
+ >
+
+
+
+
+
+
+
+ {/* /product list */}
+
+ {/* /Filter */}
+
+
+
+
+
+ setIsFilterVisible(!isFilterVisible)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ {/* /Filter */}
+
+ {loading ? (
+
+
+ Loading...
+
+
Đang tải dữ liệu tồn kho...
+
+ ) : error ? (
+
+
+
+
+ ) : (
+ <>
+
+
+ {/* Custom Pagination */}
+
+ >
+ )}
+
+
+
+ {/* /product list */}
+
+
+
+ );
+};
+
+export default ProductList3;