mirror of
https://github.com/inventree/InvenTree.git
synced 2026-05-09 03:03:41 +00:00
d1354fc147
* Move useFilterSet state to the @lib
* Refactor useTable hook into @lib
* Refactor string helper functions
* Refactor constructFormUrl func
* Refactor Boundary component
* Refactor StoredTableState
* More refactoring
* Refactor CopyButton and CopyableCell
* Pass table render func to plugins
* Provide internal wrapper function, while allowing the "api" and "navigate" functions to be provided by the caller
* Adds <InvenTreeTable /> component which is exposed to plugins
* Update frontend versioning
* Update docs
* Handle condition where UI does not provide table rendering function
* Move queryFilters out of custom state
* Fix exported type
* Extract searchParams
- Cannot be used outside of router component
- Only provide when the table is generated internally
* Bump UI version
* Fix for right-click context menu
- Function needs to be defined with the context menu provider
(cherry picked from commit 23f43ffd33)
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
212 lines
5.1 KiB
TypeScript
212 lines
5.1 KiB
TypeScript
import { t } from '@lingui/core/macro';
|
|
import { Trans } from '@lingui/react/macro';
|
|
import {
|
|
Alert,
|
|
Box,
|
|
Button,
|
|
Code,
|
|
Divider,
|
|
Group,
|
|
Image,
|
|
Select,
|
|
Skeleton,
|
|
Stack,
|
|
Text
|
|
} from '@mantine/core';
|
|
import { modals } from '@mantine/modals';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import QR from 'qrcode';
|
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
|
import { CopyButton } from '@lib/components/CopyButton';
|
|
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
|
import { apiUrl } from '@lib/functions/Api';
|
|
import { api } from '../../App';
|
|
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
|
import type { QrCodeType } from '../items/ActionDropdown';
|
|
|
|
import { extractErrorMessage } from '../../functions/api';
|
|
import { BarcodeInput } from './BarcodeInput';
|
|
|
|
type QRCodeProps = {
|
|
ecl?: 'L' | 'M' | 'Q' | 'H';
|
|
margin?: number;
|
|
data?: string;
|
|
};
|
|
|
|
export const QRCode = ({ data, ecl = 'Q', margin = 1 }: QRCodeProps) => {
|
|
const [qrCode, setQRCode] = useState<string>();
|
|
|
|
useEffect(() => {
|
|
if (!data) return setQRCode(undefined);
|
|
|
|
QR.toString(data, { errorCorrectionLevel: ecl, type: 'svg', margin }).then(
|
|
(svg) => {
|
|
setQRCode(`data:image/svg+xml;utf8,${encodeURIComponent(svg)}`);
|
|
}
|
|
);
|
|
}, [data, ecl]);
|
|
|
|
return (
|
|
<Box>
|
|
{qrCode ? (
|
|
<Image src={qrCode} alt='QR Code' />
|
|
) : (
|
|
<Skeleton height={500} />
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
type InvenTreeQRCodeProps = {
|
|
mdl_prop: QrCodeType;
|
|
showEclSelector?: boolean;
|
|
} & Omit<QRCodeProps, 'data'>;
|
|
|
|
export const InvenTreeQRCode = ({
|
|
mdl_prop,
|
|
showEclSelector = true,
|
|
ecl: eclProp = 'Q',
|
|
...props
|
|
}: InvenTreeQRCodeProps) => {
|
|
const settings = useGlobalSettingsState();
|
|
const [ecl, setEcl] = useState(eclProp);
|
|
|
|
useEffect(() => {
|
|
if (eclProp) setEcl(eclProp);
|
|
}, [eclProp]);
|
|
|
|
const { data } = useQuery({
|
|
queryKey: ['qr-code', mdl_prop.model, mdl_prop.pk],
|
|
queryFn: async () => {
|
|
return api
|
|
.post(apiUrl(ApiEndpoints.barcode_generate), {
|
|
model: mdl_prop.model,
|
|
pk: mdl_prop.pk
|
|
})
|
|
.then((res) => res.data?.barcode ?? ('' as string))
|
|
.catch((error) => '');
|
|
}
|
|
});
|
|
|
|
const eclOptions = useMemo(
|
|
() => [
|
|
{ value: 'L', label: t`Low (7%)` },
|
|
{ value: 'M', label: t`Medium (15%)` },
|
|
{ value: 'Q', label: t`Quartile (25%)` },
|
|
{ value: 'H', label: t`High (30%)` }
|
|
],
|
|
[]
|
|
);
|
|
|
|
return (
|
|
<Stack>
|
|
<Divider />
|
|
|
|
{mdl_prop.hash ? (
|
|
<Alert variant='outline' color='red' title={t`Custom barcode`}>
|
|
<Trans>
|
|
A custom barcode is registered for this item. The shown code is not
|
|
that custom barcode.
|
|
</Trans>
|
|
</Alert>
|
|
) : null}
|
|
|
|
<QRCode data={data} ecl={ecl} {...props} />
|
|
|
|
<Divider />
|
|
|
|
{data && settings.getSetting('BARCODE_SHOW_TEXT', 'false') && (
|
|
<Group
|
|
justify={showEclSelector ? 'space-between' : 'center'}
|
|
align='flex-start'
|
|
px={16}
|
|
>
|
|
<Stack gap={4} pt={2}>
|
|
<Text size='sm' fw={500}>
|
|
<Trans>Barcode Data:</Trans>
|
|
</Text>
|
|
<Group>
|
|
<Code>{data}</Code>
|
|
<CopyButton value={data} />
|
|
</Group>
|
|
</Stack>
|
|
|
|
{showEclSelector && (
|
|
<Select
|
|
allowDeselect={false}
|
|
label={t`Select Error Correction Level`}
|
|
value={ecl}
|
|
onChange={(v) =>
|
|
setEcl(v as Exclude<QRCodeProps['ecl'], undefined>)
|
|
}
|
|
data={eclOptions}
|
|
/>
|
|
)}
|
|
</Group>
|
|
)}
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export const QRCodeLink = ({ mdl_prop }: { mdl_prop: QrCodeType }) => {
|
|
const [error, setError] = useState<string>('');
|
|
|
|
const linkBarcode = useCallback((barcode: string) => {
|
|
api
|
|
.post(apiUrl(ApiEndpoints.barcode_link), {
|
|
[mdl_prop.model]: mdl_prop.pk,
|
|
barcode: barcode
|
|
})
|
|
.then((response) => {
|
|
setError('');
|
|
modals.closeAll();
|
|
location.reload();
|
|
})
|
|
.catch((error) => {
|
|
const msg = extractErrorMessage({
|
|
error: error,
|
|
field: 'error',
|
|
defaultMessage: t`Failed to link barcode`
|
|
});
|
|
setError(msg);
|
|
});
|
|
}, []);
|
|
|
|
return (
|
|
<Stack gap='xs'>
|
|
<Divider />
|
|
<BarcodeInput onScan={linkBarcode} actionText={t`Link`} error={error} />
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export const QRCodeUnlink = ({ mdl_prop }: { mdl_prop: QrCodeType }) => {
|
|
function unlinkBarcode() {
|
|
api
|
|
.post(apiUrl(ApiEndpoints.barcode_unlink), {
|
|
[mdl_prop.model]: mdl_prop.pk
|
|
})
|
|
.then((response) => {
|
|
modals.closeAll();
|
|
location.reload();
|
|
});
|
|
}
|
|
return (
|
|
<Box>
|
|
<Stack gap='sm'>
|
|
<Divider />
|
|
<Text>
|
|
<Trans>This will remove the link to the associated barcode</Trans>
|
|
</Text>
|
|
<Divider />
|
|
<Group grow>
|
|
<Button color='red' onClick={unlinkBarcode}>
|
|
<Trans>Unlink Barcode</Trans>
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
};
|