From 2e69cd9d0ca613cbdc96623e0d41cb2c98bba6b5 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Tue, 25 Nov 2025 14:35:22 +1000 Subject: [PATCH] modified: src/screens/ActivePickListScreen.tsx --- src/components/PickItemRow.tsx | 51 ++++++---- src/screens/ActivePickListScreen.tsx | 145 ++++++++++++++++++++++++--- 2 files changed, 163 insertions(+), 33 deletions(-) diff --git a/src/components/PickItemRow.tsx b/src/components/PickItemRow.tsx index ea26fe7..5a8a813 100644 --- a/src/components/PickItemRow.tsx +++ b/src/components/PickItemRow.tsx @@ -68,7 +68,6 @@ export const PickItemRow = ({ const handleRowClick = () => { if (!isNarrowScreen || isControlsOpen) return; - setIsControlsOpen(true); }; @@ -84,7 +83,6 @@ export const PickItemRow = ({ const handleRowKeyDown = (event: React.KeyboardEvent) => { if (!isNarrowScreen) return; - if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); setIsControlsOpen(true); @@ -134,19 +132,18 @@ export const PickItemRow = ({ component="span" variant="subtitle1" noWrap - sx={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', fontWeight: 600 }} + sx={{ + minWidth: 0, + overflow: 'hidden', + textOverflow: 'ellipsis', + fontWeight: 600, + whiteSpace: 'nowrap', + }} > - {product?.name ?? 'Unknown product'} - - - Qty: {item.quantity} {packagingLabel} + {`${item.quantity} x ${product?.name ?? 'Unknown product'}`} + {isNarrowScreen && ( Tap to adjust quantity and packaging @@ -154,6 +151,7 @@ export const PickItemRow = ({ )} + {isNarrowScreen ? ( @@ -175,6 +173,7 @@ export const PickItemRow = ({ > + + + { @@ -220,21 +221,32 @@ export const PickItemRow = ({ id="item-controls-title" sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} > - - {product?.name ?? 'Unknown product'} - - + {`${item.quantity} x ${product?.name ?? 'Unknown product'}`} + + + + Quantity: {item.quantity} {packagingLabel} + + + + { diff --git a/src/screens/ActivePickListScreen.tsx b/src/screens/ActivePickListScreen.tsx index 79707aa..481917e 100644 --- a/src/screens/ActivePickListScreen.tsx +++ b/src/screens/ActivePickListScreen.tsx @@ -15,6 +15,7 @@ import { RadioGroup, Radio, } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; import { Link as RouterLink, useParams, useNavigate } from 'react-router-dom'; import { useEffect, useMemo, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; @@ -49,6 +50,10 @@ export const ActivePickListScreen = () => { const [isBatchUpdating, setIsBatchUpdating] = useState(false); const [packagingFilter, setPackagingFilter] = useState<'all' | 'units' | 'cartons'>('all'); + // NEW: search within the list and category filter for the visible list + const [listSearch, setListSearch] = useState(''); + const [categoryFilter, setCategoryFilter] = useState<'all' | string>('all'); + // Keep local item state in sync with DB-driven `items` useEffect(() => { setItemState((current: PickItem[]) => { @@ -179,9 +184,7 @@ export const ActivePickListScreen = () => { const filteredProducts = useMemo(() => { const normalizedQuery = query.trim().toLowerCase(); - const availableProducts = categoryFilteredProducts.filter( - (product: Product) => !productIdsInList.has(product.id), - ); + const availableProducts = categoryFilteredProducts.filter((product: Product) => !productIdsInList.has(product.id)); if (!normalizedQuery) return availableProducts; @@ -330,6 +333,62 @@ export const ActivePickListScreen = () => { return arr; }, [itemsAfterShowPicked, productMap, packagingFilter]); + // --- NEW: build category options from pickList.categories (fallback to all categories) --- + const categoryOptions = useMemo(() => { + // helper maps + const idToName = new Map(categoriesList.map((c) => [c.id, c.name])); + const nameToId = new Map(categoriesList.map((c) => [c.name.trim().toLowerCase(), c.id])); + + if (!pickList?.categories || pickList.categories.length === 0) { + // fallback: show all categories + return categoriesList + .map((c) => ({ id: c.id, name: c.name })) + .sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })); + } + + // Build from pickList.categories while resolving legacy names -> ids + const seen = new Map(); + pickList.categories.forEach((entry: string) => { + if (!entry) return; + + if (idToName.has(entry)) { + seen.set(entry, idToName.get(entry)!); + } else { + const resolved = nameToId.get(entry.trim().toLowerCase()); + if (resolved) { + seen.set(resolved, idToName.get(resolved)!); + } + } + }); + + // Convert to array and sort by name + return Array.from(seen.entries()) + .map(([id, name]) => ({ id, name })) + .sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })); + }, [pickList?.categories, categoriesList]); + + // --- NEW: apply categoryFilter and listSearch to visibleItems --- + const visibleItemsFiltered = useMemo(() => { + const q = listSearch.trim().toLowerCase(); + let arr = visibleItems; + + if (categoryFilter !== 'all') { + arr = arr.filter((item) => { + const product = productMap.get(item.product_id); + return (product?.category ?? '') === categoryFilter; + }); + } + + if (!q) return arr; + + return arr.filter((item) => { + const product = productMap.get(item.product_id); + const catName = categoriesById.get(product?.category ?? '') ?? product?.category ?? ''; + const searchableText = `${product?.name ?? ''} ${catName} ${product?.barcode ?? ''}`.toLowerCase(); + return searchableText.includes(q); + }); + }, [visibleItems, productMap, listSearch, categoryFilter, categoriesById]); + return ( @@ -416,6 +475,49 @@ export const ActivePickListScreen = () => { + + {/* NEW: Search List (left) and Category dropdown (right) on the same row */} + + setListSearch(e.target.value)} + sx={{ flexGrow: 1, minWidth: 160 }} + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + + setCategoryFilter((e.target.value as any) ?? 'all')} + sx={{ width: { xs: '100%', sm: 240 }, ml: { xs: 0, sm: 2 }, mt: { xs: 1, sm: 0 } }} + > + + {categoryOptions.map((opt) => ( + + ))} + + {pickList?.notes ? ( @@ -424,18 +526,31 @@ export const ActivePickListScreen = () => { ) : null} - {visibleItems.map((item) => ( - handleIncrementQuantity(item.id)} - onDecrementQuantity={() => handleDecrementQuantity(item.id)} - onToggleCarton={() => handleToggleCarton(item.id)} - onStatusChange={(status) => handleStatusChange(item.id, status)} - onDelete={() => handleDeleteItem(item.id)} - /> - ))} + {/* NEW: empty-filter message */} + {visibleItemsFiltered.length === 0 ? ( + visibleItems.length === 0 ? ( + + No items in this pick list + + ) : ( + + No items match the filter + + ) + ) : ( + visibleItemsFiltered.map((item) => ( + handleIncrementQuantity(item.id)} + onDecrementQuantity={() => handleDecrementQuantity(item.id)} + onToggleCarton={() => handleToggleCarton(item.id)} + onStatusChange={(status) => handleStatusChange(item.id, status)} + onDelete={() => handleDeleteItem(item.id)} + /> + )) + )}