Merge remote-tracking branch 'upstream/master' into barcode-generation

This commit is contained in:
wolflu05 2024-07-19 22:45:26 +02:00
commit aebf7ab147
No known key found for this signature in database
GPG Key ID: 9099EFC7C5EB963C
51 changed files with 288 additions and 301 deletions

View File

@ -14,8 +14,10 @@ env:
- INVENTREE_BACKUP_DIR=/opt/inventree/backup - INVENTREE_BACKUP_DIR=/opt/inventree/backup
- INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt - INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
- INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml - INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
- APP_REPO=inventree/InvenTree
before_install: contrib/packager.io/preinstall.sh before_install: contrib/packager.io/preinstall.sh
after_install: contrib/packager.io/postinstall.sh after_install: contrib/packager.io/postinstall.sh
before_remove: contrib/packager.io/preinstall.sh
before: before:
- contrib/packager.io/before.sh - contrib/packager.io/before.sh
dependencies: dependencies:

View File

@ -6,7 +6,6 @@
set -eu set -eu
# The sha is the second element in APP_PKG_ITERATION # The sha is the second element in APP_PKG_ITERATION
REPO="inventree/InvenTree"
VERSION="$APP_PKG_VERSION-$APP_PKG_ITERATION" VERSION="$APP_PKG_VERSION-$APP_PKG_ITERATION"
SHA=$(echo $APP_PKG_ITERATION | cut -d'.' -f2) SHA=$(echo $APP_PKG_ITERATION | cut -d'.' -f2)
@ -15,17 +14,17 @@ echo "INFO collection | Getting info from github for commit $SHA"
curl -L -s -f \ curl -L -s -f \
-H "Accept: application/vnd.github+json" \ -H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \ -H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/$REPO/commits/$SHA > commit.json https://api.github.com/repos/$APP_REPO/commits/$SHA > commit.json
echo "INFO collection | Got commit.json with size $(wc -c commit.json)" echo "INFO collection | Got commit.json with size $(wc -c commit.json)"
curl -L -s -f \ curl -L -s -f \
-H "Accept: application/vnd.github+json" \ -H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \ -H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/$REPO/commits/$SHA/branches-where-head > branches.json https://api.github.com/repos/$APP_REPO/commits/$SHA/branches-where-head > branches.json
echo "INFO collection | Got branches.json with size $(wc -c branches.json)" echo "INFO collection | Got branches.json with size $(wc -c branches.json)"
curl -L -s -f \ curl -L -s -f \
-H "Accept: application/vnd.github+json" \ -H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \ -H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/$REPO/commits/$APP_PKG_VERSION > tag.json https://api.github.com/repos/$APP_REPO/commits/$APP_PKG_VERSION > tag.json
echo "INFO collection | Got tag.json with size $(wc -c tag.json)" echo "INFO collection | Got tag.json with size $(wc -c tag.json)"
# Extract info # Extract info
@ -60,7 +59,7 @@ if [ "$TAG_SHA" != "$FULL_SHA" ]; then
echo "INFO frontend | Tag sha '$TAG_SHA' is not the same as commit sha $FULL_SHA, can not download frontend" echo "INFO frontend | Tag sha '$TAG_SHA' is not the same as commit sha $FULL_SHA, can not download frontend"
else else
echo "INFO frontend | Getting frontend from github via tag" echo "INFO frontend | Getting frontend from github via tag"
curl https://github.com/$REPO/releases/download/$APP_PKG_VERSION/frontend-build.zip -L -O -f curl https://github.com/$APP_REPO/releases/download/$APP_PKG_VERSION/frontend-build.zip -L -O -f
mkdir -p src/backend/InvenTree/web/static mkdir -p src/backend/InvenTree/web/static
echo "INFO frontend | Unzipping frontend" echo "INFO frontend | Unzipping frontend"
unzip -qq frontend-build.zip -d src/backend/InvenTree/web/static/web unzip -qq frontend-build.zip -d src/backend/InvenTree/web/static/web

View File

@ -60,7 +60,7 @@ function detect_python() {
fi fi
# Try to detect a python between 3.9 and 3.12 in reverse order # Try to detect a python between 3.9 and 3.12 in reverse order
if [ -z "${SETUP_PYTHON}" ]; then if [ -z "$(which ${SETUP_PYTHON})" ]; then
echo "# Trying to detecting python3.${PYTHON_FROM} to python3.${PYTHON_TO} - using newest version" echo "# Trying to detecting python3.${PYTHON_FROM} to python3.${PYTHON_TO} - using newest version"
for i in $(seq $PYTHON_TO -1 $PYTHON_FROM); do for i in $(seq $PYTHON_TO -1 $PYTHON_FROM); do
echo "# Checking for python3.${i}" echo "# Checking for python3.${i}"
@ -318,17 +318,17 @@ function set_env() {
sed -i s=debug:\ True=debug:\ False=g ${INVENTREE_CONFIG_FILE} sed -i s=debug:\ True=debug:\ False=g ${INVENTREE_CONFIG_FILE}
# Database engine # Database engine
sed -i s=#ENGINE:\ sampleengine=ENGINE:\ ${INVENTREE_DB_ENGINE}=g ${INVENTREE_CONFIG_FILE} sed -i s=#\ ENGINE:\ Database\ engine.\ Selection\ from:=ENGINE:\ ${INVENTREE_DB_ENGINE}=g ${INVENTREE_CONFIG_FILE}
# Database name # Database name
sed -i s=#NAME:\ \'/path/to/database\'=NAME:\ \'${INVENTREE_DB_NAME}\'=g ${INVENTREE_CONFIG_FILE} sed -i s=#\ NAME:\ Database\ name=NAME:\ \'${INVENTREE_DB_NAME}\'=g ${INVENTREE_CONFIG_FILE}
# Database user # Database user
sed -i s=#USER:\ sampleuser=USER:\ ${INVENTREE_DB_USER}=g ${INVENTREE_CONFIG_FILE} sed -i s=#\ USER:\ Database\ username\ \(if\ required\)=USER:\ ${INVENTREE_DB_USER}=g ${INVENTREE_CONFIG_FILE}
# Database password # Database password
sed -i s=#PASSWORD:\ samplepassword=PASSWORD:\ ${INVENTREE_DB_PASSWORD}=g ${INVENTREE_CONFIG_FILE} sed -i s=#\ PASSWORD:\ Database\ password\ \(if\ required\)=PASSWORD:\ ${INVENTREE_DB_PASSWORD}=g ${INVENTREE_CONFIG_FILE}
# Database host # Database host
sed -i s=#HOST:\ samplehost=HOST:\ ${INVENTREE_DB_HOST}=g ${INVENTREE_CONFIG_FILE} sed -i s=#\ HOST:\ Database\ host\ address\ \(if\ required\)=HOST:\ ${INVENTREE_DB_HOST}=g ${INVENTREE_CONFIG_FILE}
# Database port # Database port
sed -i s=#PORT:\ 123456=PORT:\ ${INVENTREE_DB_PORT}=g ${INVENTREE_CONFIG_FILE} sed -i s=#\ PORT:\ Database\ host\ port\ \(if\ required\)=PORT:\ ${INVENTREE_DB_PORT}=g ${INVENTREE_CONFIG_FILE}
# Fixing the permissions # Fixing the permissions
chown ${APP_USER}:${APP_GROUP} ${DATA_DIR} ${INVENTREE_CONFIG_FILE} chown ${APP_USER}:${APP_GROUP} ${DATA_DIR} ${INVENTREE_CONFIG_FILE}

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# #
# packager.io preinstall script # packager.io preinstall/preremove script
# #
PATH=${APP_HOME}/env/bin:${APP_HOME}/:/sbin:/bin:/usr/sbin:/usr/bin: PATH=${APP_HOME}/env/bin:${APP_HOME}/:/sbin:/bin:/usr/sbin:/usr/bin:

View File

@ -11,6 +11,8 @@
# Note: Database configuration options can also be specified from environmental variables, # Note: Database configuration options can also be specified from environmental variables,
# with the prefix INVENTREE_DB_ # with the prefix INVENTREE_DB_
# e.g INVENTREE_DB_NAME / INVENTREE_DB_USER / INVENTREE_DB_PASSWORD # e.g INVENTREE_DB_NAME / INVENTREE_DB_USER / INVENTREE_DB_PASSWORD
# Do not change this section if you are using the package - use `inventree config` instead
# TO MAINTAINERS: Do not change database strings
database: database:
# --- Available options: --- # --- Available options: ---
# ENGINE: Database engine. Selection from: # ENGINE: Database engine. Selection from:

View File

@ -398,9 +398,9 @@ pyyaml==6.0.1 \
# via # via
# -c src/backend/requirements.txt # -c src/backend/requirements.txt
# pre-commit # pre-commit
setuptools==70.3.0 \ setuptools==71.0.3 \
--hash=sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5 \ --hash=sha256:3d8531791a27056f4a38cd3e54084d8b1c4228ff9cf3f2d7dd075ec99f9fd70d \
--hash=sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc --hash=sha256:f501b6e6db709818dc76882582d9c516bf3b67b948864c5fa1d1624c09a49207
# via # via
# -c src/backend/requirements.txt # -c src/backend/requirements.txt
# -r src/backend/requirements-dev.in # -r src/backend/requirements-dev.in

View File

@ -1543,15 +1543,15 @@ rpds-py==0.18.1 \
# via # via
# jsonschema # jsonschema
# referencing # referencing
sentry-sdk==2.7.0 \ sentry-sdk==2.8.0 \
--hash=sha256:d846a211d4a0378b289ced3c434480945f110d0ede00450ba631fc2852e7a0d4 \ --hash=sha256:6051562d2cfa8087bb8b4b8b79dc44690f8a054762a29c07e22588b1f619bfb5 \
--hash=sha256:db9594c27a4d21c1ebad09908b1f0dc808ef65c2b89c1c8e7e455143262e37c1 --hash=sha256:aa4314f877d9cd9add5a0c9ba18e3f27f99f7de835ce36bd150e48a41c7c646f
# via # via
# -r src/backend/requirements.in # -r src/backend/requirements.in
# django-q-sentry # django-q-sentry
setuptools==70.3.0 \ setuptools==71.0.3 \
--hash=sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5 \ --hash=sha256:3d8531791a27056f4a38cd3e54084d8b1c4228ff9cf3f2d7dd075ec99f9fd70d \
--hash=sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc --hash=sha256:f501b6e6db709818dc76882582d9c516bf3b67b948864c5fa1d1624c09a49207
# via # via
# -r src/backend/requirements.in # -r src/backend/requirements.in
# django-money # django-money

View File

@ -31,7 +31,7 @@ export function DashboardItemProxy({
queryFn: fetchData, queryFn: fetchData,
refetchOnWindowFocus: autoupdate refetchOnWindowFocus: autoupdate
}); });
const [dashdata, setDashData] = useState({ title: t`Title`, value: '000' }); const [dashData, setDashData] = useState({ title: t`Title`, value: '000' });
useEffect(() => { useEffect(() => {
if (data) { if (data) {
@ -44,7 +44,7 @@ export function DashboardItemProxy({
<div key={id}> <div key={id}>
<StatisticItem <StatisticItem
id={id} id={id}
data={dashdata} data={dashData}
isLoading={isLoading || isFetching} isLoading={isLoading || isFetching}
/> />
</div> </div>

View File

@ -1,11 +1,8 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { IconUserStar } from '@tabler/icons-react'; import { IconUserStar } from '@tabler/icons-react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { ModelType } from '../../enums/ModelType'; import { ModelType } from '../../enums/ModelType';
import { navigateToLink } from '../../functions/navigation';
import { base_url } from '../../main';
import { useLocalState } from '../../states/LocalState'; import { useLocalState } from '../../states/LocalState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { ModelInformationDict } from '../render/ModelType'; import { ModelInformationDict } from '../render/ModelType';

View File

@ -2,7 +2,7 @@ import { t } from '@lingui/macro';
import { notifications } from '@mantine/notifications'; import { notifications } from '@mantine/notifications';
import { IconPrinter, IconReport, IconTags } from '@tabler/icons-react'; import { IconPrinter, IconReport, IconTags } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { api } from '../../App'; import { api } from '../../App';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';

View File

@ -85,7 +85,7 @@ function UploadModal({
apiPath: string; apiPath: string;
setImage: (image: string) => void; setImage: (image: string) => void;
}) { }) {
const [file1, setFile] = useState<FileWithPath | null>(null); const [currentFile, setCurrentFile] = useState<FileWithPath | null>(null);
let uploading = false; let uploading = false;
// Components to show in the Dropzone when no file is selected // Components to show in the Dropzone when no file is selected
@ -168,7 +168,7 @@ function UploadModal({
return ( return (
<Paper style={{ height: '220px' }}> <Paper style={{ height: '220px' }}>
<Dropzone <Dropzone
onDrop={(files) => setFile(files[0])} onDrop={(files) => setCurrentFile(files[0])}
maxFiles={1} maxFiles={1}
accept={IMAGE_MIME_TYPE} accept={IMAGE_MIME_TYPE}
loading={uploading} loading={uploading}
@ -198,7 +198,9 @@ function UploadModal({
}} }}
/> />
</Dropzone.Reject> </Dropzone.Reject>
<Dropzone.Idle>{file1 ? fileInfo(file1) : noFileIdle}</Dropzone.Idle> <Dropzone.Idle>
{currentFile ? fileInfo(currentFile) : noFileIdle}
</Dropzone.Idle>
</Group> </Group>
</Dropzone> </Dropzone>
<Paper <Paper
@ -218,12 +220,15 @@ function UploadModal({
> >
<Button <Button
variant="outline" variant="outline"
disabled={!file1} disabled={!currentFile}
onClick={() => setFile(null)} onClick={() => setCurrentFile(null)}
> >
<Trans>Clear</Trans> <Trans>Clear</Trans>
</Button> </Button>
<Button disabled={!file1} onClick={() => uploadImage(file1)}> <Button
disabled={!currentFile}
onClick={() => uploadImage(currentFile)}
>
<Trans>Submit</Trans> <Trans>Submit</Trans>
</Button> </Button>
</Paper> </Paper>
@ -354,7 +359,6 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
}; };
return ( return (
<>
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative"> <AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative">
<> <>
<ApiImage <ApiImage
@ -363,9 +367,7 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
maw={IMAGE_DIMENSION} maw={IMAGE_DIMENSION}
onClick={expandImage} onClick={expandImage}
/> />
{permissions.hasChangeRole(props.appRole) && {permissions.hasChangeRole(props.appRole) && hasOverlay && hovered && (
hasOverlay &&
hovered && (
<Overlay color="black" opacity={0.8} onClick={expandImage}> <Overlay color="black" opacity={0.8} onClick={expandImage}>
<ImageActionButtons <ImageActionButtons
visible={hovered} visible={hovered}
@ -379,6 +381,5 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
)} )}
</> </>
</AspectRatio> </AspectRatio>
</>
); );
} }

View File

@ -1,7 +1,6 @@
import { Trans } from '@lingui/macro'; import { Trans } from '@lingui/macro';
import { import {
ActionIcon, ActionIcon,
Alert,
Button, Button,
Card, Card,
Center, Center,

View File

@ -1,15 +1,7 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import { Alert, FileInput, NumberInput, Stack, Switch } from '@mantine/core';
Alert,
FileInput,
NumberInput,
Stack,
Switch,
TextInput
} from '@mantine/core';
import { UseFormReturnType } from '@mantine/form'; import { UseFormReturnType } from '@mantine/form';
import { useId } from '@mantine/hooks'; import { useId } from '@mantine/hooks';
import { IconX } from '@tabler/icons-react';
import { ReactNode, useCallback, useEffect, useMemo } from 'react'; import { ReactNode, useCallback, useEffect, useMemo } from 'react';
import { Control, FieldValues, useController } from 'react-hook-form'; import { Control, FieldValues, useController } from 'react-hook-form';

View File

@ -1,5 +1,5 @@
import { Trans, t } from '@lingui/macro'; import { Trans, t } from '@lingui/macro';
import { Container, Flex, Group, Table } from '@mantine/core'; import { Container, Group, Table } from '@mantine/core';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { FieldValues, UseControllerReturn } from 'react-hook-form'; import { FieldValues, UseControllerReturn } from 'react-hook-form';

View File

@ -29,7 +29,6 @@ export default function ImporterImportProgress({
}, []); }, []);
return ( return (
<>
<Center> <Center>
<Container> <Container>
<Stack gap="xs"> <Stack gap="xs">
@ -41,6 +40,5 @@ export default function ImporterImportProgress({
</Stack> </Stack>
</Container> </Container>
</Center> </Center>
</>
); );
} }

View File

@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'; import { Trans } from '@lingui/macro';
import { Carousel } from '@mantine/carousel'; import { Carousel } from '@mantine/carousel';
import { Anchor, Button, Paper, Text, Title, rem } from '@mantine/core'; import { Anchor, Button, Paper, Text, Title } from '@mantine/core';
import { DocumentationLinkItem } from './DocumentationLinks'; import { DocumentationLinkItem } from './DocumentationLinks';
import * as classes from './GettingStartedCarousel.css'; import * as classes from './GettingStartedCarousel.css';

View File

@ -35,7 +35,7 @@ export function QrCodeModal({
key: 'camId', key: 'camId',
defaultValue: null defaultValue: null
}); });
const [ScanningEnabled, setIsScanning] = useState<boolean>(false); const [scanningEnabled, setScanningEnabled] = useState<boolean>(false);
const [wasAutoPaused, setWasAutoPaused] = useState<boolean>(false); const [wasAutoPaused, setWasAutoPaused] = useState<boolean>(false);
const documentState = useDocumentVisibility(); const documentState = useDocumentVisibility();
@ -48,7 +48,7 @@ export function QrCodeModal({
// Stop/star when leaving or reentering page // Stop/star when leaving or reentering page
useEffect(() => { useEffect(() => {
if (ScanningEnabled && documentState === 'hidden') { if (scanningEnabled && documentState === 'hidden') {
stopScanning(); stopScanning();
setWasAutoPaused(true); setWasAutoPaused(true);
} else if (wasAutoPaused && documentState === 'visible') { } else if (wasAutoPaused && documentState === 'visible') {
@ -128,12 +128,12 @@ export function QrCodeModal({
icon: <IconX /> icon: <IconX />
}); });
}); });
setIsScanning(true); setScanningEnabled(true);
} }
} }
function stopScanning() { function stopScanning() {
if (qrCodeScanner && ScanningEnabled) { if (qrCodeScanner && scanningEnabled) {
qrCodeScanner.stop().catch((err: string) => { qrCodeScanner.stop().catch((err: string) => {
showNotification({ showNotification({
title: t`Error while stopping`, title: t`Error while stopping`,
@ -142,7 +142,7 @@ export function QrCodeModal({
icon: <IconX /> icon: <IconX />
}); });
}); });
setIsScanning(false); setScanningEnabled(false);
} }
} }
@ -151,7 +151,7 @@ export function QrCodeModal({
<Group> <Group>
<Text size="sm">{camId?.label}</Text> <Text size="sm">{camId?.label}</Text>
<Space style={{ flex: 1 }} /> <Space style={{ flex: 1 }} />
<Badge>{ScanningEnabled ? t`Scanning` : t`Not scanning`}</Badge> <Badge>{scanningEnabled ? t`Scanning` : t`Not scanning`}</Badge>
</Group> </Group>
<Container px={0} id="reader" w={'100%'} mih="300px" /> <Container px={0} id="reader" w={'100%'} mih="300px" />
{!camId ? ( {!camId ? (
@ -164,14 +164,14 @@ export function QrCodeModal({
<Button <Button
style={{ flex: 1 }} style={{ flex: 1 }}
onClick={() => startScanning()} onClick={() => startScanning()}
disabled={camId != undefined && ScanningEnabled} disabled={camId != undefined && scanningEnabled}
> >
<Trans>Start scanning</Trans> <Trans>Start scanning</Trans>
</Button> </Button>
<Button <Button
style={{ flex: 1 }} style={{ flex: 1 }}
onClick={() => stopScanning()} onClick={() => stopScanning()}
disabled={!ScanningEnabled} disabled={!scanningEnabled}
> >
<Trans>Stop scanning</Trans> <Trans>Stop scanning</Trans>
</Button> </Button>

View File

@ -2,7 +2,7 @@ import { ActionIcon, Container, Group, Indicator, Tabs } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
import { IconBell, IconSearch } from '@tabler/icons-react'; import { IconBell, IconSearch } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useEffect, useState } from 'react'; import { ReactNode, useEffect, useMemo, useState } from 'react';
import { useMatch, useNavigate } from 'react-router-dom'; import { useMatch, useNavigate } from 'react-router-dom';
import { api } from '../../App'; import { api } from '../../App';
@ -132,10 +132,35 @@ export function Header() {
} }
function NavTabs() { function NavTabs() {
const user = useUserState();
const navigate = useNavigate(); const navigate = useNavigate();
const match = useMatch(':tabName/*'); const match = useMatch(':tabName/*');
const tabValue = match?.params.tabName; const tabValue = match?.params.tabName;
const tabs: ReactNode[] = useMemo(() => {
let _tabs: ReactNode[] = [];
mainNavTabs.forEach((tab) => {
if (tab.role && !user.hasViewRole(tab.role)) {
return;
}
_tabs.push(
<Tabs.Tab
value={tab.name}
key={tab.name}
onClick={(event: any) =>
navigateToLink(`/${tab.name}`, navigate, event)
}
>
{tab.text}
</Tabs.Tab>
);
});
return _tabs;
}, [mainNavTabs, user]);
return ( return (
<Tabs <Tabs
defaultValue="home" defaultValue="home"
@ -146,19 +171,7 @@ function NavTabs() {
}} }}
value={tabValue} value={tabValue}
> >
<Tabs.List> <Tabs.List>{tabs.map((tab) => tab)}</Tabs.List>
{mainNavTabs.map((tab) => (
<Tabs.Tab
value={tab.name}
key={tab.name}
onClick={(event: any) =>
navigateToLink(`/${tab.name}`, navigate, event)
}
>
{tab.text}
</Tabs.Tab>
))}
</Tabs.List>
</Tabs> </Tabs>
); );
} }

View File

@ -7,7 +7,6 @@ import {
Drawer, Drawer,
Group, Group,
Loader, Loader,
LoadingOverlay,
Space, Space,
Stack, Stack,
Text, Text,

View File

@ -1,6 +1,6 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Alert, Anchor, Group, Skeleton, Space, Text } from '@mantine/core'; import { Alert, Anchor, Group, Skeleton, Space, Text } from '@mantine/core';
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { ReactNode, useCallback } from 'react'; import { ReactNode, useCallback } from 'react';
import { api } from '../../App'; import { api } from '../../App';

View File

@ -15,7 +15,7 @@ export function RenderPart(
const { instance } = props; const { instance } = props;
let badgeText = ''; let badgeText = '';
let badgeColor = 'green'; let badgeColor = '';
let stock = instance.total_in_stock; let stock = instance.total_in_stock;

View File

@ -3,6 +3,7 @@ import { openContextModal } from '@mantine/modals';
import { DocumentationLinkItem } from '../components/items/DocumentationLinks'; import { DocumentationLinkItem } from '../components/items/DocumentationLinks';
import { StylishText } from '../components/items/StylishText'; import { StylishText } from '../components/items/StylishText';
import { UserRoles } from '../enums/Roles';
import { IS_DEV_OR_DEMO } from '../main'; import { IS_DEV_OR_DEMO } from '../main';
export const footerLinks = [ export const footerLinks = [
@ -25,12 +26,17 @@ export const footerLinks = [
export const navTabs = [ export const navTabs = [
{ text: <Trans>Home</Trans>, name: 'home' }, { text: <Trans>Home</Trans>, name: 'home' },
{ text: <Trans>Dashboard</Trans>, name: 'dashboard' }, { text: <Trans>Dashboard</Trans>, name: 'dashboard' },
{ text: <Trans>Parts</Trans>, name: 'part' }, { text: <Trans>Parts</Trans>, name: 'part', role: UserRoles.part },
{ text: <Trans>Stock</Trans>, name: 'stock' }, { text: <Trans>Stock</Trans>, name: 'stock', role: UserRoles.stock },
{ text: <Trans>Build</Trans>, name: 'build' }, { text: <Trans>Build</Trans>, name: 'build', role: UserRoles.build },
{ text: <Trans>Purchasing</Trans>, name: 'purchasing' }, {
{ text: <Trans>Sales</Trans>, name: 'sales' } text: <Trans>Purchasing</Trans>,
name: 'purchasing',
role: UserRoles.purchase_order
},
{ text: <Trans>Sales</Trans>, name: 'sales', role: UserRoles.sales_order }
]; ];
if (IS_DEV_OR_DEMO) { if (IS_DEV_OR_DEMO) {
navTabs.push({ text: <Trans>Playground</Trans>, name: 'playground' }); navTabs.push({ text: <Trans>Playground</Trans>, name: 'playground' });
} }

View File

@ -1,5 +1,5 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { ActionIcon, Alert, Stack, Text } from '@mantine/core'; import { Alert, Stack, Text } from '@mantine/core';
import { import {
IconCalendar, IconCalendar,
IconLink, IconLink,

View File

@ -33,10 +33,7 @@ import {
ApiFormAdjustFilterType, ApiFormAdjustFilterType,
ApiFormFieldSet ApiFormFieldSet
} from '../components/forms/fields/ApiFormField'; } from '../components/forms/fields/ApiFormField';
import { import { TableFieldExtraRow } from '../components/forms/fields/TableField';
TableField,
TableFieldExtraRow
} from '../components/forms/fields/TableField';
import { Thumbnail } from '../components/images/Thumbnail'; import { Thumbnail } from '../components/images/Thumbnail';
import { ProgressBar } from '../components/items/ProgressBar'; import { ProgressBar } from '../components/items/ProgressBar';
import { StylishText } from '../components/items/StylishText'; import { StylishText } from '../components/items/StylishText';

View File

@ -7,13 +7,10 @@ import { Suspense, useCallback, useMemo, useState } from 'react';
import { api } from '../App'; import { api } from '../App';
import { ActionButton } from '../components/buttons/ActionButton'; import { ActionButton } from '../components/buttons/ActionButton';
import { StandaloneField } from '../components/forms/StandaloneField';
import { import {
ApiFormAdjustFilterType, ApiFormAdjustFilterType,
ApiFormField,
ApiFormFieldSet ApiFormFieldSet
} from '../components/forms/fields/ApiFormField'; } from '../components/forms/fields/ApiFormField';
import { ChoiceField } from '../components/forms/fields/ChoiceField';
import { TableFieldExtraRow } from '../components/forms/fields/TableField'; import { TableFieldExtraRow } from '../components/forms/fields/TableField';
import { Thumbnail } from '../components/images/Thumbnail'; import { Thumbnail } from '../components/images/Thumbnail';
import { StylishText } from '../components/items/StylishText'; import { StylishText } from '../components/items/StylishText';

View File

@ -1,8 +1,6 @@
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { api } from '../App';
import { ApiEndpoints } from '../enums/ApiEndpoints'; import { ApiEndpoints } from '../enums/ApiEndpoints';
import { apiUrl } from '../states/ApiState';
import { useInstance } from './UseInstance'; import { useInstance } from './UseInstance';
/* /*

View File

@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useState } from 'react';
import { api } from '../App'; import { api } from '../App';
import { ApiEndpoints } from '../enums/ApiEndpoints'; import { ApiEndpoints } from '../enums/ApiEndpoints';

View File

@ -14,7 +14,6 @@ export default function Logged_In() {
}, [navigate]); }, [navigate]);
return ( return (
<>
<Container> <Container>
<Stack align="center"> <Stack align="center">
<Card shadow="sm" padding="lg" radius="md"> <Card shadow="sm" padding="lg" radius="md">
@ -29,6 +28,5 @@ export default function Logged_In() {
</Card> </Card>
</Stack> </Stack>
</Container> </Container>
</>
); );
} }

View File

@ -14,7 +14,6 @@ export default function Logout() {
}, []); }, []);
return ( return (
<>
<Container> <Container>
<Stack align="center"> <Stack align="center">
<Card shadow="sm" padding="lg" radius="md"> <Card shadow="sm" padding="lg" radius="md">
@ -29,6 +28,5 @@ export default function Logout() {
</Card> </Card>
</Stack> </Stack>
</Container> </Container>
</>
); );
} }

View File

@ -146,7 +146,6 @@ function ApiFormsPlayground() {
function StatusLabelPlayground() { function StatusLabelPlayground() {
const [status, setStatus] = useState<string>('10'); const [status, setStatus] = useState<string>('10');
return ( return (
<>
<Group> <Group>
<Text>Stock Status</Text> <Text>Stock Status</Text>
<TextInput <TextInput
@ -155,7 +154,6 @@ function StatusLabelPlayground() {
/> />
<StatusRenderer type={ModelType.stockitem} status={status} /> <StatusRenderer type={ModelType.stockitem} status={status} />
</Group> </Group>
</>
); );
} }
@ -202,14 +200,12 @@ function PlaygroundArea({
content: ReactNode; content: ReactNode;
}) { }) {
return ( return (
<>
<Accordion.Item value={`accordion-playground-${title}`}> <Accordion.Item value={`accordion-playground-${title}`}>
<Accordion.Control> <Accordion.Control>
<Text>{title}</Text> <Text>{title}</Text>
</Accordion.Control> </Accordion.Control>
<Accordion.Panel>{content}</Accordion.Panel> <Accordion.Panel>{content}</Accordion.Panel>
</Accordion.Item> </Accordion.Item>
</>
); );
} }

View File

@ -41,7 +41,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { Html5Qrcode } from 'html5-qrcode'; import { Html5Qrcode } from 'html5-qrcode';
import { CameraDevice } from 'html5-qrcode/camera/core'; import { CameraDevice } from 'html5-qrcode/camera/core';
import { ReactNode, useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { api } from '../../App'; import { api } from '../../App';
import { DocInfo } from '../../components/items/DocInfo'; import { DocInfo } from '../../components/items/DocInfo';
@ -553,7 +553,7 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
}); });
const [cameras, setCameras] = useState<any[]>([]); const [cameras, setCameras] = useState<any[]>([]);
const [cameraValue, setCameraValue] = useState<string | null>(null); const [cameraValue, setCameraValue] = useState<string | null>(null);
const [ScanningEnabled, setIsScanning] = useState<boolean>(false); const [scanningEnabled, setScanningEnabled] = useState<boolean>(false);
const [wasAutoPaused, setWasAutoPaused] = useState<boolean>(false); const [wasAutoPaused, setWasAutoPaused] = useState<boolean>(false);
const documentState = useDocumentVisibility(); const documentState = useDocumentVisibility();
@ -580,7 +580,7 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
// Stop/start when leaving or reentering page // Stop/start when leaving or reentering page
useEffect(() => { useEffect(() => {
if (ScanningEnabled && documentState === 'hidden') { if (scanningEnabled && documentState === 'hidden') {
btnStopScanning(); btnStopScanning();
setWasAutoPaused(true); setWasAutoPaused(true);
} else if (wasAutoPaused && documentState === 'visible') { } else if (wasAutoPaused && documentState === 'visible') {
@ -642,7 +642,7 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
} }
function btnStartScanning() { function btnStartScanning() {
if (camId && qrCodeScanner && !ScanningEnabled) { if (camId && qrCodeScanner && !scanningEnabled) {
qrCodeScanner qrCodeScanner
.start( .start(
camId.id, camId.id,
@ -662,12 +662,12 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
icon: <IconX /> icon: <IconX />
}); });
}); });
setIsScanning(true); setScanningEnabled(true);
} }
} }
function btnStopScanning() { function btnStopScanning() {
if (qrCodeScanner && ScanningEnabled) { if (qrCodeScanner && scanningEnabled) {
qrCodeScanner.stop().catch((err: string) => { qrCodeScanner.stop().catch((err: string) => {
showNotification({ showNotification({
title: t`Error while stopping`, title: t`Error while stopping`,
@ -676,7 +676,7 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
icon: <IconX /> icon: <IconX />
}); });
}); });
setIsScanning(false); setScanningEnabled(false);
} }
} }
@ -690,7 +690,7 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
const cam = cameras.find((cam) => cam.id === cameraValue); const cam = cameras.find((cam) => cam.id === cameraValue);
// stop scanning if cam changed while scanning // stop scanning if cam changed while scanning
if (qrCodeScanner && ScanningEnabled) { if (qrCodeScanner && scanningEnabled) {
// stop scanning // stop scanning
qrCodeScanner.stop().then(() => { qrCodeScanner.stop().then(() => {
// change ID // change ID
@ -723,7 +723,7 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
})} })}
size="sm" size="sm"
/> />
{ScanningEnabled ? ( {scanningEnabled ? (
<ActionIcon <ActionIcon
onClick={btnStopScanning} onClick={btnStopScanning}
title={t`Stop scanning`} title={t`Stop scanning`}
@ -742,8 +742,8 @@ function InputImageBarcode({ action }: Readonly<ScanInputInterface>) {
</ActionIcon> </ActionIcon>
)} )}
<Space style={{ flex: 1 }} /> <Space style={{ flex: 1 }} />
<Badge color={ScanningEnabled ? 'green' : 'orange'}> <Badge color={scanningEnabled ? 'green' : 'orange'}>
{ScanningEnabled ? t`Scanning` : t`Not scanning`} {scanningEnabled ? t`Scanning` : t`Not scanning`}
</Badge> </Badge>
</Group> </Group>
<Container px={0} id="reader" w={'100%'} mih="300px" /> <Container px={0} id="reader" w={'100%'} mih="300px" />

View File

@ -204,7 +204,7 @@ function EmailContent() {
function SsoContent({ dataProvider }: { dataProvider: any | undefined }) { function SsoContent({ dataProvider }: { dataProvider: any | undefined }) {
const [value, setValue] = useState<string>(''); const [value, setValue] = useState<string>('');
const [currentProviders, setcurrentProviders] = useState<[]>(); const [currentProviders, setCurrentProviders] = useState<[]>();
const { isLoading, data } = useQuery({ const { isLoading, data } = useQuery({
queryKey: ['sso-list'], queryKey: ['sso-list'],
queryFn: () => queryFn: () =>
@ -225,7 +225,7 @@ function SsoContent({ dataProvider }: { dataProvider: any | undefined }) {
// remove providers that are used currently // remove providers that are used currently
let newData = dataProvider.providers; let newData = dataProvider.providers;
newData = newData.filter(isAlreadyInUse); newData = newData.filter(isAlreadyInUse);
setcurrentProviders(newData); setCurrentProviders(newData);
}, [dataProvider, data]); }, [dataProvider, data]);
function removeProvider() { function removeProvider() {

View File

@ -120,7 +120,6 @@ export default function UserSettings() {
} }
return ( return (
<>
<Stack gap="xs"> <Stack gap="xs">
<SettingsHeader <SettingsHeader
title={t`Account Settings`} title={t`Account Settings`}
@ -132,6 +131,5 @@ export default function UserSettings() {
/> />
<PanelGroup pageKey="user-settings" panels={userSettingsPanels} /> <PanelGroup pageKey="user-settings" panels={userSettingsPanels} />
</Stack> </Stack>
</>
); );
} }

View File

@ -147,11 +147,9 @@ export default function NotificationsPage() {
}, [unreadTable, readTable]); }, [unreadTable, readTable]);
return ( return (
<>
<Stack> <Stack>
<PageDetail title={t`Notifications`} /> <PageDetail title={t`Notifications`} />
<PanelGroup pageKey="notifications" panels={notificationPanels} /> <PanelGroup pageKey="notifications" panels={notificationPanels} />
</Stack> </Stack>
</>
); );
} }

View File

@ -1,19 +1,26 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Stack } from '@mantine/core'; import { Stack } from '@mantine/core';
import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { UserRoles } from '../../enums/Roles';
import { useUserState } from '../../states/UserState';
import { BuildOrderTable } from '../../tables/build/BuildOrderTable'; import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
/** /**
* Build Order index page * Build Order index page
*/ */
export default function BuildIndex() { export default function BuildIndex() {
const user = useUserState();
if (!user.isLoggedIn() || !user.hasViewRole(UserRoles.build)) {
return <PermissionDenied />;
}
return ( return (
<>
<Stack> <Stack>
<PageDetail title={t`Build Orders`} actions={[]} /> <PageDetail title={t`Build Orders`} actions={[]} />
<BuildOrderTable /> <BuildOrderTable />
</Stack> </Stack>
</>
); );
} }

View File

@ -39,7 +39,6 @@ import { UserRoles } from '../../enums/Roles';
import { companyFields } from '../../forms/CompanyForms'; import { companyFields } from '../../forms/CompanyForms';
import { useEditApiFormModal } from '../../hooks/UseForm'; import { useEditApiFormModal } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { AddressTable } from '../../tables/company/AddressTable'; import { AddressTable } from '../../tables/company/AddressTable';
import { ContactTable } from '../../tables/company/ContactTable'; import { ContactTable } from '../../tables/company/ContactTable';

View File

@ -31,11 +31,7 @@ export enum panelOptions {
export default function PartPricingPanel({ part }: { part: any }) { export default function PartPricingPanel({ part }: { part: any }) {
const user = useUserState(); const user = useUserState();
const { const { instance: pricing, instanceQuery } = useInstance({
instance: pricing,
refreshInstance,
instanceQuery
} = useInstance({
pk: part?.pk, pk: part?.pk,
hasPrimaryKey: true, hasPrimaryKey: true,
endpoint: ApiEndpoints.part_pricing_get, endpoint: ApiEndpoints.part_pricing_get,

View File

@ -12,11 +12,7 @@ import { ReactNode, useMemo, useState } from 'react';
import { CHART_COLORS } from '../../../components/charts/colors'; import { CHART_COLORS } from '../../../components/charts/colors';
import { tooltipFormatter } from '../../../components/charts/tooltipFormatter'; import { tooltipFormatter } from '../../../components/charts/tooltipFormatter';
import { import { formatDecimal, formatPriceRange } from '../../../defaults/formatters';
formatCurrency,
formatDecimal,
formatPriceRange
} from '../../../defaults/formatters';
import { ApiEndpoints } from '../../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';

View File

@ -7,12 +7,17 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup } from '../../components/nav/PanelGroup'; import { PanelGroup } from '../../components/nav/PanelGroup';
import { UserRoles } from '../../enums/Roles';
import { useUserState } from '../../states/UserState';
import { CompanyTable } from '../../tables/company/CompanyTable'; import { CompanyTable } from '../../tables/company/CompanyTable';
import { PurchaseOrderTable } from '../../tables/purchasing/PurchaseOrderTable'; import { PurchaseOrderTable } from '../../tables/purchasing/PurchaseOrderTable';
export default function PurchasingIndex() { export default function PurchasingIndex() {
const user = useUserState();
const panels = useMemo(() => { const panels = useMemo(() => {
return [ return [
{ {
@ -46,12 +51,14 @@ export default function PurchasingIndex() {
]; ];
}, []); }, []);
if (!user.isLoggedIn() || !user.hasViewRole(UserRoles.purchase_order)) {
return <PermissionDenied />;
}
return ( return (
<>
<Stack> <Stack>
<PageDetail title={t`Purchasing`} /> <PageDetail title={t`Purchasing`} />
<PanelGroup pageKey="purchasing-index" panels={panels} /> <PanelGroup pageKey="purchasing-index" panels={panels} />
</Stack> </Stack>
</>
); );
} }

View File

@ -7,13 +7,18 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup } from '../../components/nav/PanelGroup'; import { PanelGroup } from '../../components/nav/PanelGroup';
import { UserRoles } from '../../enums/Roles';
import { useUserState } from '../../states/UserState';
import { CompanyTable } from '../../tables/company/CompanyTable'; import { CompanyTable } from '../../tables/company/CompanyTable';
import { ReturnOrderTable } from '../../tables/sales/ReturnOrderTable'; import { ReturnOrderTable } from '../../tables/sales/ReturnOrderTable';
import { SalesOrderTable } from '../../tables/sales/SalesOrderTable'; import { SalesOrderTable } from '../../tables/sales/SalesOrderTable';
export default function PurchasingIndex() { export default function PurchasingIndex() {
const user = useUserState();
const panels = useMemo(() => { const panels = useMemo(() => {
return [ return [
{ {
@ -39,12 +44,14 @@ export default function PurchasingIndex() {
]; ];
}, []); }, []);
if (!user.isLoggedIn() || !user.hasViewRole(UserRoles.sales_order)) {
return <PermissionDenied />;
}
return ( return (
<>
<Stack> <Stack>
<PageDetail title={t`Sales`} /> <PageDetail title={t`Sales`} />
<PanelGroup pageKey="sales-index" panels={panels} /> <PanelGroup pageKey="sales-index" panels={panels} />
</Stack> </Stack>
</>
); );
} }

View File

@ -1,5 +1,4 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { ActionIcon, Menu, Tooltip } from '@mantine/core';
import { import {
IconDownload, IconDownload,
IconFileSpreadsheet, IconFileSpreadsheet,
@ -33,12 +32,10 @@ export function DownloadAction({
}, [formatOptions, downloadCallback]); }, [formatOptions, downloadCallback]);
return ( return (
<>
<ActionDropdown <ActionDropdown
tooltip={t`Download Data`} tooltip={t`Download Data`}
icon={<IconDownload />} icon={<IconDownload />}
actions={actions} actions={actions}
/> />
</>
); );
} }

View File

@ -4,9 +4,5 @@ import { IconUpload } from '@tabler/icons-react';
import { ActionButton } from '../components/buttons/ActionButton'; import { ActionButton } from '../components/buttons/ActionButton';
export function UploadAction({}) { export function UploadAction({}) {
return ( return <ActionButton icon={<IconUpload />} tooltip={t`Upload Data`} />;
<>
<ActionButton icon={<IconUpload />} tooltip={t`Upload Data`} />
</>
);
} }

View File

@ -8,7 +8,7 @@ import {
IconUpload, IconUpload,
IconX IconX
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; import { ReactNode, useCallback, useMemo, useState } from 'react';
import { api } from '../../App'; import { api } from '../../App';
import { ActionButton } from '../../components/buttons/ActionButton'; import { ActionButton } from '../../components/buttons/ActionButton';
@ -28,7 +28,7 @@ import { useUserState } from '../../states/UserState';
import { TableColumn } from '../Column'; import { TableColumn } from '../Column';
import { TableFilter } from '../Filter'; import { TableFilter } from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable'; import { InvenTreeTable } from '../InvenTreeTable';
import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions'; import { RowDeleteAction, RowEditAction } from '../RowActions';
/** /**
* Define set of columns to display for the attachment table * Define set of columns to display for the attachment table

View File

@ -128,13 +128,13 @@ export function PartThumbTable({
pk, pk,
setImage setImage
}: ThumbTableProps) { }: ThumbTableProps) {
const [img, selectImage] = useState<string | null>(null); const [thumbImage, setThumbImage] = useState<string | null>(null);
const [filterInput, setFilterInput] = useState<string>(''); const [filterInput, setFilterInput] = useState<string>('');
const [filterQuery, setFilter] = useState<string>(search); const [filterQuery, setFilterQuery] = useState<string>(search);
// Keep search filters from updating while user is typing // Keep search filters from updating while user is typing
useEffect(() => { useEffect(() => {
const timeoutId = setTimeout(() => setFilter(filterInput), 500); const timeoutId = setTimeout(() => setFilterQuery(filterInput), 500);
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
}, [filterInput]); }, [filterInput]);
@ -160,7 +160,6 @@ export function PartThumbTable({
<Suspense> <Suspense>
<Divider /> <Divider />
<Paper p="sm"> <Paper p="sm">
<>
<SimpleGrid cols={8}> <SimpleGrid cols={8}>
{!thumbQuery.isFetching {!thumbQuery.isFetching
? thumbQuery.data?.data.map( ? thumbQuery.data?.data.map(
@ -168,8 +167,8 @@ export function PartThumbTable({
<PartThumbComponent <PartThumbComponent
element={data} element={data}
key={index} key={index}
selected={img} selected={thumbImage}
selectImage={selectImage} selectImage={setThumbImage}
/> />
) )
) )
@ -183,7 +182,6 @@ export function PartThumbTable({
/> />
))} ))}
</SimpleGrid> </SimpleGrid>
</>
</Paper> </Paper>
</Suspense> </Suspense>
@ -197,8 +195,8 @@ export function PartThumbTable({
}} }}
/> />
<Button <Button
disabled={!img} disabled={!thumbImage}
onClick={() => setNewImage(img, pk, setImage)} onClick={() => setNewImage(thumbImage, pk, setImage)}
> >
<Trans>Select</Trans> <Trans>Select</Trans>
</Button> </Button>

View File

@ -1,6 +1,5 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Text } from '@mantine/core'; import { Text } from '@mantine/core';
import { Action } from '@mdxeditor/editor';
import { IconFileArrowLeft, IconSquareArrowRight } from '@tabler/icons-react'; import { IconFileArrowLeft, IconSquareArrowRight } from '@tabler/icons-react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';

View File

@ -25,7 +25,6 @@ import { RowAction, RowDeleteAction } from '../RowActions';
export default function ImportSesssionTable() { export default function ImportSesssionTable() {
const table = useTable('importsession'); const table = useTable('importsession');
const user = useUserState();
const [opened, setOpened] = useState<boolean>(false); const [opened, setOpened] = useState<boolean>(false);

View File

@ -50,7 +50,6 @@ export default function InstalledItemsTable({
}, [user]); }, [user]);
return ( return (
<>
<InvenTreeTable <InvenTreeTable
url={apiUrl(ApiEndpoints.stock_item_list)} url={apiUrl(ApiEndpoints.stock_item_list)}
tableState={table} tableState={table}
@ -64,6 +63,5 @@ export default function InstalledItemsTable({
} }
}} }}
/> />
</>
); );
} }

View File

@ -1,5 +1,5 @@
import { expect } from './baseFixtures.js'; import { expect } from './baseFixtures.js';
import { baseUrl, loginUrl, logoutUrl, user } from './defaults'; import { baseUrl, logoutUrl, user } from './defaults';
/* /*
* Perform form based login operation from the "login" URL * Perform form based login operation from the "login" URL

View File

@ -1,5 +1,5 @@
import { expect, test } from './baseFixtures.js'; import { expect, test } from './baseFixtures.js';
import { baseUrl, loginUrl, user } from './defaults.js'; import { baseUrl, user } from './defaults.js';
import { doLogin, doQuickLogin } from './login.js'; import { doLogin, doQuickLogin } from './login.js';
test('PUI - Basic Login Test', async ({ page }) => { test('PUI - Basic Login Test', async ({ page }) => {

View File

@ -1,6 +1,6 @@
import { test } from './baseFixtures.js'; import { test } from './baseFixtures.js';
import { baseUrl } from './defaults.js'; import { baseUrl } from './defaults.js';
import { doLogout, doQuickLogin } from './login.js'; import { doQuickLogin } from './login.js';
test('PUI - Parts', async ({ page }) => { test('PUI - Parts', async ({ page }) => {
await doQuickLogin(page); await doQuickLogin(page);

View File

@ -1,6 +1,6 @@
import { test } from './baseFixtures.js'; import { test } from './baseFixtures.js';
import { baseUrl } from './defaults.js'; import { baseUrl } from './defaults.js';
import { doLogout, doQuickLogin } from './login.js'; import { doQuickLogin } from './login.js';
test('PUI - Admin', async ({ page }) => { test('PUI - Admin', async ({ page }) => {
// Note here we login with admin access // Note here we login with admin access