From c582ca0afd287d802cef7738e6d830844472a14a Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Sat, 28 Dec 2024 15:12:42 +0100 Subject: [PATCH] Add scan to action menu (#8781) * small style fixes * refactor: split scanning and dialog logic * feat: Add modal and quick action to scan a barcode from anywhere --- .../components/barcodes/BarcodeScanDialog.tsx | 43 ++++++++++--------- .../src/components/modals/QrModal.tsx | 21 +++++++++ src/frontend/src/contexts/ThemeContext.tsx | 4 +- src/frontend/src/defaults/actions.tsx | 17 +++++++- 4 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 src/frontend/src/components/modals/QrModal.tsx diff --git a/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx b/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx index 1bec3d0298..6aaf0ad2ea 100644 --- a/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx +++ b/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx @@ -1,7 +1,7 @@ import { t } from '@lingui/macro'; import { Box, Divider, Modal } from '@mantine/core'; import { useCallback, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { type NavigateFunction, useNavigate } from 'react-router-dom'; import { api } from '../../App'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; import type { ModelType } from '../../enums/ModelType'; @@ -17,17 +17,34 @@ export default function BarcodeScanDialog({ title, opened, onClose -}: { +}: Readonly<{ title?: string; opened: boolean; onClose: () => void; -}) { +}>) { const navigate = useNavigate(); - const user = useUserState(); + return ( + {title ?? t`Scan Barcode`}} + > + + + + + + ); +} +export function ScanInputHandler({ + onClose, + navigate +}: Readonly<{ onClose: () => void; navigate: NavigateFunction }>) { const [error, setError] = useState(''); - const [processing, setProcessing] = useState(false); + const user = useUserState(); const onScan = useCallback((barcode: string) => { if (!barcode || barcode.length === 0) { @@ -80,19 +97,5 @@ export default function BarcodeScanDialog({ }); }, []); - return ( - <> - {title ?? t`Scan Barcode`}} - > - - - - - - - ); + return ; } diff --git a/src/frontend/src/components/modals/QrModal.tsx b/src/frontend/src/components/modals/QrModal.tsx new file mode 100644 index 0000000000..14d5ef2ee7 --- /dev/null +++ b/src/frontend/src/components/modals/QrModal.tsx @@ -0,0 +1,21 @@ +import {} from '@mantine/core'; +import type { ContextModalProps } from '@mantine/modals'; +import type { NavigateFunction } from 'react-router-dom'; +import { ScanInputHandler } from '../barcodes/BarcodeScanDialog'; + +export function QrModal({ + context, + id, + innerProps +}: Readonly< + ContextModalProps<{ modalBody: string; navigate: NavigateFunction }> +>) { + function close() { + context.closeModal(id); + } + function navigate() { + context.closeModal(id); + } + + return ; +} diff --git a/src/frontend/src/contexts/ThemeContext.tsx b/src/frontend/src/contexts/ThemeContext.tsx index 61c326a287..023ca0eb6a 100644 --- a/src/frontend/src/contexts/ThemeContext.tsx +++ b/src/frontend/src/contexts/ThemeContext.tsx @@ -6,6 +6,7 @@ import { ContextMenuProvider } from 'mantine-contextmenu'; import { AboutInvenTreeModal } from '../components/modals/AboutInvenTreeModal'; import { LicenseModal } from '../components/modals/LicenseModal'; +import { QrModal } from '../components/modals/QrModal'; import { ServerInfoModal } from '../components/modals/ServerInfoModal'; import { useLocalState } from '../states/LocalState'; import { LanguageContext } from './LanguageContext'; @@ -47,7 +48,8 @@ export function ThemeContext({ modals={{ info: ServerInfoModal, about: AboutInvenTreeModal, - license: LicenseModal + license: LicenseModal, + qr: QrModal }} > diff --git a/src/frontend/src/defaults/actions.tsx b/src/frontend/src/defaults/actions.tsx index b15d469222..0bda2be938 100644 --- a/src/frontend/src/defaults/actions.tsx +++ b/src/frontend/src/defaults/actions.tsx @@ -1,12 +1,20 @@ import { t } from '@lingui/macro'; import type { SpotlightActionData } from '@mantine/spotlight'; -import { IconLink, IconPointer } from '@tabler/icons-react'; +import { IconBarcode, IconLink, IconPointer } from '@tabler/icons-react'; import type { NavigateFunction } from 'react-router-dom'; +import { openContextModal } from '@mantine/modals'; import { useLocalState } from '../states/LocalState'; import { useUserState } from '../states/UserState'; import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links'; +export function openQrModal(navigate: NavigateFunction) { + return openContextModal({ + modal: 'qr', + innerProps: { navigate: navigate } + }); +} + export function getActions(navigate: NavigateFunction) { const setNavigationOpen = useLocalState((state) => state.setNavigationOpen); const { user } = useUserState(); @@ -55,6 +63,13 @@ export function getActions(navigate: NavigateFunction) { description: t`Open the main navigation menu`, onClick: () => setNavigationOpen(true), leftSection: + }, + { + id: 'scan', + label: t`Scan`, + description: t`Scan a barcode or QR code`, + onClick: () => openQrModal(navigate), + leftSection: } ];