Merge pull request #13 from beatz174-bit/codex/move-camera-frame-to-popup-on-scan
Move barcode scanning to modal and allow adding missing products
This commit is contained in:
@@ -1,6 +1,20 @@
|
|||||||
import { Alert, Card, CardContent, Chip, Container, Divider, Stack, Typography } from '@mui/material';
|
import {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
Chip,
|
||||||
|
Container,
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Divider,
|
||||||
|
Stack,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
import { liveQuery } from 'dexie';
|
import { liveQuery } from 'dexie';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { BarcodeScannerView } from '../components/BarcodeScannerView';
|
import { BarcodeScannerView } from '../components/BarcodeScannerView';
|
||||||
import { useDatabase } from '../context/DBProvider';
|
import { useDatabase } from '../context/DBProvider';
|
||||||
import { Area } from '../models/Area';
|
import { Area } from '../models/Area';
|
||||||
@@ -14,8 +28,10 @@ interface ProductLookup {
|
|||||||
|
|
||||||
export const BarcodeScannerScreen = () => {
|
export const BarcodeScannerScreen = () => {
|
||||||
const db = useDatabase();
|
const db = useDatabase();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [lastCode, setLastCode] = useState('');
|
const [lastCode, setLastCode] = useState('');
|
||||||
const [lookup, setLookup] = useState<ProductLookup>({ pickLists: [] });
|
const [lookup, setLookup] = useState<ProductLookup>({ pickLists: [] });
|
||||||
|
const [scannerOpen, setScannerOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!lastCode) {
|
if (!lastCode) {
|
||||||
@@ -54,13 +70,45 @@ export const BarcodeScannerScreen = () => {
|
|||||||
|
|
||||||
const hasProduct = useMemo(() => lookup.product && lookup.product !== null, [lookup.product]);
|
const hasProduct = useMemo(() => lookup.product && lookup.product !== null, [lookup.product]);
|
||||||
|
|
||||||
|
const handleDetected = (code: string) => {
|
||||||
|
setLastCode(code);
|
||||||
|
setScannerOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddProduct = () => {
|
||||||
|
if (!lastCode) return;
|
||||||
|
navigate('/products', { state: { newBarcode: lastCode } });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ py: 4 }}>
|
<Container sx={{ py: 4 }}>
|
||||||
<Typography variant="h5" gutterBottom>
|
<Typography variant="h5" gutterBottom>
|
||||||
Barcode Scanner
|
Barcode Scanner
|
||||||
</Typography>
|
</Typography>
|
||||||
<BarcodeScannerView onDetected={(code) => setLastCode(code)} />
|
|
||||||
<Stack spacing={2} sx={{ mt: 2 }}>
|
<Stack spacing={2} sx={{ mt: 2 }}>
|
||||||
|
<Card variant="outlined">
|
||||||
|
<CardContent>
|
||||||
|
<Stack spacing={1.5}>
|
||||||
|
<Stack spacing={0.25}>
|
||||||
|
<Typography variant="subtitle1">Scan barcodes</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Open the camera in a popup to keep scanned details easy to read.
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" spacing={1} alignItems="center">
|
||||||
|
<Button variant="contained" onClick={() => setScannerOpen(true)}>
|
||||||
|
Scan barcode
|
||||||
|
</Button>
|
||||||
|
{lastCode ? (
|
||||||
|
<Button variant="outlined" onClick={() => setScannerOpen(true)}>
|
||||||
|
Scan another
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{lastCode ? (
|
{lastCode ? (
|
||||||
<Card variant="outlined">
|
<Card variant="outlined">
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -77,7 +125,14 @@ export const BarcodeScannerScreen = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
{lookup.product === null ? (
|
{lookup.product === null ? (
|
||||||
<Alert severity="warning" sx={{ mt: 2 }}>
|
<Alert severity="warning" sx={{ mt: 2 }}>
|
||||||
This barcode is not present in the product list.
|
<Stack spacing={1}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
This barcode is not present in the product list.
|
||||||
|
</Typography>
|
||||||
|
<Button variant="outlined" size="small" onClick={handleAddProduct}>
|
||||||
|
Add barcode to products
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -129,6 +184,12 @@ export const BarcodeScannerScreen = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Dialog open={scannerOpen} onClose={() => setScannerOpen(false)} fullWidth>
|
||||||
|
<DialogTitle>Scan a barcode</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<BarcodeScannerView onDetected={handleDetected} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink, useLocation } from 'react-router-dom';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { ProductRow } from '../components/ProductRow';
|
import { ProductRow } from '../components/ProductRow';
|
||||||
@@ -24,6 +24,7 @@ export const ManageProductsScreen = () => {
|
|||||||
const db = useDatabase();
|
const db = useDatabase();
|
||||||
const products = useProducts();
|
const products = useProducts();
|
||||||
const categories = useCategories();
|
const categories = useCategories();
|
||||||
|
const location = useLocation();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [category, setCategory] = useState('');
|
const [category, setCategory] = useState('');
|
||||||
@@ -51,6 +52,13 @@ export const ManageProductsScreen = () => {
|
|||||||
[products, search],
|
[products, search],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const state = location.state as { newBarcode?: string } | null;
|
||||||
|
if (state?.newBarcode) {
|
||||||
|
setBarcode(state.newBarcode);
|
||||||
|
}
|
||||||
|
}, [location.state]);
|
||||||
|
|
||||||
const addProduct = async () => {
|
const addProduct = async () => {
|
||||||
if (!name || !category) return;
|
if (!name || !category) return;
|
||||||
await db.products.add({
|
await db.products.add({
|
||||||
|
|||||||
Reference in New Issue
Block a user