Merge pull request #31 from beatz174-bit/codex/refactor-additemscreen-for-quantity-input

Add quantity and carton controls to pick items
This commit is contained in:
beatz174-bit
2025-11-23 12:09:38 +10:00
committed by GitHub
3 changed files with 49 additions and 44 deletions
+20 -32
View File
@@ -6,21 +6,24 @@ import { Product } from '../models/Product';
interface PickItemRowProps { interface PickItemRowProps {
item: PickItem; item: PickItem;
product?: Product | null; product?: Product | null;
onIncrementUnit: () => void; onIncrementQuantity: () => void;
onIncrementBulk: () => void; onToggleCarton: () => void;
onToggleStatus: () => void; onSwipeLeft: () => void;
onDelete: () => void; onSwipeRight: () => void;
} }
export const PickItemRow = ({ export const PickItemRow = ({
item, item,
product, product,
onIncrementUnit, onIncrementQuantity,
onIncrementBulk, onToggleCarton,
onToggleStatus, onSwipeLeft,
onDelete, onSwipeRight,
}: PickItemRowProps) => { }: PickItemRowProps) => {
const isPicked = item.status === 'picked'; const longPressHandlers = useLongPress({ onLongPress: onToggleCarton, onClick: onIncrementQuantity });
const swipeHandlers = useSwipe({ onSwipeLeft, onSwipeRight });
const isCarton = item.quantity_bulk > 0;
return ( return (
<Stack <Stack
@@ -30,30 +33,15 @@ export const PickItemRow = ({
spacing={1} spacing={1}
sx={{ p: 1, borderRadius: 1, bgcolor: 'background.paper', boxShadow: 1 }} sx={{ p: 1, borderRadius: 1, bgcolor: 'background.paper', boxShadow: 1 }}
> >
<Stack direction="row" alignItems="center" spacing={1} flex={1}> <div>
<Checkbox color="success" checked={isPicked} onChange={onToggleStatus} /> <Typography variant="subtitle1">{product?.name ?? 'Unknown product'}</Typography>
<Stack spacing={0.5} flex={1}> <Typography variant="caption" color="text.secondary">
<Typography Qty: {item.quantity_units}
variant="subtitle1" </Typography>
sx={{ textDecoration: isPicked ? 'line-through' : 'none', display: 'flex', gap: 0.5, alignItems: 'center' }} </div>
>
{isPicked ? '✔️' : null} {product?.name ?? 'Unknown product'}
</Typography>
<Typography variant="caption" color="text.secondary">
{item.quantity_units} units / {item.quantity_bulk} bulk
</Typography>
</Stack>
</Stack>
<Stack direction="row" spacing={1} alignItems="center"> <Stack direction="row" spacing={1} alignItems="center">
<Button variant="outlined" size="small" startIcon={<Add />} onClick={onIncrementUnit}> {isCarton ? <Chip label="Carton" color="primary" size="small" /> : null}
Unit <Chip label={item.status} color={statusColor[item.status]} size="small" />
</Button>
<Button variant="outlined" size="small" startIcon={<Add />} onClick={onIncrementBulk}>
Bulk
</Button>
<Button variant="text" color="error" size="small" startIcon={<Delete />} onClick={onDelete}>
Delete
</Button>
</Stack> </Stack>
</Stack> </Stack>
); );
+14 -8
View File
@@ -19,7 +19,7 @@ export const ActivePickListScreen = () => {
[areas, pickList?.area_id], [areas, pickList?.area_id],
); );
const handleIncrement = async (itemId: string) => { const handleIncrementQuantity = async (itemId: string) => {
const existing = await db.pickItems.get(itemId); const existing = await db.pickItems.get(itemId);
if (!existing) return; if (!existing) return;
await db.pickItems.update(itemId, { await db.pickItems.update(itemId, {
@@ -28,11 +28,17 @@ export const ActivePickListScreen = () => {
}); });
}; };
const handleToggleStatus = async (itemId: string) => { const handleToggleCarton = async (itemId: string) => {
const existing = await db.pickItems.get(itemId); const existing = await db.pickItems.get(itemId);
if (!existing) return; if (!existing) return;
const nextStatus = existing.status === 'picked' ? 'pending' : 'picked'; await db.pickItems.update(itemId, {
await db.pickItems.update(itemId, { status: nextStatus, updated_at: Date.now() }); quantity_bulk: existing.quantity_bulk > 0 ? 0 : 1,
updated_at: Date.now(),
});
};
const handleSwipeLeft = async (itemId: string) => {
await db.pickItems.update(itemId, { status: 'picked', updated_at: Date.now() });
}; };
const handleDeleteItem = async (itemId: string) => { const handleDeleteItem = async (itemId: string) => {
@@ -62,10 +68,10 @@ export const ActivePickListScreen = () => {
key={item.id} key={item.id}
item={item} item={item}
product={products.find((p) => p.id === item.product_id)} product={products.find((p) => p.id === item.product_id)}
onIncrementUnit={() => handleIncrementUnit(item.id)} onIncrementQuantity={() => handleIncrementQuantity(item.id)}
onIncrementBulk={() => handleIncrementBulk(item.id)} onToggleCarton={() => handleToggleCarton(item.id)}
onToggleStatus={() => handleToggleStatus(item.id)} onSwipeLeft={() => handleSwipeLeft(item.id)}
onDelete={() => handleDeleteItem(item.id)} onSwipeRight={() => handleSwipeRight(item.id)}
/> />
))} ))}
</Stack> </Stack>
+15 -4
View File
@@ -1,17 +1,18 @@
import { import {
Autocomplete, Autocomplete,
Button, Button,
Checkbox,
Container, Container,
FormControlLabel,
InputAdornment,
Stack, Stack,
TextField, TextField,
Typography, Typography,
InputAdornment,
} from '@mui/material'; } from '@mui/material';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { NumericStepper } from '../components/NumericStepper';
import { usePickItems, useProducts } from '../hooks/dataHooks'; import { usePickItems, useProducts } from '../hooks/dataHooks';
import { useDatabase } from '../context/DBProvider'; import { useDatabase } from '../context/DBProvider';
@@ -22,8 +23,8 @@ export const AddItemScreen = () => {
const products = useProducts(); const products = useProducts();
const [selectedProduct, setSelectedProduct] = useState<typeof products[number] | null>(null); const [selectedProduct, setSelectedProduct] = useState<typeof products[number] | null>(null);
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const [units, setUnits] = useState(1); const [quantity, setQuantity] = useState(1);
const [bulk, setBulk] = useState(0); const [isCarton, setIsCarton] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const unitLabel = selectedProduct?.unit_type ?? 'Units'; const unitLabel = selectedProduct?.unit_type ?? 'Units';
const cartonLabel = selectedProduct?.bulk_name ?? 'Cartons'; const cartonLabel = selectedProduct?.bulk_name ?? 'Cartons';
@@ -45,6 +46,16 @@ export const AddItemScreen = () => {
} }
}, [filteredProducts, selectedProduct]); }, [filteredProducts, selectedProduct]);
useEffect(() => {
setQuantity(1);
setIsCarton(false);
}, [selectedProduct]);
const quantityHelperText = selectedProduct?.unit_type
? `Enter ${selectedProduct.unit_type} to pick`
: 'Enter the quantity to pick';
const cartonLabel = selectedProduct?.bulk_name ? `Carton (${selectedProduct.bulk_name})` : 'Carton';
const addItem = async () => { const addItem = async () => {
const productId = selectedProduct?.id; const productId = selectedProduct?.id;