[dyad] Implemented a QR code scanner to filter the stock list - wrote 5 file(s), added html5-qrcode package(s)

This commit is contained in:
davidels1 2025-09-09 00:56:13 +02:00
parent d72b809813
commit f9a6bdc346
5 changed files with 118 additions and 10 deletions

View file

@ -46,6 +46,7 @@
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.3.0",
"html5-qrcode": "^2.3.8",
"input-otp": "^1.2.4",
"lucide-react": "^0.462.0",
"next-themes": "^0.3.0",

View file

@ -113,6 +113,9 @@ importers:
embla-carousel-react:
specifier: ^8.3.0
version: 8.6.0(react@18.3.1)
html5-qrcode:
specifier: ^2.3.8
version: 2.3.8
input-otp:
specifier: ^1.2.4
version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -1829,6 +1832,9 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
html5-qrcode@2.3.8:
resolution: {integrity: sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@ -4052,6 +4058,8 @@ snapshots:
dependencies:
function-bind: 1.1.2
html5-qrcode@2.3.8: {}
ignore@5.3.2: {}
import-fresh@3.3.1:

View file

@ -0,0 +1,74 @@
"use client"
import { useEffect } from "react"
import { Html5QrcodeScanner } from "html5-qrcode"
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog"
interface QRScannerDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
onScanSuccess: (decodedText: string) => void
}
const QRScannerDialog = ({ open, onOpenChange, onScanSuccess }: QRScannerDialogProps) => {
useEffect(() => {
if (!open) return;
const scanner = new Html5QrcodeScanner(
"reader", // The ID of the element to render the scanner in
{
qrbox: {
width: 250,
height: 250,
},
fps: 5,
},
false // verbose
);
const handleSuccess = (decodedText: string) => {
scanner.clear();
onScanSuccess(decodedText);
};
const handleError = (error: any) => {
// Don't log common errors like QR code not found
if (typeof error !== 'string' || !error.includes('No QR code found')) {
console.error("QR Scanner Error:", error);
}
};
scanner.render(handleSuccess, handleError);
// Cleanup function to stop the scanner when the component unmounts or dialog closes
return () => {
if (scanner) {
scanner.clear().catch(error => {
console.error("Failed to clear scanner.", error);
});
}
};
}, [open, onScanSuccess]);
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Scan QR Code</DialogTitle>
<DialogDescription>
Point your camera at a QR code to scan it.
</DialogDescription>
</DialogHeader>
<div id="reader" className="w-full"></div>
</DialogContent>
</Dialog>
);
};
export default QRScannerDialog;

View file

@ -35,11 +35,15 @@ import {
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
filterValue: string
onFilterChange: (value: string) => void
}
export function DataTable<TData, TValue>({
columns,
data,
filterValue,
onFilterChange,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
@ -76,15 +80,18 @@ export function DataTable<TData, TValue>({
},
})
// Sync external filter value with the table's internal state
React.useEffect(() => {
table.getColumn("sku")?.setFilterValue(filterValue)
}, [filterValue, table])
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder="Filter by item name..."
value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("name")?.setFilterValue(event.target.value)
}
placeholder="Filter by SKU..."
value={filterValue}
onChange={(event) => onFilterChange(event.target.value)}
className="max-w-sm"
/>
<DropdownMenu>

View file

@ -12,6 +12,7 @@ import {
DialogTrigger,
} from "@/components/ui/dialog"
import { AddItemForm } from "../components/stock/AddItemForm"
import QRScannerDialog from "../components/stock/QRScannerDialog"
// In a real app, you would fetch this data from an API.
const data: StockItem[] = [
@ -88,21 +89,33 @@ const data: StockItem[] = [
];
const StockList = () => {
const [open, setOpen] = useState(false);
const [addItemOpen, setAddItemOpen] = useState(false);
const [scannerOpen, setScannerOpen] = useState(false);
const [filterValue, setFilterValue] = useState("");
const handleScanSuccess = (decodedText: string) => {
setFilterValue(decodedText);
setScannerOpen(false);
};
return (
<div>
<QRScannerDialog
open={scannerOpen}
onOpenChange={setScannerOpen}
onScanSuccess={handleScanSuccess}
/>
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4 mb-6">
<div>
<h1 className="text-3xl font-bold">Stock List</h1>
<p className="text-gray-500">Manage your inventory here.</p>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline">
<Button variant="outline" onClick={() => setScannerOpen(true)}>
<Camera className="mr-2 h-4 w-4" />
Scan Barcode
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<Dialog open={addItemOpen} onOpenChange={setAddItemOpen}>
<DialogTrigger asChild>
<Button>
<PlusCircle className="mr-2 h-4 w-4" />
@ -116,12 +129,17 @@ const StockList = () => {
Fill in the details below to add a new item to your inventory.
</DialogDescription>
</DialogHeader>
<AddItemForm setOpen={setOpen} />
<AddItemForm setOpen={setAddItemOpen} />
</DialogContent>
</Dialog>
</div>
</div>
<DataTable columns={columns} data={data} />
<DataTable
columns={columns}
data={data}
filterValue={filterValue}
onFilterChange={setFilterValue}
/>
</div>
)
}