import { Box, Edit, Eye, Filter, 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 */}
option.value === filterValues.stockLevel)} onChange={(selectedOption) => handleFilterChange('stockLevel', selectedOption?.value || '')} />
option.value === filterValues.stockStatus)} onChange={(selectedOption) => handleFilterChange('stockStatus', selectedOption?.value || '')} />
Tìm kiếm
{/* /Filter */}
setIsFilterVisible(!isFilterVisible)} >