Merge branch 'main' into codex/add-quantity-and-is_carton-fields-to-pickitem

This commit is contained in:
beatz174-bit
2025-11-23 12:07:03 +10:00
committed by GitHub
3 changed files with 55 additions and 51 deletions
+8 -17
View File
@@ -157,10 +157,8 @@ quantities remain distinct.
### ActivePickListScreen ### ActivePickListScreen
- List of PickItems\ - List of PickItems\
- Tap = +1 of the item's unit type\ - Use the checkbox in each row to toggle between pending and picked without removing the item\
- Long-press = +1 of the item's unit type\ - Row controls provide explicit +1 unit and +1 bulk actions (no long-press or swipe)\
- Swipe left = mark picked\
- Swipe right = delete\
- Add Item button\ - Add Item button\
- Complete List button - Complete List button
@@ -193,23 +191,15 @@ quantities remain distinct.
------------------------------------------------------------------------ ------------------------------------------------------------------------
## 6. Gestures & Interaction Rules ## 6. Interaction Rules
### Tap ### Checkbox Toggle
Increase `quantity` by **1** for the tapped item's unit type. Use a checkbox in each pick item row to switch between `"pending"` and `"picked"` without removing the item from the list. Picked rows must remain visible with clear status cues (e.g., checkmarks/strikethrough).
### Long Press ### Increment Controls
Increase `quantity` by **1** using a shared `useLongPress()` hook. Provide explicit controls to increase `quantity_units` and `quantity_bulk` (no long-press). Avoid swipe gestures for status changes or deletion on pick item rows.
### Swipe Left
Mark item as `"picked"`.
### Swipe Right
Delete item.
### Barcode Scanning ### Barcode Scanning
@@ -323,6 +313,7 @@ Expose port 8080:
- Product creation must be minimal friction\ - Product creation must be minimal friction\
- Auto-save pick lists\ - Auto-save pick lists\
- Smooth animations on long press - Smooth animations on long press
- Pick list rows use checkboxes to toggle items between pending and picked; no swipe or long-press gestures should be required to update status, and picked rows stay visible with clear status cues
------------------------------------------------------------------------ ------------------------------------------------------------------------
+37 -28
View File
@@ -1,33 +1,26 @@
import { Chip, Stack, Typography } from '@mui/material'; import { Add, Delete } from '@mui/icons-material';
import { PickItem, PickItemStatus } from '../models/PickItem'; import { Button, Checkbox, Stack, Typography } from '@mui/material';
import { PickItem } from '../models/PickItem';
import { Product } from '../models/Product'; import { Product } from '../models/Product';
import { useLongPress } from '../hooks/useLongPress';
import { useSwipe } from '../hooks/useSwipe';
interface PickItemRowProps { interface PickItemRowProps {
item: PickItem; item: PickItem;
product?: Product | null; product?: Product | null;
onIncrement: () => void; onIncrementUnit: () => void;
onSwipeLeft: () => void; onIncrementBulk: () => void;
onSwipeRight: () => void; onToggleStatus: () => void;
onDelete: () => void;
} }
const statusColor: Record<PickItemStatus, 'default' | 'success' | 'warning'> = {
pending: 'default',
picked: 'success',
skipped: 'warning',
};
export const PickItemRow = ({ export const PickItemRow = ({
item, item,
product, product,
onIncrement, onIncrementUnit,
onSwipeLeft, onIncrementBulk,
onSwipeRight, onToggleStatus,
onDelete,
}: PickItemRowProps) => { }: PickItemRowProps) => {
const longPressHandlers = useLongPress({ onLongPress: onIncrement, onClick: onIncrement }); const isPicked = item.status === 'picked';
const swipeHandlers = useSwipe({ onSwipeLeft, onSwipeRight });
const unitLabel = item.is_carton ? product?.bulk_name ?? 'cartons' : product?.unit_type ?? 'units';
return ( return (
<Stack <Stack
@@ -36,16 +29,32 @@ export const PickItemRow = ({
justifyContent="space-between" justifyContent="space-between"
spacing={1} spacing={1}
sx={{ p: 1, borderRadius: 1, bgcolor: 'background.paper', boxShadow: 1 }} sx={{ p: 1, borderRadius: 1, bgcolor: 'background.paper', boxShadow: 1 }}
{...longPressHandlers}
{...swipeHandlers}
> >
<div> <Stack direction="row" alignItems="center" spacing={1} flex={1}>
<Typography variant="subtitle1">{product?.name ?? 'Unknown product'}</Typography> <Checkbox color="success" checked={isPicked} onChange={onToggleStatus} />
<Typography variant="caption" color="text.secondary"> <Stack spacing={0.5} flex={1}>
{item.quantity} {unitLabel} <Typography
</Typography> variant="subtitle1"
</div> sx={{ textDecoration: isPicked ? 'line-through' : 'none', display: 'flex', gap: 0.5, alignItems: 'center' }}
<Chip label={item.status} color={statusColor[item.status]} size="small" /> >
{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">
<Button variant="outlined" size="small" startIcon={<Add />} onClick={onIncrementUnit}>
Unit
</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>
); );
}; };
+10 -6
View File
@@ -28,11 +28,14 @@ export const ActivePickListScreen = () => {
}); });
}; };
const handleSwipeLeft = async (itemId: string) => { const handleToggleStatus = async (itemId: string) => {
await db.pickItems.update(itemId, { status: 'picked', updated_at: Date.now() }); const existing = await db.pickItems.get(itemId);
if (!existing) return;
const nextStatus = existing.status === 'picked' ? 'pending' : 'picked';
await db.pickItems.update(itemId, { status: nextStatus, updated_at: Date.now() });
}; };
const handleSwipeRight = async (itemId: string) => { const handleDeleteItem = async (itemId: string) => {
await db.pickItems.delete(itemId); await db.pickItems.delete(itemId);
}; };
@@ -59,9 +62,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)}
onIncrement={() => handleIncrement(item.id)} onIncrementUnit={() => handleIncrementUnit(item.id)}
onSwipeLeft={() => handleSwipeLeft(item.id)} onIncrementBulk={() => handleIncrementBulk(item.id)}
onSwipeRight={() => handleSwipeRight(item.id)} onToggleStatus={() => handleToggleStatus(item.id)}
onDelete={() => handleDeleteItem(item.id)}
/> />
))} ))}
</Stack> </Stack>