import Add from "@mui/icons-material/Add"; import DeleteForeverOutlined from "@mui/icons-material/DeleteForeverOutlined"; import Edit from "@mui/icons-material/Edit"; import QrCodeScanner from "@mui/icons-material/QrCodeScanner"; import TapAndPlay from "@mui/icons-material/TapAndPlay"; import { Alert, Box, Button, DialogActions, List, ListItem, ListItemText, Tab, Tabs } from "@mui/material"; import { getLocalizedString, localize } from "@olenbetong/appframe-core"; import { ResponsiveDialog } from "@olenbetong/appframe-mui"; import { useEffect, useState } from "react"; import { flushSync } from "react-dom"; import { BarcodeScanner } from "./BarcodeScanner"; import { type AddElementSource, type ElementDetails, getElement } from "./getElement"; import { AddElementHIDScanner } from "./HIDScanner"; import { AddElementManualForm } from "./Manual/ManualForm"; import { useBeeper } from "./utils/beep"; import Giai from "./utils/Giai"; export type AddElement = { element: ElementDetails; mode: AddElementSource; scanTime?: number; }; type InitialTabProps = "camera" | "scanner" | "manual"; export type ElementLookupDialogSharedProps = { open: boolean; onClose: () => void; initialTab?: InitialTabProps | null; useSearch?: boolean; allowEmptyElement?: boolean; }; export type ElementLookupDialogSingleModeProps = ElementLookupDialogSharedProps & { mode: "single" | "search"; onAdd: (element: AddElement) => Promise; }; export type ElementLookupDialogMultiModeProps = ElementLookupDialogSharedProps & { mode: "multi"; onAdd: (element: AddElement[]) => Promise; }; export type ElementLookupDialogProps = ElementLookupDialogSingleModeProps | ElementLookupDialogMultiModeProps; export function ElementLookupDialog(props: ElementLookupDialogProps) { let { open, onClose, initialTab } = props; let [tab, setTab] = useState<"camera" | "scanner" | "manual">("camera"); let [elements, setElements] = useState>([]); let [adding, setAdding] = useState(false); let beeper = useBeeper(); useEffect(() => { if (open && initialTab) { setTab(initialTab); } }, [open, initialTab]); async function handleElementAdd(element: string | ElementDetails, mode: AddElementSource) { try { let record = typeof element === "string" ? await getElement(element.replace("\u001d", ""), { mode }) : element; // Flush changes to DOM to be able to query newly added elements and animate their background flushSync(() => { setElements((current) => { let now = Date.now(); let exists = current.some((e) => e.element.ElementID === record.ElementID); let updated: Array; if (exists) { updated = current.map((e) => (e.element.ElementID === record.ElementID ? { ...e, scanTime: now } : e)); } else { beeper(); updated = props.mode === "multi" ? [{ element: record, mode, scanTime: now }, ...current] : [{ element: record, mode, scanTime: now }]; } if (props.mode === "multi") { updated.sort((a, b) => (b.scanTime ?? 0) - (a.scanTime ?? 0)); } return updated; }); }); let addedDomElement = document.querySelector(`[data-elementid="${record.ElementID}"]`); if (addedDomElement) { addedDomElement.animate( [ { backgroundColor: "var(--brand-light)", offset: 0 }, { backgroundColor: "var(--brand-light)", offset: 0.2 }, { backgroundColor: "transparent" }, ], { duration: 1000, easing: "ease-out", }, ); } } catch (error) { alert((error as Error).message); } } if (tab === "manual" && props.mode === "search") { tab = "camera"; } return ( setTab(newTab)} variant="fullWidth" sx={{ flexShrink: 0 }}> {props.mode !== "search" && } label={getLocalizedString("Manual")} value="manual" />} } label={getLocalizedString("Scanner")} value="scanner" /> } label={getLocalizedString("Camera")} value="camera" /> {tab === "camera" && ( handleElementAdd(element, "camera-barcode")} /> )} {tab === "manual" && ( handleElementAdd(element, "manual")} allowEmptyElement={props.allowEmptyElement} /> )} {tab === "scanner" && ( handleElementAdd(element, element.startsWith(Giai.BARCODE_PREFIX) ? "hid-barcode" : "hid-rfid") } /> )} {props.mode === "multi" && ( {localize`${elements.length} elements selected`} )} {elements.map(({ element }) => ( { setElements((current) => props.mode === "multi" ? current.filter((e) => e.element.ElementID !== element.ElementID) : [], ); }} > } > ))} ); }