Merge pull request #23 from beatz174-bit/codex/fix-search-feature-on-pick-lists
Improve pick list product search UX
This commit is contained in:
@@ -55,9 +55,8 @@ describe('AddItemScreen product search', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await user.type(screen.getByPlaceholderText(/search products/i), 'cola');
|
await user.type(screen.getByPlaceholderText(/search products/i), 'cola');
|
||||||
await user.click(screen.getByLabelText(/product/i));
|
|
||||||
|
|
||||||
expect(screen.getByRole('option', { name: /cola \(drinks\)/i })).toBeVisible();
|
expect(await screen.findByRole('option', { name: /cola \(drinks\)/i })).toBeVisible();
|
||||||
expect(screen.queryByRole('option', { name: /chips \(snacks\)/i })).not.toBeInTheDocument();
|
expect(screen.queryByRole('option', { name: /chips \(snacks\)/i })).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
|
Autocomplete,
|
||||||
Button,
|
Button,
|
||||||
Container,
|
Container,
|
||||||
MenuItem,
|
|
||||||
Stack,
|
Stack,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
@@ -20,7 +20,7 @@ export const AddItemScreen = () => {
|
|||||||
const db = useDatabase();
|
const db = useDatabase();
|
||||||
const items = usePickItems(id);
|
const items = usePickItems(id);
|
||||||
const products = useProducts();
|
const products = useProducts();
|
||||||
const [productId, setProductId] = useState('');
|
const [selectedProduct, setSelectedProduct] = useState<typeof products[number] | null>(null);
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [units, setUnits] = useState(1);
|
const [units, setUnits] = useState(1);
|
||||||
const [bulk, setBulk] = useState(0);
|
const [bulk, setBulk] = useState(0);
|
||||||
@@ -47,13 +47,15 @@ export const AddItemScreen = () => {
|
|||||||
}, [availableProducts, query]);
|
}, [availableProducts, query]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!productId) return;
|
if (!selectedProduct) return;
|
||||||
if (!filteredProducts.some((product) => product.id === productId)) {
|
if (!filteredProducts.some((product) => product.id === selectedProduct.id)) {
|
||||||
setProductId('');
|
setSelectedProduct(null);
|
||||||
}
|
}
|
||||||
}, [filteredProducts, productId]);
|
}, [filteredProducts, selectedProduct]);
|
||||||
|
|
||||||
const addItem = async () => {
|
const addItem = async () => {
|
||||||
|
const productId = selectedProduct?.id;
|
||||||
|
|
||||||
if (!id || !productId || existingProductIds.has(productId)) return;
|
if (!id || !productId || existingProductIds.has(productId)) return;
|
||||||
await db.pickItems.add({
|
await db.pickItems.add({
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
@@ -74,28 +76,46 @@ export const AddItemScreen = () => {
|
|||||||
Add Item
|
Add Item
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<TextField
|
<Autocomplete
|
||||||
placeholder="Search products"
|
options={filteredProducts}
|
||||||
value={query}
|
getOptionLabel={(option) => `${option.name} (${option.category})`}
|
||||||
onChange={(event) => setQuery(event.target.value)}
|
isOptionEqualToValue={(option, value) => option.id === value.id}
|
||||||
InputProps={{ startAdornment: <InputAdornment position="start">{<SearchIcon />}</InputAdornment> }}
|
value={selectedProduct}
|
||||||
/>
|
onChange={(_, value) => setSelectedProduct(value)}
|
||||||
<TextField
|
inputValue={query}
|
||||||
select
|
onInputChange={(_, value, reason) => {
|
||||||
label="Product"
|
if (reason === 'input') {
|
||||||
|
setQuery(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason === 'clear') {
|
||||||
|
setQuery('');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
filterOptions={(options) => options}
|
||||||
|
noOptionsText={query.trim() ? 'No matching products' : 'No products available'}
|
||||||
fullWidth
|
fullWidth
|
||||||
value={productId}
|
renderInput={(params) => (
|
||||||
onChange={(event) => setProductId(event.target.value)}
|
<TextField
|
||||||
>
|
{...params}
|
||||||
{filteredProducts.map((product) => (
|
placeholder="Search products"
|
||||||
<MenuItem key={product.id} value={product.id}>
|
InputProps={{
|
||||||
{product.name} ({product.category})
|
...params.InputProps,
|
||||||
</MenuItem>
|
startAdornment: (
|
||||||
))}
|
<>
|
||||||
</TextField>
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
{params.InputProps.startAdornment}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<NumericStepper label="Units" value={units} onChange={setUnits} />
|
<NumericStepper label="Units" value={units} onChange={setUnits} />
|
||||||
<NumericStepper label="Bulk" value={bulk} onChange={setBulk} />
|
<NumericStepper label="Bulk" value={bulk} onChange={setBulk} />
|
||||||
<Button variant="contained" disabled={!productId} onClick={addItem}>
|
<Button variant="contained" disabled={!selectedProduct} onClick={addItem}>
|
||||||
Add to List
|
Add to List
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
Reference in New Issue
Block a user