Merge pull request #12 from beatz174-bit/codex/update-barcode-scanner-page-features
Enhance barcode scanner lookup
This commit is contained in:
@@ -1,9 +1,58 @@
|
|||||||
import { Container, Typography } from '@mui/material';
|
import { Alert, Card, CardContent, Chip, Container, Divider, Stack, Typography } from '@mui/material';
|
||||||
import { useState } from 'react';
|
import { liveQuery } from 'dexie';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { BarcodeScannerView } from '../components/BarcodeScannerView';
|
import { BarcodeScannerView } from '../components/BarcodeScannerView';
|
||||||
|
import { useDatabase } from '../context/DBProvider';
|
||||||
|
import { Area } from '../models/Area';
|
||||||
|
import { PickList } from '../models/PickList';
|
||||||
|
import { Product } from '../models/Product';
|
||||||
|
|
||||||
|
interface ProductLookup {
|
||||||
|
product?: Product | null;
|
||||||
|
pickLists: { list: PickList; area?: Area }[];
|
||||||
|
}
|
||||||
|
|
||||||
export const BarcodeScannerScreen = () => {
|
export const BarcodeScannerScreen = () => {
|
||||||
|
const db = useDatabase();
|
||||||
const [lastCode, setLastCode] = useState('');
|
const [lastCode, setLastCode] = useState('');
|
||||||
|
const [lookup, setLookup] = useState<ProductLookup>({ pickLists: [] });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!lastCode) {
|
||||||
|
setLookup({ pickLists: [] });
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscription = liveQuery(async () => {
|
||||||
|
const product = await db.products.where('barcode').equals(lastCode).first();
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
return { product: null, pickLists: [] } satisfies ProductLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pickItems = await db.pickItems.where('product_id').equals(product.id).toArray();
|
||||||
|
const pickListIds = Array.from(new Set(pickItems.map((item) => item.pick_list_id)));
|
||||||
|
const pickLists = (await db.pickLists.bulkGet(pickListIds)).filter(
|
||||||
|
(list): list is PickList => Boolean(list),
|
||||||
|
);
|
||||||
|
const areas = await db.areas.bulkGet(pickLists.map((list) => list.area_id));
|
||||||
|
const areaMap = new Map<string, Area>();
|
||||||
|
areas.forEach((area) => {
|
||||||
|
if (area) areaMap.set(area.id, area);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
product,
|
||||||
|
pickLists: pickLists.map((list) => ({ list, area: areaMap.get(list.area_id) })),
|
||||||
|
} satisfies ProductLookup;
|
||||||
|
}).subscribe({
|
||||||
|
next: setLookup,
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => subscription.unsubscribe();
|
||||||
|
}, [db, lastCode]);
|
||||||
|
|
||||||
|
const hasProduct = useMemo(() => lookup.product && lookup.product !== null, [lookup.product]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ py: 4 }}>
|
<Container sx={{ py: 4 }}>
|
||||||
@@ -11,11 +60,75 @@ export const BarcodeScannerScreen = () => {
|
|||||||
Barcode Scanner
|
Barcode Scanner
|
||||||
</Typography>
|
</Typography>
|
||||||
<BarcodeScannerView onDetected={(code) => setLastCode(code)} />
|
<BarcodeScannerView onDetected={(code) => setLastCode(code)} />
|
||||||
{lastCode ? (
|
<Stack spacing={2} sx={{ mt: 2 }}>
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
{lastCode ? (
|
||||||
Last detected: {lastCode}
|
<Card variant="outlined">
|
||||||
</Typography>
|
<CardContent>
|
||||||
) : null}
|
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={1}>
|
||||||
|
<Typography variant="subtitle1">Last detected</Typography>
|
||||||
|
<Chip
|
||||||
|
color={hasProduct ? 'success' : 'default'}
|
||||||
|
label={hasProduct ? 'Product found' : 'Not linked to a product'}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Typography variant="h6" sx={{ mt: 1 }}>
|
||||||
|
{lastCode}
|
||||||
|
</Typography>
|
||||||
|
{lookup.product === null ? (
|
||||||
|
<Alert severity="warning" sx={{ mt: 2 }}>
|
||||||
|
This barcode is not present in the product list.
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{lookup.product ? (
|
||||||
|
<Card variant="outlined">
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Product
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h6">{lookup.product.name}</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Category: {lookup.product.category}
|
||||||
|
</Typography>
|
||||||
|
{lookup.product.barcode ? (
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Barcode: {lookup.product.barcode}
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{hasProduct ? (
|
||||||
|
<Card variant="outlined">
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Pick lists containing this product
|
||||||
|
</Typography>
|
||||||
|
{lookup.pickLists.length === 0 ? (
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
This product is not in any pick lists yet.
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Stack divider={<Divider flexItem />} spacing={1}>
|
||||||
|
{lookup.pickLists.map(({ list, area }) => (
|
||||||
|
<Stack key={list.id} spacing={0.25}>
|
||||||
|
<Typography variant="body1">Pick List #{list.id.slice(0, 8)}</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Area: {area?.name ?? 'Unknown area'}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user