diff --git a/pyproject.toml b/pyproject.toml
index 69176dbccf..04f341b519 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -58,6 +58,7 @@ ignore = [
"N812",
# - RUF032 - decimal-from-float-literal
"RUF032",
+ # - RUF045 - implicit-class-var-in-dataclass
"RUF045",
# - UP045 - Use `X | None` instead of `Optional[X]`
"UP045",
diff --git a/src/backend/InvenTree/web/templates/web/index.html b/src/backend/InvenTree/web/templates/web/index.html
index 300368d9b4..879f7daf2a 100644
--- a/src/backend/InvenTree/web/templates/web/index.html
+++ b/src/backend/InvenTree/web/templates/web/index.html
@@ -9,6 +9,7 @@
{% inventree_instance_name %}
+ {% include "favicon.html" %}
diff --git a/src/frontend/index.html b/src/frontend/index.html
index 3e7fd76816..36deaa0c1a 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -5,6 +5,7 @@
InvenTree
+
diff --git a/src/frontend/lib/types/Forms.tsx b/src/frontend/lib/types/Forms.tsx
index 9425f43774..8e3a574f01 100644
--- a/src/frontend/lib/types/Forms.tsx
+++ b/src/frontend/lib/types/Forms.tsx
@@ -1,6 +1,6 @@
import type { DefaultMantineColor, MantineStyleProp } from '@mantine/core';
import type { UseFormReturnType } from '@mantine/form';
-import type { ReactNode } from 'react';
+import type { JSX, ReactNode } from 'react';
import type { FieldValues, UseFormReturn } from 'react-hook-form';
import type { ApiEndpoints } from '../enums/ApiEndpoints';
import type { ModelType } from '../enums/ModelType';
diff --git a/src/frontend/src/components/Boundary.tsx b/src/frontend/src/components/Boundary.tsx
index ec557f7b66..119b8fc9eb 100644
--- a/src/frontend/src/components/Boundary.tsx
+++ b/src/frontend/src/components/Boundary.tsx
@@ -23,7 +23,7 @@ export function Boundary({
}: Readonly<{
children: ReactNode;
label: string;
- fallback?: React.ReactElement | FallbackRender;
+ fallback?: React.ReactElement | FallbackRender;
}>): ReactNode {
const onError = useCallback(
(error: unknown, componentStack: string | undefined, eventId: string) => {
diff --git a/src/frontend/src/components/buttons/CopyButton.tsx b/src/frontend/src/components/buttons/CopyButton.tsx
index a31338bde6..79edcc174c 100644
--- a/src/frontend/src/components/buttons/CopyButton.tsx
+++ b/src/frontend/src/components/buttons/CopyButton.tsx
@@ -10,6 +10,8 @@ import {
import { InvenTreeIcon } from '../../functions/icons';
+import type { JSX } from 'react';
+
export function CopyButton({
value,
label,
diff --git a/src/frontend/src/components/buttons/SSOButton.tsx b/src/frontend/src/components/buttons/SSOButton.tsx
index 4a418b1816..892c4c8057 100644
--- a/src/frontend/src/components/buttons/SSOButton.tsx
+++ b/src/frontend/src/components/buttons/SSOButton.tsx
@@ -18,6 +18,8 @@ import type { AuthProvider } from '@lib/types/Auth';
import { t } from '@lingui/core/macro';
import { ProviderLogin } from '../../functions/auth';
+import type { JSX } from 'react';
+
const brandIcons: { [key: string]: JSX.Element } = {
google: ,
github: ,
diff --git a/src/frontend/src/components/buttons/StarredToggleButton.tsx b/src/frontend/src/components/buttons/StarredToggleButton.tsx
index 7fd9bbbb7c..73d241d92c 100644
--- a/src/frontend/src/components/buttons/StarredToggleButton.tsx
+++ b/src/frontend/src/components/buttons/StarredToggleButton.tsx
@@ -4,6 +4,7 @@ import { apiUrl } from '@lib/functions/Api';
import { t } from '@lingui/core/macro';
import { showNotification } from '@mantine/notifications';
import { IconBell } from '@tabler/icons-react';
+import type { JSX } from 'react';
import { useApi } from '../../contexts/ApiContext';
import { ActionButton } from './ActionButton';
diff --git a/src/frontend/src/components/dashboard/DashboardWidget.tsx b/src/frontend/src/components/dashboard/DashboardWidget.tsx
index d2f4840470..3054963285 100644
--- a/src/frontend/src/components/dashboard/DashboardWidget.tsx
+++ b/src/frontend/src/components/dashboard/DashboardWidget.tsx
@@ -4,6 +4,8 @@ import { IconX } from '@tabler/icons-react';
import { Boundary } from '../Boundary';
+import type { JSX } from 'react';
+
/**
* Dashboard widget properties.
*
diff --git a/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx b/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx
index 9398eec487..f712cc6de8 100644
--- a/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx
+++ b/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx
@@ -99,7 +99,7 @@ export function TemplateEditor(props: Readonly) {
previewAreas[0].key
);
- const codeRef = useRef();
+ const codeRef = useRef(undefined);
const loadCodeToEditor = useCallback(async (code: string) => {
try {
diff --git a/src/frontend/src/components/items/DocTooltip.tsx b/src/frontend/src/components/items/DocTooltip.tsx
index ce4b6b1528..f2566edb81 100644
--- a/src/frontend/src/components/items/DocTooltip.tsx
+++ b/src/frontend/src/components/items/DocTooltip.tsx
@@ -1,6 +1,6 @@
import { Trans } from '@lingui/react/macro';
import { Anchor, Container, HoverCard, ScrollArea, Text } from '@mantine/core';
-import { useEffect, useRef, useState } from 'react';
+import { type JSX, useEffect, useRef, useState } from 'react';
import * as classes from '../../main.css';
diff --git a/src/frontend/src/components/items/LanguageToggle.tsx b/src/frontend/src/components/items/LanguageToggle.tsx
index 63acbad93f..046c2d644a 100644
--- a/src/frontend/src/components/items/LanguageToggle.tsx
+++ b/src/frontend/src/components/items/LanguageToggle.tsx
@@ -16,6 +16,7 @@ export function LanguageToggle() {
margin: open === true ? 2 : 12,
padding: open === true ? 8 : 0
}}
+ aria-label='Open language options'
>
{
diff --git a/src/frontend/src/components/modals/AboutInvenTreeModal.tsx b/src/frontend/src/components/modals/AboutInvenTreeModal.tsx
index a8c7e6aeed..cf916327d3 100644
--- a/src/frontend/src/components/modals/AboutInvenTreeModal.tsx
+++ b/src/frontend/src/components/modals/AboutInvenTreeModal.tsx
@@ -24,6 +24,8 @@ import { useUserState } from '../../states/UserState';
import { CopyButton } from '../buttons/CopyButton';
import { StylishText } from '../items/StylishText';
+import type { JSX } from 'react';
+
type AboutLookupRef = {
ref: string;
title: JSX.Element;
diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx
index 524ac8a144..d6e2b12eb8 100644
--- a/src/frontend/src/components/nav/Header.tsx
+++ b/src/frontend/src/components/nav/Header.tsx
@@ -56,7 +56,7 @@ export function Header() {
{ open: openNotificationDrawer, close: closeNotificationDrawer }
] = useDisclosure(false);
- const { isLoggedIn } = useUserState();
+ const { isLoggedIn, isStaff } = useUserState();
const [notificationCount, setNotificationCount] = useState(0);
const globalSettings = useGlobalSettingsState();
diff --git a/src/frontend/src/components/nav/Layout.tsx b/src/frontend/src/components/nav/Layout.tsx
index e25712e618..f34187ebbd 100644
--- a/src/frontend/src/components/nav/Layout.tsx
+++ b/src/frontend/src/components/nav/Layout.tsx
@@ -2,7 +2,7 @@ import { t } from '@lingui/core/macro';
import { Container, Flex, Space } from '@mantine/core';
import { Spotlight, createSpotlight } from '@mantine/spotlight';
import { IconSearch } from '@tabler/icons-react';
-import { useEffect, useState } from 'react';
+import { type JSX, useEffect, useState } from 'react';
import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
import { getActions } from '../../defaults/actions';
diff --git a/src/frontend/src/contexts/LanguageContext.tsx b/src/frontend/src/contexts/LanguageContext.tsx
index 9923c12093..8332baf961 100644
--- a/src/frontend/src/contexts/LanguageContext.tsx
+++ b/src/frontend/src/contexts/LanguageContext.tsx
@@ -2,7 +2,7 @@ import { i18n } from '@lingui/core';
import { t } from '@lingui/core/macro';
import { I18nProvider } from '@lingui/react';
import { LoadingOverlay, Text } from '@mantine/core';
-import { useEffect, useRef, useState } from 'react';
+import { type JSX, useEffect, useRef, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../App';
diff --git a/src/frontend/src/contexts/ThemeContext.tsx b/src/frontend/src/contexts/ThemeContext.tsx
index 7fa0f7fb3e..a0fb46842e 100644
--- a/src/frontend/src/contexts/ThemeContext.tsx
+++ b/src/frontend/src/contexts/ThemeContext.tsx
@@ -13,6 +13,8 @@ import { useLocalState } from '../states/LocalState';
import { LanguageContext } from './LanguageContext';
import { colorSchema } from './colorSchema';
+import type { JSX } from 'react';
+
export function ThemeContext({
children
}: Readonly<{ children: JSX.Element }>) {
diff --git a/src/frontend/src/forms/StockForms.tsx b/src/frontend/src/forms/StockForms.tsx
index 16d64aa528..a0449c4168 100644
--- a/src/frontend/src/forms/StockForms.tsx
+++ b/src/frontend/src/forms/StockForms.tsx
@@ -20,7 +20,7 @@ import {
IconUsersGroup
} from '@tabler/icons-react';
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
-import { Suspense, useEffect, useMemo, useState } from 'react';
+import { type JSX, Suspense, useEffect, useMemo, useState } from 'react';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { ModelType } from '@lib/enums/ModelType';
diff --git a/src/frontend/src/functions/icons.tsx b/src/frontend/src/functions/icons.tsx
index 35d45c934b..bd9a3b0337 100644
--- a/src/frontend/src/functions/icons.tsx
+++ b/src/frontend/src/functions/icons.tsx
@@ -1,6 +1,5 @@
import type { InvenTreeIconType, TablerIconType } from '@lib/types/Icons';
import {
- Icon123,
IconArrowBigDownLineFilled,
IconArrowMerge,
IconBell,
@@ -61,6 +60,7 @@ import {
IconMapPinHeart,
IconMinusVertical,
IconNotes,
+ IconNumber123,
IconNumbers,
IconPackage,
IconPackageImport,
@@ -122,7 +122,7 @@ const icons: InvenTreeIconType = {
ordering: IconShoppingCart,
building: IconTool,
category: IconBinaryTree2,
- IPN: Icon123,
+ IPN: IconNumber123,
revision: IconGitBranch,
units: IconRulerMeasure,
keywords: IconTag,
diff --git a/src/frontend/src/functions/loading.tsx b/src/frontend/src/functions/loading.tsx
index 87c3f249c6..73c56d04a7 100644
--- a/src/frontend/src/functions/loading.tsx
+++ b/src/frontend/src/functions/loading.tsx
@@ -1,5 +1,5 @@
import { Center, Loader, MantineProvider, Stack } from '@mantine/core';
-import { Suspense } from 'react';
+import { type JSX, Suspense } from 'react';
import { colorSchema } from '../contexts/colorSchema';
import { theme } from '../theme';
diff --git a/src/frontend/src/hooks/UseCalendar.tsx b/src/frontend/src/hooks/UseCalendar.tsx
index e728e399b4..7335ea28cc 100644
--- a/src/frontend/src/hooks/UseCalendar.tsx
+++ b/src/frontend/src/hooks/UseCalendar.tsx
@@ -60,7 +60,7 @@ export default function useCalendar({
endpoint: ApiEndpoints;
queryParams?: any;
}): CalendarState {
- const ref = useRef(null);
+ const ref = useRef(null as any);
const filterSet = useFilterSet(`calendar-${name}`);
diff --git a/src/frontend/src/pages/Index/Settings/AccountSettings/useConfirm.tsx b/src/frontend/src/pages/Index/Settings/AccountSettings/useConfirm.tsx
index decfe20249..e66a8d4cd4 100644
--- a/src/frontend/src/pages/Index/Settings/AccountSettings/useConfirm.tsx
+++ b/src/frontend/src/pages/Index/Settings/AccountSettings/useConfirm.tsx
@@ -1,7 +1,7 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { Button, Group, Modal, Stack, TextInput } from '@mantine/core';
-import { useState } from 'react';
+import { type JSX, useState } from 'react';
/* Adapted from https://daveteu.medium.com/react-custom-confirmation-box-458cceba3f7b */
const createPromise = () => {
diff --git a/src/frontend/tests/pui_printing.spec.ts b/src/frontend/tests/pui_printing.spec.ts
index a2f5a546d6..3bc8130167 100644
--- a/src/frontend/tests/pui_printing.spec.ts
+++ b/src/frontend/tests/pui_printing.spec.ts
@@ -28,7 +28,7 @@ test('Label Printing', async ({ browser }) => {
// Select plugin
await page.getByLabel('related-field-plugin').click();
- await page.getByText('InvenTreeLabelSheet').click();
+ await page.getByText('InvenTreeLabelSheet').last().click();
// Select label template
await page.getByLabel('related-field-template').click();
@@ -36,6 +36,9 @@ test('Label Printing', async ({ browser }) => {
await page.waitForTimeout(100);
+ await page.getByLabel('related-field-plugin').click();
+ await page.getByText('InvenTreeLabelSheet').last().click();
+
// Submit the print form (second time should result in success)
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
await page.getByRole('button', { name: 'Print', exact: true }).click();
diff --git a/src/frontend/tests/settings/selectionList.spec.ts b/src/frontend/tests/settings/selectionList.spec.ts
index 1f899ce78e..21507a9d9b 100644
--- a/src/frontend/tests/settings/selectionList.spec.ts
+++ b/src/frontend/tests/settings/selectionList.spec.ts
@@ -89,7 +89,7 @@ test('PUI - Admin - Parameter', async ({ browser }) => {
.getByText('Template *Parameter')
.locator('div')
.filter({ hasText: /^Search\.\.\.$/ })
- .nth(2)
+ .first()
.click();
await page
.getByText('Template *Parameter')