Add 'Show Non-Replenishable' filter to Products page and update API to handle non-replenishable products
This commit is contained in:
@@ -13,13 +13,18 @@ router.get('/', async (req, res) => {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 50;
|
||||
const offset = (page - 1) * limit;
|
||||
const sortColumn = req.query.sortColumn || 'title';
|
||||
const sortDirection = req.query.sortDirection === 'desc' ? 'DESC' : 'ASC';
|
||||
const sortColumn = req.query.sort || 'title';
|
||||
const sortDirection = req.query.order === 'desc' ? 'DESC' : 'ASC';
|
||||
|
||||
// Build the WHERE clause
|
||||
const conditions = ['p.visible = true'];
|
||||
const params = [];
|
||||
|
||||
// Add default replenishable filter unless explicitly showing non-replenishable
|
||||
if (req.query.showNonReplenishable !== 'true') {
|
||||
conditions.push('p.replenishable = true');
|
||||
}
|
||||
|
||||
// Handle text search filters
|
||||
if (req.query.search) {
|
||||
conditions.push('(p.title LIKE ? OR p.SKU LIKE ?)');
|
||||
@@ -195,20 +200,23 @@ router.get('/', async (req, res) => {
|
||||
params.push(parseFloat(req.query.maxStockCoverage));
|
||||
}
|
||||
|
||||
// Handle status filters
|
||||
// Handle stock status filter
|
||||
if (req.query.stockStatus && req.query.stockStatus !== 'all') {
|
||||
conditions.push('pm.stock_status = ?');
|
||||
params.push(req.query.stockStatus);
|
||||
}
|
||||
|
||||
// Combine all conditions with AND
|
||||
const whereClause = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : '';
|
||||
|
||||
// Get total count for pagination
|
||||
const [countResult] = await pool.query(
|
||||
`SELECT COUNT(DISTINCT p.product_id) as total
|
||||
const countQuery = `
|
||||
SELECT COUNT(DISTINCT p.product_id) as total
|
||||
FROM products p
|
||||
LEFT JOIN product_metrics pm ON p.product_id = pm.product_id
|
||||
WHERE ${conditions.join(' AND ')}`,
|
||||
params
|
||||
);
|
||||
${whereClause}
|
||||
`;
|
||||
const [countResult] = await pool.query(countQuery, params);
|
||||
const total = countResult[0].total;
|
||||
|
||||
// Get available filters
|
||||
@@ -229,19 +237,19 @@ router.get('/', async (req, res) => {
|
||||
p.product_id,
|
||||
COALESCE(
|
||||
(SELECT overstock_days FROM stock_thresholds st
|
||||
JOIN product_categories pc ON st.category_id = pc.category_id
|
||||
WHERE st.category_id IN (
|
||||
SELECT pc.category_id
|
||||
FROM product_categories pc
|
||||
WHERE pc.product_id = p.product_id
|
||||
AND st.vendor = p.vendor LIMIT 1),
|
||||
(SELECT overstock_days FROM stock_thresholds st
|
||||
JOIN product_categories pc ON st.category_id = pc.category_id
|
||||
WHERE pc.product_id = p.product_id
|
||||
AND st.vendor IS NULL LIMIT 1),
|
||||
)
|
||||
AND (st.vendor = p.vendor OR st.vendor IS NULL)
|
||||
ORDER BY st.vendor IS NULL
|
||||
LIMIT 1),
|
||||
(SELECT overstock_days FROM stock_thresholds st
|
||||
WHERE st.category_id IS NULL
|
||||
AND st.vendor = p.vendor LIMIT 1),
|
||||
(SELECT overstock_days FROM stock_thresholds st
|
||||
WHERE st.category_id IS NULL
|
||||
AND st.vendor IS NULL LIMIT 1),
|
||||
AND (st.vendor = p.vendor OR st.vendor IS NULL)
|
||||
ORDER BY st.vendor IS NULL
|
||||
LIMIT 1),
|
||||
90
|
||||
) as target_days
|
||||
FROM products p
|
||||
@@ -283,13 +291,18 @@ router.get('/', async (req, res) => {
|
||||
LEFT JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
LEFT JOIN categories c ON pc.category_id = c.id
|
||||
LEFT JOIN product_thresholds pt ON p.product_id = pt.product_id
|
||||
WHERE ${conditions.join(' AND ')}
|
||||
${whereClause}
|
||||
GROUP BY p.product_id
|
||||
ORDER BY ${sortColumn} ${sortDirection}
|
||||
LIMIT ? OFFSET ?
|
||||
`;
|
||||
|
||||
const [rows] = await pool.query(query, [...params, limit, offset]);
|
||||
// Add pagination params to the main query params
|
||||
const queryParams = [...params, limit, offset];
|
||||
console.log('Query:', query.replace(/\s+/g, ' '));
|
||||
console.log('Params:', queryParams);
|
||||
|
||||
const [rows] = await pool.query(query, queryParams);
|
||||
|
||||
// Transform the results
|
||||
const products = rows.map(row => ({
|
||||
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Label } from '@/components/ui/label';
|
||||
|
||||
// Column definition type
|
||||
interface ColumnDef {
|
||||
@@ -104,6 +106,7 @@ export function Products() {
|
||||
const [selectedProductId, setSelectedProductId] = useState<number | null>(null);
|
||||
const [activeView, setActiveView] = useState("all");
|
||||
const [pageSize] = useState(50);
|
||||
const [showNonReplenishable, setShowNonReplenishable] = useState(false);
|
||||
|
||||
// Group columns by their group property
|
||||
const columnsByGroup = AVAILABLE_COLUMNS.reduce((acc, col) => {
|
||||
@@ -133,6 +136,11 @@ export function Products() {
|
||||
params.append('stockStatus', activeView);
|
||||
}
|
||||
|
||||
// Add showNonReplenishable param
|
||||
if (showNonReplenishable) {
|
||||
params.append('showNonReplenishable', 'true');
|
||||
}
|
||||
|
||||
// Add other filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== '') {
|
||||
@@ -149,7 +157,7 @@ export function Products() {
|
||||
|
||||
// Query for products data
|
||||
const { data, isFetching } = useQuery({
|
||||
queryKey: ['products', currentPage, pageSize, sortColumn, sortDirection, activeView, filters],
|
||||
queryKey: ['products', currentPage, pageSize, sortColumn, sortDirection, activeView, filters, showNonReplenishable],
|
||||
queryFn: fetchProducts,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
@@ -271,6 +279,17 @@ export function Products() {
|
||||
activeFilters={filters}
|
||||
/>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="show-non-replenishable"
|
||||
checked={showNonReplenishable}
|
||||
onCheckedChange={(checked) => {
|
||||
setShowNonReplenishable(checked);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="show-non-replenishable">Show Non-Replenishable</Label>
|
||||
</div>
|
||||
{data?.pagination.total > 0 && (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{data.pagination.total.toLocaleString()} products
|
||||
|
||||
Reference in New Issue
Block a user