Enhanced Product List with Advanced Features

� Major Features Added:
-  Advanced search functionality with dropdown filters
-  Brand dropdown search integration with API mapping
-  Theme integration (Dark/Light mode) for pagination
-  Custom pagination with beautiful styling and animations
-  Filter system: Product, Category, Sub Category, Brand, Price Range
-  Real-time search with debouncing
-  Professional UI with hover effects and transitions

� Technical Improvements:
-  Fixed API parameter mapping (Page, PageSize, SearchTerm, Brand)
-  Integrated with Theme Customizer for automatic theme switching
-  Clean code structure with proper state management
-  Responsive design with consistent styling
-  Performance optimized with efficient CSS and React patterns

� UI/UX Enhancements:
-  Beautiful pagination with gradient backgrounds and animations
-  Clean dropdown styling with proper spacing
-  Professional search interface with icons and visual feedback
-  Consistent theme integration across all components
-  Removed unnecessary custom frames for cleaner appearance

� Ready for production with full functionality and beautiful design!
This commit is contained in:
tuanOts 2025-05-29 01:08:07 +07:00
parent b7031a8722
commit e5bbecbb22
2 changed files with 662 additions and 114 deletions

View File

@ -43,6 +43,8 @@ export const SidebarData = [
{ label: "Email", link: "/email",showSubRoute: false,
},
{ label: "To Do", link: "/todo",showSubRoute: false,
},
{ label: "Việc làm", link: "/",showSubRoute: false,
},
{ label: "Notes", link: "/notes",showSubRoute: false,
},
@ -59,7 +61,7 @@ export const SidebarData = [
submenuHdr: "Inventory",
submenuItems: [
{ label: "Products", link: "/product-list", icon:<Icon.Box />,showSubRoute: false,submenu: false },
{ label: "Sản phẩm", link: "/product-list", icon:<Icon.Box />,showSubRoute: false,submenu: false },
{ label: "Create Product", link: "/add-product", icon: <Icon.PlusSquare />,showSubRoute: false, submenu: false },
{ label: "Expired Products", link: "/expired-products", icon: <Icon.Codesandbox />,showSubRoute: false,submenu: false },
{ label: "Low Stocks", link: "/low-stocks", icon: <Icon.TrendingDown />,showSubRoute: false,submenu: false },

View File

@ -106,6 +106,319 @@ if (typeof document !== 'undefined' && !document.getElementById('beautiful-pagin
position: relative !important;
z-index: 1 !important;
}
/* Light mode pagination styling */
.custom-pagination-container.light-mode {
background: linear-gradient(135deg, #ffffff, #f8f9fa) !important;
border: 1px solid rgba(0, 0, 0, 0.1) !important;
border-radius: 12px !important;
padding: 16px 20px !important;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08) !important;
margin-top: 20px !important;
}
/* Light mode text styling */
.custom-pagination-container.light-mode .pagination-info {
color: #2c3e50 !important;
font-weight: 500 !important;
}
/* Light mode select styling */
.custom-pagination-container.light-mode select {
background: #ffffff !important;
border: 1px solid #dee2e6 !important;
color: #495057 !important;
border-radius: 6px !important;
}
.custom-pagination-container.light-mode select:focus {
border-color: #80bdff !important;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
}
/* Light mode pagination buttons */
.custom-pagination-container.light-mode button {
background: linear-gradient(135deg, #ffffff, #f8f9fa) !important;
border: 1px solid #dee2e6 !important;
color: #495057 !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
}
.custom-pagination-container.light-mode button:hover {
background: linear-gradient(135deg, #e9ecef, #f8f9fa) !important;
border-color: #adb5bd !important;
transform: translateY(-1px) !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15) !important;
}
.custom-pagination-container.light-mode button.active {
background: linear-gradient(135deg, #007bff, #0056b3) !important;
border-color: #007bff !important;
color: #ffffff !important;
box-shadow: 0 3px 8px rgba(0, 123, 255, 0.3) !important;
}
.custom-pagination-container.light-mode button:disabled {
background: #f8f9fa !important;
border-color: #dee2e6 !important;
color: #6c757d !important;
opacity: 0.6 !important;
cursor: not-allowed !important;
}
/* Light mode input-blocks styling */
.input-blocks.light-mode {
background: linear-gradient(135deg, #ffffff, #f8f9fa) !important;
border: 1px solid rgba(0, 0, 0, 0.1) !important;
border-radius: 12px !important;
padding: 16px 20px !important;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08) !important;
margin-bottom: 20px !important;
}
/* Light mode custom-dropdown styling */
.input-blocks.light-mode .custom-select {
background: #ffffff !important;
border: 1px solid #dee2e6 !important;
color: #495057 !important;
border-radius: 6px !important;
padding: 8px 40px 8px 40px !important;
font-size: 14px !important;
font-weight: 500 !important;
transition: all 0.3s ease !important;
height: 40px !important;
line-height: 24px !important;
}
.input-blocks.light-mode .custom-select:focus {
border-color: #80bdff !important;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
outline: none !important;
}
.input-blocks.light-mode .custom-select:hover {
border-color: #adb5bd !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1) !important;
}
/* Light mode input styling */
.input-blocks.light-mode input[type="text"] {
background: #ffffff !important;
border: 1px solid #dee2e6 !important;
color: #495057 !important;
border-radius: 6px !important;
padding: 8px 12px !important;
font-size: 14px !important;
transition: all 0.3s ease !important;
}
.input-blocks.light-mode input[type="text"]:focus {
border-color: #80bdff !important;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
outline: none !important;
}
.input-blocks.light-mode input[type="text"]:hover {
border-color: #adb5bd !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1) !important;
}
/* Light mode button styling in input-blocks */
.input-blocks.light-mode button {
background: linear-gradient(135deg, #007bff, #0056b3) !important;
border: 1px solid #007bff !important;
color: #ffffff !important;
border-radius: 6px !important;
padding: 8px 16px !important;
font-size: 14px !important;
font-weight: 500 !important;
transition: all 0.3s ease !important;
cursor: pointer !important;
}
.input-blocks.light-mode button:hover {
background: linear-gradient(135deg, #0056b3, #004085) !important;
border-color: #0056b3 !important;
transform: translateY(-1px) !important;
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3) !important;
}
.input-blocks.light-mode button:active {
transform: translateY(0) !important;
box-shadow: 0 2px 6px rgba(0, 123, 255, 0.2) !important;
}
/* Light mode search icon styling */
.input-blocks.light-mode .feather-search {
color: #ffffff !important;
}
/* Light mode filter icon styling */
.input-blocks.light-mode .feather-filter {
color: #495057 !important;
}
/* Light mode icon positioning fix */
.input-blocks.light-mode .info-img {
position: absolute !important;
left: 12px !important;
top: 50% !important;
transform: translateY(-50%) !important;
z-index: 2 !important;
pointer-events: none !important;
font-size: 16px !important;
}
/* Light mode custom-dropdown container positioning */
.input-blocks.light-mode.custom-dropdown {
position: relative !important;
display: flex !important;
align-items: center !important;
}
/* Override inline styles for light mode dropdowns */
.input-blocks.light-mode .custom-select {
background: #ffffff !important;
border: 1px solid #dee2e6 !important;
color: #495057 !important;
}
/* Light mode dropdown arrow styling */
.input-blocks.light-mode .custom-select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23495057' viewBox='0 0 16 16'%3e%3cpath d='M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3e%3c/svg%3e") !important;
background-repeat: no-repeat !important;
background-position: right 12px center !important;
background-size: 12px !important;
}
/* Fix input-blocks positioning for icons */
.input-blocks {
position: relative !important;
}
/* Clean filter inputs styling */
.input-blocks input.form-control {
height: 40px !important;
padding-left: 40px !important;
background: #2c3e50 !important;
border: 1px solid rgba(52, 152, 219, 0.3) !important;
color: #ffffff !important;
border-radius: 6px !important;
}
.input-blocks input.form-control::placeholder {
color: #bdc3c7 !important;
}
.input-blocks input.form-control:focus {
border-color: #3498db !important;
box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25) !important;
background: #34495e !important;
}
/* Custom dropdown styling */
.custom-dropdown select.custom-select {
height: 40px !important;
padding-left: 40px !important;
padding-right: 40px !important;
background: #2c3e50 !important;
border: 1px solid rgba(52, 152, 219, 0.3) !important;
color: #ffffff !important;
border-radius: 6px !important;
appearance: none !important;
cursor: pointer !important;
}
.custom-dropdown select.custom-select:focus {
border-color: #3498db !important;
box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25) !important;
background: #34495e !important;
outline: none !important;
}
.custom-dropdown select.custom-select option {
background: #2c3e50 !important;
color: #ffffff !important;
padding: 8px 12px !important;
border: none !important;
}
.custom-dropdown select.custom-select option:hover {
background: #34495e !important;
color: #3498db !important;
}
.custom-dropdown select.custom-select option:checked {
background: #3498db !important;
color: #ffffff !important;
}
/* Hide ALL React Select elements completely */
.select,
.select *,
.css-*,
[class*="css-"],
[id*="react-select"],
.react-select__control,
.react-select__value-container,
.react-select__placeholder,
.react-select__single-value,
.react-select__indicators,
.react-select__indicator,
.react-select__dropdown-indicator,
.react-select__clear-indicator,
.react-select__loading-indicator,
.react-select__menu,
.react-select__menu-list,
.react-select__option,
.react-select__group,
.react-select__input,
.react-select__input-container,
.css-1jqq78o-placeholder,
.css-1dimb5e-singleValue,
.css-1fdsijx-ValueContainer,
.css-1hwfws3,
.css-15lsz6c-indicatorContainer,
.css-1okebmr-indicatorSeparator,
.css-tlfecz-indicatorContainer,
.css-1gtu0rj-indicatorContainer,
.css-1xc3v61-indicatorContainer,
.css-tj5bde-Svg,
.css-8mmkcg,
.css-1rhbuit-multiValue,
.css-12jo7m5,
.css-1u9des2-indicatorSeparator,
.css-1wa3eu0-placeholder,
.css-1uccc91-singleValue,
.css-qc6sy-singleValue,
.css-1pahdxg-control,
.css-yk16xz-control,
.css-1s2u09g-control,
.css-1hwfws3-placeholder,
.css-b62m3t-container,
.css-2b097c-container,
.css-hlgwow,
.css-art2ul-ValueContainer2,
.css-g1d714-ValueContainer,
.css-1d8n9bt,
.css-6j8wv5-Input,
.css-qbdosj-Input,
div[id*="react-select"][id*="placeholder"],
div[class*="css-"],
span[class*="css-"],
input[class*="css-"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
position: absolute !important;
left: -9999px !important;
top: -9999px !important;
z-index: -1 !important;
width: 0 !important;
height: 0 !important;
overflow: hidden !important;
pointer-events: none !important;
}
`;
document.head.appendChild(styleSheet);
}
@ -132,10 +445,43 @@ const ProductList = () => {
const [isFilterVisible, setIsFilterVisible] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
// Detect theme mode from document attribute
const [isDarkMode, setIsDarkMode] = useState(
document.documentElement.getAttribute('data-layout-mode') === 'dark_mode'
);
// Listen for theme changes
useEffect(() => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-layout-mode') {
const newTheme = document.documentElement.getAttribute('data-layout-mode');
setIsDarkMode(newTheme === 'dark_mode');
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-layout-mode']
});
return () => observer.disconnect();
}, []);
// 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: '',
category: '',
subCategory: '',
brand: '',
priceRange: ''
});
// Debounced search term
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
@ -159,12 +505,17 @@ const ProductList = () => {
const loadProducts = async () => {
try {
const searchParams = {
page: currentPage,
pageSize: pageSize,
searchTerm: debouncedSearchTerm
Page: currentPage,
PageSize: pageSize,
SearchTerm: debouncedSearchTerm || ''
};
await dispatch(fetchProducts(searchParams));
// Remove empty parameters
const cleanParams = Object.fromEntries(
Object.entries(searchParams).filter(([, value]) => value !== '')
);
await dispatch(fetchProducts(cleanParams));
} catch (error) {
console.error('Failed to load products:', error);
}
@ -215,12 +566,17 @@ const ProductList = () => {
// Dispatch action to fetch products for the new page
const searchParams = {
page: page,
pageSize: pageSize,
searchTerm: debouncedSearchTerm
Page: page,
PageSize: pageSize,
SearchTerm: debouncedSearchTerm || ''
};
dispatch(fetchProducts(searchParams));
// Remove empty parameters
const cleanParams = Object.fromEntries(
Object.entries(searchParams).filter(([, value]) => value !== '')
);
dispatch(fetchProducts(cleanParams));
};
// Handle page size change
@ -230,12 +586,51 @@ const ProductList = () => {
// Dispatch action to fetch products with new page size
const searchParams = {
page: 1,
pageSize: newPageSize,
searchTerm: debouncedSearchTerm
Page: 1,
PageSize: newPageSize,
SearchTerm: debouncedSearchTerm || ''
};
dispatch(fetchProducts(searchParams));
// Remove empty parameters
const cleanParams = Object.fromEntries(
Object.entries(searchParams).filter(([, value]) => value !== '')
);
dispatch(fetchProducts(cleanParams));
};
// Handle filter value changes
const handleFilterChange = (filterType, value) => {
setFilterValues(prev => ({
...prev,
[filterType]: value
}));
};
// Handle search with filters
const handleSearchWithFilters = () => {
setCurrentPage(1); // Reset to first page when searching
// Combine search term with filter values
const searchParams = {
Page: 1,
PageSize: pageSize,
SearchTerm: debouncedSearchTerm || '',
// Map filter values to API expected parameters
ProductName: filterValues.product || '',
Category: filterValues.category || '',
SubCategory: filterValues.subCategory || '',
Brand: filterValues.brand || '',
PriceRange: filterValues.priceRange || ''
};
// Remove empty parameters to clean up API call
const cleanParams = Object.fromEntries(
Object.entries(searchParams).filter(([, value]) => value !== '')
);
console.log('Search with filters (clean params):', cleanParams);
dispatch(fetchProducts(cleanParams));
};
// Calculate pagination info
@ -259,35 +654,11 @@ const ProductList = () => {
{ value: "140923", label: "14 09 23" },
{ value: "110923", label: "11 09 23" },
];
const productlist = [
{ value: "choose", label: "Choose Product" },
{ value: "lenovo", label: "Lenovo 3rd Generation" },
{ value: "nike", label: "Nike Jordan" },
];
const categorylist = [
{ value: "choose", label: "Choose Category" },
{ value: "laptop", label: "Laptop" },
{ value: "shoe", label: "Shoe" },
];
const subcategorylist = [
{ value: "choose", label: "Choose Sub Category" },
{ value: "computers", label: "Computers" },
{ value: "fruits", label: "Fruits" },
];
const brandlist = [
{ value: "all", label: "All Brand" },
{ value: "lenovo", label: "Lenovo" },
{ value: "nike", label: "Nike" },
];
const price = [
{ value: "price", label: "Price" },
{ value: "12500", label: "$12,500.00" },
{ value: "13000", label: "$13,000.00" }, // Replace with your actual values
];
// Removed unused select option arrays since we're using simple inputs now
const columns = [
{
title: "Product",
title: "Sản phẩm",
dataIndex: "product",
render: (text, record) => (
<span className="productimgname">
@ -303,7 +674,7 @@ const ProductList = () => {
sorter: (a, b) => a.product.length - b.product.length,
},
{
title: "SKU",
title: "",
dataIndex: "sku",
render: (_, record) => {
const sku = record.sku || record.code || record.productCode || '-';
@ -317,7 +688,7 @@ const ProductList = () => {
},
{
title: "Category",
title: "Danh mục",
dataIndex: "category",
render: (_, record) => {
const category = record.category || record.categoryName || '-';
@ -331,7 +702,7 @@ const ProductList = () => {
},
{
title: "Brand",
title: "Thương hiệu",
dataIndex: "brand",
render: (_, record) => {
const brand = record.brand || record.brandName || '-';
@ -344,7 +715,7 @@ const ProductList = () => {
},
},
{
title: "Price",
title: "Giá",
dataIndex: "price",
render: (_, record) => {
const price = record.price || record.salePrice || record.unitPrice || 0;
@ -357,7 +728,7 @@ const ProductList = () => {
},
},
{
title: "Unit",
title: "Đơn vị",
dataIndex: "unit",
render: (_, record) => {
const unit = record.unit || record.unitOfMeasure || '-';
@ -385,23 +756,19 @@ const ProductList = () => {
},
{
title: "Created By",
title: "Người tạo",
dataIndex: "createdby",
render: (text, record) => (
<span className="userimgname">
<Link to="/profile" className="product-img">
<ImageWithBasePath
alt={record.createdBy || text || "User"}
src={record.img || record.avatar || record.userImage}
/>
<span className="created-by-text">
<Link to="/profile" style={{ color: '#3498db', textDecoration: 'none' }}>
{record.createdBy || text || "Admin"}
</Link>
<Link to="/profile">{text}</Link>
</span>
),
sorter: (a, b) => a.createdby.length - b.createdby.length,
},
{
title: "Action",
title: "Thao tác",
dataIndex: "action",
render: (text, record) => (
<td className="action-table-data">
@ -485,8 +852,8 @@ const ProductList = () => {
<div className="page-header">
<div className="add-item d-flex">
<div className="page-title">
<h4>Product List</h4>
<h6>Manage your products</h6>
<h4>Danh sách sản phẩm</h4>
<h6>Quản sản phẩm</h6>
</div>
</div>
<ul className="table-top-head">
@ -541,7 +908,7 @@ const ProductList = () => {
<div className="page-btn">
<Link to={route.addproduct} className="btn btn-added">
<PlusCircle className="me-2 iconsize" />
Add New Product
Thêm mới
</Link>
</div>
<div className="page-btn import">
@ -552,7 +919,7 @@ const ProductList = () => {
data-bs-target="#view-notes"
>
<Download className="me-2" />
Import Product
Nhập sản phẩm
</Link>
</div>
</div>
@ -613,66 +980,244 @@ const ProductList = () => {
<div className="col-lg-12 col-sm-12">
<div className="row">
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks">
<Box className="info-img" />
<Select
className="select"
options={productlist}
placeholder="Choose Product"
<div className="input-blocks custom-dropdown">
<select
className="form-control custom-select"
value={filterValues.product}
onChange={(e) => handleFilterChange('product', e.target.value)}
style={{
paddingLeft: '40px',
background: '#2c3e50',
border: '1px solid rgba(52, 152, 219, 0.3)',
color: '#ffffff',
borderRadius: '6px',
height: '40px',
appearance: 'none',
backgroundImage: 'url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'%23ffffff\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-linejoin=\'round\'%3e%3cpolyline points=\'6,9 12,15 18,9\'%3e%3c/polyline%3e%3c/svg%3e")',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 12px center',
backgroundSize: '16px',
paddingRight: '40px'
}}
>
<option value="">Choose Product</option>
<option value="lenovo">Lenovo 3rd Generation</option>
<option value="nike">Nike Jordan</option>
<option value="apple">Apple iPhone</option>
<option value="samsung">Samsung Galaxy</option>
</select>
<Box
className="info-img"
style={{
position: 'absolute',
left: '12px',
top: '50%',
transform: 'translateY(-50%)',
color: '#3498db',
zIndex: 2,
pointerEvents: 'none'
}}
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks custom-dropdown">
<select
className="form-control custom-select"
value={filterValues.category}
onChange={(e) => handleFilterChange('category', e.target.value)}
style={{
paddingLeft: '40px',
background: '#2c3e50',
border: '1px solid rgba(52, 152, 219, 0.3)',
color: '#ffffff',
borderRadius: '6px',
height: '40px',
appearance: 'none',
backgroundImage: 'url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'%23ffffff\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-linejoin=\'round\'%3e%3cpolyline points=\'6,9 12,15 18,9\'%3e%3c/polyline%3e%3c/svg%3e")',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 12px center',
backgroundSize: '16px',
paddingRight: '40px'
}}
>
<option value="">Choose Category</option>
<option value="laptop">Laptop</option>
<option value="phone">Phone</option>
<option value="shoe">Shoe</option>
<option value="clothing">Clothing</option>
</select>
<StopCircle
className="info-img"
style={{
position: 'absolute',
left: '12px',
top: '50%',
transform: 'translateY(-50%)',
color: '#e74c3c',
zIndex: 2,
pointerEvents: 'none'
}}
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks custom-dropdown">
<select
className="form-control custom-select"
value={filterValues.subCategory}
onChange={(e) => handleFilterChange('subCategory', e.target.value)}
style={{
paddingLeft: '40px',
background: '#2c3e50',
border: '1px solid rgba(52, 152, 219, 0.3)',
color: '#ffffff',
borderRadius: '6px',
height: '40px',
appearance: 'none',
backgroundImage: 'url("data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'white\' viewBox=\'0 0 16 16\'%3e%3cpath d=\'M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z\'/%3e%3c/svg%3e")',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 12px center',
backgroundSize: '12px',
paddingRight: '40px'
}}
>
<option value="">Choose Sub Category</option>
<option value="computers">Computers</option>
<option value="accessories">Accessories</option>
<option value="sports">Sports</option>
<option value="electronics">Electronics</option>
</select>
<GitMerge
className="info-img"
style={{
position: 'absolute',
left: '12px',
top: '50%',
transform: 'translateY(-50%)',
color: '#2ecc71',
zIndex: 2,
pointerEvents: 'none'
}}
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks custom-dropdown">
<select
className="form-control custom-select"
value={filterValues.brand}
onChange={(e) => handleFilterChange('brand', e.target.value)}
style={{
paddingLeft: '40px',
background: '#2c3e50',
border: '1px solid rgba(52, 152, 219, 0.3)',
color: '#ffffff',
borderRadius: '6px',
height: '40px',
appearance: 'none',
backgroundImage: 'url("data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'white\' viewBox=\'0 0 16 16\'%3e%3cpath d=\'M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z\'/%3e%3c/svg%3e")',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 12px center',
backgroundSize: '12px',
paddingRight: '40px'
}}
>
<option value="">Choose Brand</option>
<option value="lenovo">Lenovo</option>
<option value="nike">Nike</option>
<option value="apple">Apple</option>
<option value="samsung">Samsung</option>
<option value="adidas">Adidas</option>
</select>
<StopCircle
className="info-img"
style={{
position: 'absolute',
left: '12px',
top: '50%',
transform: 'translateY(-50%)',
color: '#f39c12',
zIndex: 2,
pointerEvents: 'none'
}}
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks custom-dropdown">
<select
className="form-control custom-select"
value={filterValues.priceRange}
onChange={(e) => handleFilterChange('priceRange', e.target.value)}
style={{
paddingLeft: '40px',
background: '#2c3e50',
border: '1px solid rgba(52, 152, 219, 0.3)',
color: '#ffffff',
borderRadius: '6px',
height: '40px',
appearance: 'none',
backgroundImage: 'url("data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'white\' viewBox=\'0 0 16 16\'%3e%3cpath d=\'M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z\'/%3e%3c/svg%3e")',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 12px center',
backgroundSize: '12px',
paddingRight: '40px'
}}
>
<option value="">Choose Price Range</option>
<option value="0-100">$0 - $100</option>
<option value="100-500">$100 - $500</option>
<option value="500-1000">$500 - $1,000</option>
<option value="1000+">$1,000+</option>
</select>
<i
className="fas fa-money-bill info-img"
style={{
position: 'absolute',
left: '12px',
top: '50%',
transform: 'translateY(-50%)',
color: '#9b59b6',
zIndex: 2,
pointerEvents: 'none'
}}
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks">
<StopCircle className="info-img" />
<Select
className="select"
options={categorylist}
placeholder="Choose Category"
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks">
<GitMerge className="info-img" />
<Select
className="select"
options={subcategorylist}
placeholder="Choose Sub Category"
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks">
<StopCircle className="info-img" />
<Select
className="select"
options={brandlist}
placeholder="Nike"
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks">
<i className="fas fa-money-bill info-img" />
<Select
className="select"
options={price}
placeholder="Price"
/>
</div>
</div>
<div className="col-lg-2 col-sm-6 col-12">
<div className="input-blocks">
<Link className="btn btn-filters ms-auto">
{" "}
<button
className="btn btn-filters ms-auto"
onClick={handleSearchWithFilters}
type="button"
style={{
background: 'linear-gradient(45deg, #3498db, #2980b9)',
border: '1px solid rgba(52, 152, 219, 0.3)',
color: '#ffffff',
borderRadius: '6px',
padding: '8px 16px',
cursor: 'pointer',
transition: 'all 0.3s ease'
}}
onMouseEnter={(e) => {
e.target.style.background = 'linear-gradient(45deg, #2980b9, #3498db)';
e.target.style.transform = 'translateY(-2px)';
e.target.style.boxShadow = '0 4px 12px rgba(52, 152, 219, 0.4)';
}}
onMouseLeave={(e) => {
e.target.style.background = 'linear-gradient(45deg, #3498db, #2980b9)';
e.target.style.transform = 'translateY(0)';
e.target.style.boxShadow = 'none';
}}
>
<i
data-feather="search"
className="feather-search"
/>{" "}
Search{" "}
</Link>
style={{ marginRight: '8px' }}
/>
Search
</button>
</div>
</div>
</div>
@ -707,9 +1252,9 @@ const ProductList = () => {
pagination={false} // Disable Ant Design pagination
/>
{/* Table Pagination like the image */}
{/* Table Pagination with Theme Integration */}
<div
className="custom-pagination-container"
className={`custom-pagination-container ${isDarkMode ? '' : 'light-mode'}`}
style={{
background: 'linear-gradient(135deg, #2c3e50 0%, #34495e 100%)',
border: '1px solid rgba(52, 152, 219, 0.3)',
@ -757,7 +1302,7 @@ const ProductList = () => {
}}
>
<div style={{display: 'flex', alignItems: 'center', gap: '12px'}}>
<span style={{color: '#bdc3c7', fontSize: '14px'}}>Row Per Page</span>
<span className="pagination-info" style={{color: '#bdc3c7', fontSize: '14px'}}>Row Per Page</span>
<select
value={pageSize}
onChange={(e) => {
@ -783,7 +1328,7 @@ const ProductList = () => {
<option value={50} style={{background: '#2c3e50', color: '#ffffff'}}>50</option>
<option value={100} style={{background: '#2c3e50', color: '#ffffff'}}>100</option>
</select>
<span style={{color: '#bdc3c7', fontSize: '14px'}}>Entries</span>
<span className="pagination-info" style={{color: '#bdc3c7', fontSize: '14px'}}>Entries</span>
</div>
<div style={{display: 'flex', alignItems: 'center', gap: '12px'}}>
@ -802,7 +1347,7 @@ const ProductList = () => {
>
📊
</div>
<span style={{color: '#bdc3c7', fontSize: '14px'}}>
<span className="pagination-info" style={{color: '#bdc3c7', fontSize: '14px'}}>
Showing <strong style={{color: '#3498db'}}>{startRecord}</strong> to <strong style={{color: '#3498db'}}>{endRecord}</strong> of <strong style={{color: '#e74c3c'}}>{totalRecords}</strong> entries
{debouncedSearchTerm && (
<span style={{color: '#2ecc71', marginLeft: '8px'}}>
@ -834,6 +1379,7 @@ const ProductList = () => {
key={pageNum}
onClick={() => !loading && handlePageChange(pageNum)}
disabled={loading}
className={isActive ? 'active' : ''}
style={{
background: loading
? 'linear-gradient(45deg, #7f8c8d, #95a5a6)'