2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-17 23:08:28 +00:00

[UI] Mantine 9 (#11947)

* Update mantine deps

* Get it to compile

* Update QueryCount widgets

- use new RollingNumber component

* Table updates

- Remove "hack" for column ordering

* "Fix" the column pinning bug (maybe?)

* Fix ColumnRenderers.tsx

* Fix login code for playwright

* Remove hashing requirement

* Fix build tests

* More fixes

* More test fixes

* Fix playwright test for dashboard item

* Update frontend version

* Update changelog

* Reduce query repeats

* More playwright fixes

* Further playwright fixes

* Fix for useFilterSet hook

* Fix unique key error

* Fix rendering issues when opening edit forms

* reduce console errors

* Fix unique key issues in search drawer

* Update frontend CHANGELOG.md

* More form tweaks

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
Oliver
2026-05-17 19:26:37 +10:00
committed by GitHub
parent 582013e51c
commit 9f78e994c2
29 changed files with 261 additions and 215 deletions
+13 -2
View File
@@ -2,7 +2,19 @@
This file contains historical changelog information for the InvenTree UI components library. This file contains historical changelog information for the InvenTree UI components library.
### 0.11.4 - Unreleased ### 1.4.0 - May 2026
#### Version Numbering
This update brings the version numbering in-line with the core InvenTree server version, which is currently at `1.4.0`. This versioning scheme will be maintained going forward, with the UI components library version matching the core server version.
Thus, version `1.4.x` of the UI components library will be compatible with version `1.4.x` of the InvenTree server, and so on.
#### Mantine Library Update
The underlying Mantine library has been updated from version `8.x` to version `9.x`. This update may introduce breaking changes for plugins that rely on the InvenTree UI components library, as the Mantine library is a core dependency. Plugin developers should test their plugins against this new version to ensure compatibility.
#### New Components
Adds additional functions in the plugin context related to form rendering and API invocation: Adds additional functions in the plugin context related to form rendering and API invocation:
- `useInstance` - `useInstance`
@@ -15,7 +27,6 @@ Exposes sub-components related to DetailDrawer rendering:
- `DetailDrawerComponent` - `DetailDrawerComponent`
- `useLocalLibState` - `useLocalLibState`
### 0.11.3 - April 2026 ### 0.11.3 - April 2026
Exposes additional type definitions related to rendering drawers from tables: Exposes additional type definitions related to rendering drawers from tables:
+2 -2
View File
@@ -139,8 +139,8 @@ export function RowActions({
aria-label={`row-action-menu-${index ?? ''}`} aria-label={`row-action-menu-${index ?? ''}`}
onClick={openMenu} onClick={openMenu}
disabled={disabled} disabled={disabled}
variant='subtle' variant='transparent'
color='gray' size='sm'
> >
<IconDots /> <IconDots />
</ActionIcon> </ActionIcon>
+8 -8
View File
@@ -1,5 +1,5 @@
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import { useCallback, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import type { FilterSetState, TableFilter } from '../types/Filters'; import type { FilterSetState, TableFilter } from '../types/Filters';
export default function useFilterSet( export default function useFilterSet(
@@ -16,15 +16,15 @@ export default function useFilterSet(
getInitialValueInEffect: false getInitialValueInEffect: false
}); });
const activeFilters: TableFilter[] = useMemo(() => { useEffect(() => {
if (storedFilters == null) { if (storedFilters == null) {
// If there are no stored filters, set initial values setStoredFilters(initialFilters || []);
const filters = initialFilters || [];
setStoredFilters(filters);
return filters;
} }
return storedFilters || []; }, [storedFilters, initialFilters, setStoredFilters]);
}, [storedFilters]);
const activeFilters: TableFilter[] = useMemo(() => {
return storedFilters ?? initialFilters ?? [];
}, [storedFilters, initialFilters]);
// Callback to clear all active filters from the table // Callback to clear all active filters from the table
const clearActiveFilters = useCallback(() => { const clearActiveFilters = useCallback(() => {
+1 -1
View File
@@ -100,7 +100,7 @@ export type TableState = {
*/ */
export type TableColumnProps<T = any> = { export type TableColumnProps<T = any> = {
accessor?: string; accessor?: string;
title?: string; title?: string | ReactNode;
ordering?: string; ordering?: string;
sortable?: boolean; sortable?: boolean;
switchable?: boolean; switchable?: boolean;
+14 -14
View File
@@ -1,7 +1,7 @@
{ {
"name": "@inventreedb/ui", "name": "@inventreedb/ui",
"description": "UI components for the InvenTree project", "description": "UI components for the InvenTree project",
"version": "0.11.3", "version": "1.4.0",
"private": false, "private": false,
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
@@ -60,17 +60,17 @@
"@github/webauthn-json": "^2.1.1", "@github/webauthn-json": "^2.1.1",
"@lingui/core": "^5.9.2", "@lingui/core": "^5.9.2",
"@lingui/react": "^5.9.2", "@lingui/react": "^5.9.2",
"@mantine/carousel": "^8.2.7", "@mantine/carousel": "^9.2.1",
"@mantine/charts": "^8.2.7", "@mantine/charts": "^9.2.1",
"@mantine/core": "^8.2.7", "@mantine/core": "^9.2.1",
"@mantine/dates": "^8.2.7", "@mantine/dates": "^9.2.1",
"@mantine/dropzone": "^8.2.7", "@mantine/dropzone": "^9.2.1",
"@mantine/form": "^8.2.7", "@mantine/form": "^9.2.1",
"@mantine/hooks": "^8.2.7", "@mantine/hooks": "^9.2.1",
"@mantine/modals": "^8.2.7", "@mantine/modals": "^9.2.1",
"@mantine/notifications": "^8.2.7", "@mantine/notifications": "^9.2.1",
"@mantine/spotlight": "^8.2.7", "@mantine/spotlight": "^9.2.1",
"@mantine/vanilla-extract": "^8.2.7", "@mantine/vanilla-extract": "^9.2.1",
"@messageformat/date-skeleton": "^1.1.0", "@messageformat/date-skeleton": "^1.1.0",
"@sentry/react": "^10.43.0", "@sentry/react": "^10.43.0",
"@tabler/icons-react": "^3.17.0", "@tabler/icons-react": "^3.17.0",
@@ -89,8 +89,8 @@
"embla-carousel-react": "^8.5.2", "embla-carousel-react": "^8.5.2",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"html5-qrcode": "^2.3.8", "html5-qrcode": "^2.3.8",
"mantine-contextmenu": "^8.2.0", "mantine-contextmenu": "^9.2.1",
"mantine-datatable": "^8.2.0", "mantine-datatable": "^9.2.0",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
@@ -1,7 +1,7 @@
import { ActionIcon, Anchor, Group, Loader } from '@mantine/core'; import { ActionIcon, Anchor, Group, RollingNumber } from '@mantine/core';
import { IconExclamationCircle } from '@tabler/icons-react'; import { IconExclamationCircle } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { type ReactNode, useCallback, useMemo } from 'react'; import { type ReactNode, useCallback, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { StylishText } from '@lib/components/StylishText'; import { StylishText } from '@lib/components/StylishText';
@@ -37,11 +37,15 @@ function QueryCountWidget({
const modelProperties = ModelInformationDict[modelType]; const modelProperties = ModelInformationDict[modelType];
const [count, setCount] = useState<number>(0);
const query = useQuery({ const query = useQuery({
queryKey: ['dashboard-query-count', modelType, params, visibility], queryKey: ['dashboard-query-count', modelType, params, visibility],
enabled: user.hasViewPermission(modelType) && visibility === 'visible', enabled: user.hasViewPermission(modelType) && visibility === 'visible',
refetchOnWindowFocus: false,
refetchOnMount: true, refetchOnMount: true,
refetchInterval: 10 * 60 * 1000, // 10 minute refetch interval refetchInterval: 10 * 60 * 1000, // 10 minute refetch interval
staleTime: 5 * 60 * 1000, // 5 minute stale time
queryFn: () => { queryFn: () => {
if (visibility !== 'visible') { if (visibility !== 'visible') {
return null; return null;
@@ -54,7 +58,10 @@ function QueryCountWidget({
limit: 1 limit: 1
} }
}) })
.then((res) => res.data); .then((res) => {
setCount(res.data?.count ?? 0);
return res.data;
});
} }
}); });
@@ -81,18 +88,16 @@ function QueryCountWidget({
); );
const result: ReactNode = useMemo(() => { const result: ReactNode = useMemo(() => {
if (query.isFetching) { if (query.isError) {
return <Loader size='xs' />;
} else if (query.isError) {
return ( return (
<ActionIcon color='red' variant='transparent' size='lg'> <ActionIcon color='red' variant='transparent' size='lg'>
<IconExclamationCircle /> <IconExclamationCircle />
</ActionIcon> </ActionIcon>
); );
} else { } else {
return <StylishText size='xl'>{query.data?.count ?? '-'}</StylishText>; return <RollingNumber value={count} fz='20px' />;
} }
}, [query.isFetching, query.isError, query.data]); }, [query.isFetching, query.isError, count]);
return ( return (
<Anchor href='#' onClick={onFollowLink} underline='never'> <Anchor href='#' onClick={onFollowLink} underline='never'>
@@ -81,10 +81,14 @@ export function ApiFormField({
...fieldDefinition, ...fieldDefinition,
autoFill: undefined, autoFill: undefined,
placeholderAutofill: undefined, placeholderAutofill: undefined,
placeholderWarning: undefined,
placeholderWarningCompare: undefined,
singleFetchFunction: undefined,
autoFillFilters: undefined, autoFillFilters: undefined,
onValueChange: undefined, onValueChange: undefined,
adjustFilters: undefined, adjustFilters: undefined,
adjustValue: undefined, adjustValue: undefined,
allow_blank: undefined,
allow_null: undefined, allow_null: undefined,
read_only: undefined, read_only: undefined,
children: undefined, children: undefined,
@@ -419,7 +419,13 @@ export function RelatedModelField({
modelRenderer: undefined, modelRenderer: undefined,
onValueChange: undefined, onValueChange: undefined,
adjustFilters: undefined, adjustFilters: undefined,
adjustValue: undefined,
placeholderAutofill: undefined,
placeholderWarning: undefined,
placeholderWarningCompare: undefined,
singleFetchFunction: undefined,
exclude: undefined, exclude: undefined,
allow_blank: undefined,
allow_null: undefined, allow_null: undefined,
read_only: undefined read_only: undefined
}; };
+3 -1
View File
@@ -114,7 +114,9 @@ export function Header() {
}, },
// Refetch every minute, *if* the tab is visible // Refetch every minute, *if* the tab is visible
refetchInterval: 60 * 1000, refetchInterval: 60 * 1000,
refetchOnMount: true refetchOnMount: true,
refetchOnWindowFocus: false,
staleTime: 30 * 1000
}); });
// Sync Navigation Drawer state with zustand // Sync Navigation Drawer state with zustand
@@ -48,7 +48,10 @@ export function PageDetail({
useHotkeys([ useHotkeys([
[ [
'mod+E', 'mod+E',
() => { (event) => {
if (event.repeat) {
return;
}
if (editEnabled ?? true) { if (editEnabled ?? true) {
editAction?.(); editAction?.();
} }
@@ -30,7 +30,7 @@ import {
IconX IconX
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
import { type NavigateFunction, useNavigate } from 'react-router-dom'; import { type NavigateFunction, useNavigate } from 'react-router-dom';
import { Boundary } from '@lib/components/Boundary'; import { Boundary } from '@lib/components/Boundary';
@@ -165,7 +165,7 @@ function QueryResultGroup({
<Accordion.Panel> <Accordion.Panel>
<Stack gap={'xs'} aria-label={`search-group-results-${query.model}`}> <Stack gap={'xs'} aria-label={`search-group-results-${query.model}`}>
{query.results.results.map((result: any, index: number) => ( {query.results.results.map((result: any, index: number) => (
<> <Fragment key={`result-group-${query.model}-${result.pk}`}>
<Anchor <Anchor
underline='never' underline='never'
href={getDetailUrl(query.model, result.pk, true)} href={getDetailUrl(query.model, result.pk, true)}
@@ -184,7 +184,7 @@ function QueryResultGroup({
key={`divider-${query.model}-${result.pk}`} key={`divider-${query.model}-${result.pk}`}
/> />
)} )}
</> </Fragment>
))} ))}
</Stack> </Stack>
</Accordion.Panel> </Accordion.Panel>
@@ -370,6 +370,7 @@ function BasePanelGroup({
(panel) => (panel) =>
!panel.hidden && ( !panel.hidden && (
<PanelTabComponent <PanelTabComponent
key={`panel-tab-${group.id}-${panel.name}`}
expanded={expanded} expanded={expanded}
panel={panel} panel={panel}
onClick={(event: any) => onClick={(event: any) =>
+10 -6
View File
@@ -62,14 +62,18 @@ export function useApiFormModal(props: ApiFormModalProps) {
id: modalId, id: modalId,
title: formProps.title, title: formProps.title,
onOpen: () => { onOpen: () => {
setIsOpen(true); queueMicrotask(() => {
modalState.setModalOpen(modalId, true); setIsOpen(true);
formProps.onOpen?.(); modalState.setModalOpen(modalId, true);
formProps.onOpen?.();
});
}, },
onClose: () => { onClose: () => {
setIsOpen(false); queueMicrotask(() => {
modalState.setModalOpen(modalId, false); setIsOpen(false);
formProps.onClose?.(); modalState.setModalOpen(modalId, false);
formProps.onClose?.();
});
}, },
closeOnClickOutside: formProps.closeOnClickOutside, closeOnClickOutside: formProps.closeOnClickOutside,
size: props.size ?? 'xl', size: props.size ?? 'xl',
@@ -8,17 +8,15 @@ export function AccountContent() {
const SECONDARY_COL_HEIGHT = PRIMARY_COL_HEIGHT / 2 - 8; const SECONDARY_COL_HEIGHT = PRIMARY_COL_HEIGHT / 2 - 8;
return ( return (
<div> <SimpleGrid cols={{ base: 1, md: 2 }} spacing='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='md'> <Container w='100%'>
<Container w='100%'> <AccountDetailPanel />
<AccountDetailPanel /> </Container>
</Container> <Grid gap='md'>
<Grid gutter='md'> <Grid.Col>
<Grid.Col> <UserTheme height={SECONDARY_COL_HEIGHT} />
<UserTheme height={SECONDARY_COL_HEIGHT} /> </Grid.Col>
</Grid.Col> </Grid>
</Grid> </SimpleGrid>
</SimpleGrid>
</div>
); );
} }
+1 -1
View File
@@ -330,7 +330,7 @@ export function PathColumn(props: TableColumnProps): TableColumn {
<TableHoverCard <TableHoverCard
value={<Text size='sm'>{instance.name}</Text>} value={<Text size='sm'>{instance.name}</Text>}
icon='sitemap' icon='sitemap'
title={props.title} title={props.title?.toLocaleString() ?? t`Path`}
extra={[<Text size='sm'>{instance.pathstring}</Text>]} extra={[<Text size='sm'>{instance.pathstring}</Text>]}
/> />
); );
+18 -22
View File
@@ -7,7 +7,6 @@ import { cancelEvent } from '@lib/functions/Events';
import { mapFields } from '@lib/functions/Forms'; import { mapFields } from '@lib/functions/Forms';
import { getDetailUrl } from '@lib/functions/Navigation'; import { getDetailUrl } from '@lib/functions/Navigation';
import { navigateToLink } from '@lib/functions/Navigation'; import { navigateToLink } from '@lib/functions/Navigation';
import { hashString } from '@lib/functions/String';
import { useStoredTableState } from '@lib/states/StoredTableState'; import { useStoredTableState } from '@lib/states/StoredTableState';
import type { TableFilter } from '@lib/types/Filters'; import type { TableFilter } from '@lib/types/Filters';
import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { ApiFormFieldSet } from '@lib/types/Forms';
@@ -18,8 +17,8 @@ import type {
} from '@lib/types/Tables'; } from '@lib/types/Tables';
import type { TableColumn } from '@lib/types/Tables'; import type { TableColumn } from '@lib/types/Tables';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { Box, Stack } from '@mantine/core'; import { ActionIcon, Box, Stack } from '@mantine/core';
import { IconArrowRight } from '@tabler/icons-react'; import { IconArrowRight, IconClick } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { import {
type ContextMenuItemOptions, type ContextMenuItemOptions,
@@ -254,12 +253,6 @@ export function InvenTreeTableInternal<T extends Record<string, any>>({
[tableState.setSelectedRecords] [tableState.setSelectedRecords]
); );
// A hash of the current column configuration
// This is a workaround to fix an issue with mantine-datatable where
// the columns do not update correctly when they are changed dynamically
// Ref: https://github.com/icflorescu/mantine-datatable/issues/759
const [columnHash, setColumnHash] = useState<string>('');
// Update column visibility when hiddenColumns change // Update column visibility when hiddenColumns change
const dataColumns: any = useMemo(() => { const dataColumns: any = useMemo(() => {
let cols: TableColumn[] = columns.filter((col) => col?.hidden != true); let cols: TableColumn[] = columns.filter((col) => col?.hidden != true);
@@ -326,7 +319,11 @@ export function InvenTreeTableInternal<T extends Record<string, any>>({
if (tableProps.rowActions) { if (tableProps.rowActions) {
cols.push({ cols.push({
accessor: ACTIONS_COLUMN_ACCESSOR, accessor: ACTIONS_COLUMN_ACCESSOR,
title: ' ', title: (
<ActionIcon variant='transparent' size='sm'>
<IconClick />
</ActionIcon>
),
hidden: false, hidden: false,
resizable: false, resizable: false,
switchable: false, switchable: false,
@@ -350,13 +347,6 @@ export function InvenTreeTableInternal<T extends Record<string, any>>({
tableState.selectedRecords tableState.selectedRecords
]); ]);
useEffect(() => {
const columnNames: string = dataColumns
.map((col: any) => col.accessor)
.join(',');
setColumnHash(hashString(columnNames));
}, [dataColumns]);
// Callback when column visibility is toggled // Callback when column visibility is toggled
const toggleColumn = useCallback( const toggleColumn = useCallback(
(columnName: string) => { (columnName: string) => {
@@ -378,19 +368,25 @@ export function InvenTreeTableInternal<T extends Record<string, any>>({
[cacheKey, dataColumns] [cacheKey, dataColumns]
); );
// Generate an ordered list of column names,
// which we use to ensure the table is reloaded correctly when columns are added/removed/renamed
const tableColumnNames = useMemo(
() => dataColumns.map((col: any) => col.accessor).join(','),
[dataColumns]
);
// Final state of the table columns // Final state of the table columns
const tableColumns = useDataTableColumns({ const tableColumns = useDataTableColumns({
key: `${cacheKey}-${columnHash}`, key: cacheKey,
columns: dataColumns, columns: dataColumns,
getInitialValueInEffect: false getInitialValueInEffect: false
}); });
// Reset column ordering and custom widths when the component is mounted // Reset column ordering and custom widths when the component is mounted
// Ref: https://github.com/icflorescu/mantine-datatable/issues/759#issuecomment-4148942070 // Ref: https://github.com/icflorescu/mantine-datatable/issues/759
useEffect(() => { useEffect(() => {
tableColumns.resetColumnsOrder(); tableColumns.setColumnsOrder(dataColumns.map((col: any) => col.accessor));
tableColumns.resetColumnsWidth(); }, [tableColumnNames]);
}, []);
// Reset the pagination state when the search term changes // Reset the pagination state when the search term changes
useEffect(() => { useEffect(() => {
@@ -122,6 +122,9 @@ function OutputAllocationDrawer({
opened={opened} opened={opened}
onClose={close} onClose={close}
withCloseButton withCloseButton
closeButtonProps={{
'aria-label': 'close-allocation-drawer'
}}
closeOnEscape closeOnEscape
closeOnClickOutside closeOnClickOutside
styles={{ styles={{
+2 -2
View File
@@ -36,8 +36,8 @@ export const doLogin = async (page: Page, options?: LoginOptions) => {
await page.waitForURL('**/web/login'); await page.waitForURL('**/web/login');
await page.getByLabel('username').fill(username); await page.getByRole('textbox', { name: 'login-username' }).fill(username);
await page.getByLabel('password').fill(password); await page.getByRole('textbox', { name: 'login-password' }).fill(password);
await page.waitForTimeout(100); await page.waitForTimeout(100);
+4 -1
View File
@@ -225,6 +225,7 @@ test('Build Order - Build Outputs', async ({ browser }) => {
await page.getByRole('cell', { name: 'BO0011' }).click(); await page.getByRole('cell', { name: 'BO0011' }).click();
await loadTab(page, 'Incomplete Outputs'); await loadTab(page, 'Incomplete Outputs');
await page.getByRole('cell', { name: 'BX-123' }).waitFor();
// Check the "printing" actions for the selected outputs // Check the "printing" actions for the selected outputs
await page.getByRole('checkbox', { name: 'Select all records' }).check(); await page.getByRole('checkbox', { name: 'Select all records' }).check();
@@ -523,6 +524,8 @@ test('Build Order - Consume Stock', async ({ browser }) => {
// Consume the rest of the stock via line items // Consume the rest of the stock via line items
await loadTab(page, 'Required Parts'); await loadTab(page, 'Required Parts');
await page.getByText('10K resistor in 0805 SMD').first().waitFor();
await page.getByRole('checkbox', { name: 'Select all records' }).check(); await page.getByRole('checkbox', { name: 'Select all records' }).check();
await page await page
.getByRole('button', { name: 'action-button-consume-stock' }) .getByRole('button', { name: 'action-button-consume-stock' })
@@ -603,7 +606,7 @@ test('Build Order - Tracked Outputs', async ({ browser }) => {
await allocationRow.getByText('1 / 1').waitFor(); await allocationRow.getByText('1 / 1').waitFor();
// Close the allocation wizard // Close the allocation wizard
await page.getByRole('banner').getByRole('button').click(); await page.getByRole('button', { name: 'close-allocation-drawer' }).click();
// Check that the output is now allocated as expected // Check that the output is now allocated as expected
await row.getByText('1 / 6').waitFor(); await row.getByText('1 / 6').waitFor();
+28 -19
View File
@@ -1,6 +1,8 @@
import type { Page } from '@playwright/test'; import type { Page } from '@playwright/test';
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { createApi } from '../api.js';
import { test } from '../baseFixtures.js'; import { test } from '../baseFixtures.js';
import { allaccessuser } from '../defaults.js';
import { doCachedLogin } from '../login.js'; import { doCachedLogin } from '../login.js';
import { setPluginState } from '../settings.js'; import { setPluginState } from '../settings.js';
@@ -96,9 +98,7 @@ test('Dashboard - Plugins', async ({ browser }) => {
await page.getByText('Hello world! This is a sample').waitFor(); await page.getByText('Hello world! This is a sample').waitFor();
}); });
test('Dashboard - Preserve widget sizes when adding new widget', async ({ test('Dashboard - Preserve widget sizes', async ({ browser }) => {
browser
}) => {
// Regression: addWidget previously snapped every existing widget back to // Regression: addWidget previously snapped every existing widget back to
// its minW/minH. Fix is in DashboardLayout.tsx::addWidget (overrideSize=false). // its minW/minH. Fix is in DashboardLayout.tsx::addWidget (overrideSize=false).
const TARGET_W = 10; const TARGET_W = 10;
@@ -110,7 +110,11 @@ test('Dashboard - Preserve widget sizes when adding new widget', async ({
return raw ? (JSON.parse(raw)?.state?.layouts ?? {}) : {}; return raw ? (JSON.parse(raw)?.state?.layouts ?? {}) : {};
}); });
const page = await doCachedLogin(browser); const user = allaccessuser;
const page = await doCachedLogin(browser, {
user: user
});
await resetDashboard(page); await resetDashboard(page);
// Add widget A; this also persists to the backend user profile. // Add widget A; this also persists to the backend user profile.
@@ -120,7 +124,7 @@ test('Dashboard - Preserve widget sizes when adding new widget', async ({
await page.getByLabel('add-widget-ovr-so').click(); await page.getByLabel('add-widget-ovr-so').click();
await page.getByRole('banner').getByRole('button').click(); await page.getByRole('banner').getByRole('button').click();
await page.getByText('Overdue Sales Orders').waitFor(); await page.getByText('Overdue Sales Orders').waitFor();
await page.waitForTimeout(500); await page.waitForTimeout(100);
// Inflate widget A on the backend profile and reload. The auth flow on // Inflate widget A on the backend profile and reload. The auth flow on
// page load rehydrates layouts from the profile, not localStorage, so a // page load rehydrates layouts from the profile, not localStorage, so a
@@ -132,26 +136,31 @@ test('Dashboard - Preserve widget sizes when adding new widget', async ({
it?.i === 'ovr-so' ? { ...it, w: TARGET_W, h: TARGET_H } : it it?.i === 'ovr-so' ? { ...it, w: TARGET_W, h: TARGET_H } : it
); );
} }
await page.evaluate(async (layouts) => {
const csrf = document.cookie.match(/csrftoken=([^;]+)/)?.[1] ?? ''; const api = createApi({
await fetch('/api/user/profile/', { username: user.username,
method: 'PATCH', password: user.testcred
credentials: 'same-origin', });
headers: {
'Content-Type': 'application/json', (await api).patch('user/profile/', {
'X-CSRFToken': csrf data: {
}, widgets: {
body: JSON.stringify({ widgets: { widgets: ['ovr-so'], layouts } }) widgets: ['ovr-so'],
}); layouts: inflated
}, inflated); }
}
});
await page.reload(); await page.reload();
await page.getByText('Overdue Sales Orders').waitFor(); await page.getByText('Overdue Sales Orders').waitFor();
await page.waitForTimeout(500); await page.waitForTimeout(100);
// Sanity: profile rehydration produced the inflated values. // Sanity: profile rehydration produced the inflated values.
for (const [bp, items] of Object.entries(await readLayouts(page))) { for (const [bp, items] of Object.entries(await readLayouts(page))) {
const entry = (items as any[]).find((i) => i?.i === 'ovr-so'); const entry = (items as any[]).find((i) => i?.i === 'ovr-so');
console.log('entry:', bp, entry);
expect(entry?.w, `${bp}: ovr-so missing or wrong w`).toBe(TARGET_W); expect(entry?.w, `${bp}: ovr-so missing or wrong w`).toBe(TARGET_W);
expect(entry?.h, `${bp}: ovr-so missing or wrong h`).toBe(TARGET_H); expect(entry?.h, `${bp}: ovr-so missing or wrong h`).toBe(TARGET_H);
} }
@@ -163,7 +172,7 @@ test('Dashboard - Preserve widget sizes when adding new widget', async ({
await page.getByLabel('add-widget-ovr-po').click(); await page.getByLabel('add-widget-ovr-po').click();
await page.getByRole('banner').getByRole('button').click(); await page.getByRole('banner').getByRole('button').click();
await page.getByText('Overdue Purchase Orders').waitFor(); await page.getByText('Overdue Purchase Orders').waitFor();
await page.waitForTimeout(800); await page.waitForTimeout(100);
for (const [bp, items] of Object.entries(await readLayouts(page))) { for (const [bp, items] of Object.entries(await readLayouts(page))) {
const entry = (items as any[]).find((i) => i?.i === 'ovr-so'); const entry = (items as any[]).find((i) => i?.i === 'ovr-so');
+3 -1
View File
@@ -344,7 +344,9 @@ test('Parts - BOM Comparison', async ({ browser }) => {
await page.getByText('Removed from BOM').first().waitFor(); await page.getByText('Removed from BOM').first().waitFor();
// Change display mode // Change display mode
await page.getByRole('textbox', { name: 'bom-compare-display-mode' }).click(); await page
.getByRole('combobox', { name: 'bom-compare-display-mode' })
.click();
await page.getByRole('option', { name: 'Show different Parts' }).click(); await page.getByRole('option', { name: 'Show different Parts' }).click();
// Use URL params to compare directly // Use URL params to compare directly
@@ -162,7 +162,7 @@ test('Purchasing - Manufacturer Parts', async ({ browser }) => {
await page.getByRole('button', { name: 'table-export-data' }).click(); await page.getByRole('button', { name: 'table-export-data' }).click();
await page.getByText('Select export plugin').waitFor(); await page.getByText('Select export plugin').waitFor();
await page await page
.getByRole('textbox', { name: 'choice-field-export_plugin' }) .getByRole('combobox', { name: 'choice-field-export_plugin' })
.fill('CSV'); .fill('CSV');
await page.getByRole('button', { name: 'Export', exact: true }).click(); await page.getByRole('button', { name: 'Export', exact: true }).click();
await page.getByText('Process completed successfully').waitFor(); await page.getByText('Process completed successfully').waitFor();
@@ -509,8 +509,9 @@ test('Purchase Orders - Receive Items', async ({ browser }) => {
// Select all line items to receive // Select all line items to receive
await loadTab(page, 'Line Items'); await loadTab(page, 'Line Items');
await page.getByRole('cell', { name: '002.02-PCB' }).waitFor();
await page.getByLabel('Select all records').click(); await page.getByLabel('Select all records').click();
await page.waitForTimeout(200); await page.waitForTimeout(100);
await page.getByLabel('action-button-receive-items').click(); await page.getByLabel('action-button-receive-items').click();
// Check for display of individual locations // Check for display of individual locations
@@ -606,6 +607,8 @@ test('Purchase Orders - Receive Virtual Items', async ({ browser }) => {
// Receive the line item // Receive the line item
await loadTab(page, 'Line Items'); await loadTab(page, 'Line Items');
await page.getByRole('cell', { name: 'Thumbnail CRM license' }).waitFor();
await page.getByRole('checkbox', { name: 'Select all records' }).click(); await page.getByRole('checkbox', { name: 'Select all records' }).click();
await page await page
.getByRole('button', { name: 'action-button-receive-items' }) .getByRole('button', { name: 'action-button-receive-items' })
@@ -12,6 +12,8 @@ test('Return Orders - Receive Items', async ({ browser }) => {
await loadTab(page, 'Attachments'); await loadTab(page, 'Attachments');
await loadTab(page, 'Line Items'); await loadTab(page, 'Line Items');
await page.getByRole('cell', { name: 'WID-REV-A' }).first().waitFor();
await page.getByRole('checkbox', { name: 'Select all records' }).click(); await page.getByRole('checkbox', { name: 'Select all records' }).click();
await page.getByRole('button', { name: 'action-button-receive-' }).click(); await page.getByRole('button', { name: 'action-button-receive-' }).click();
await page.getByRole('banner').getByText('Receive Items').waitFor(); await page.getByRole('banner').getByText('Receive Items').waitFor();
+2 -2
View File
@@ -111,14 +111,14 @@ test('Stock - Location Delete', async ({ browser }) => {
.click(); .click();
await page await page
.getByRole('textbox', { name: 'choice-field-delete_stock_items' }) .getByRole('combobox', { name: 'choice-field-delete_stock_items' })
.click(); .click();
await page await page
.getByRole('option', { name: 'Move items to parent location' }) .getByRole('option', { name: 'Move items to parent location' })
.click(); .click();
await page await page
.getByRole('textbox', { name: 'choice-field-delete_sub_locations' }) .getByRole('combobox', { name: 'choice-field-delete_sub_locations' })
.click(); .click();
await page.getByRole('option', { name: 'Delete items' }).click(); await page.getByRole('option', { name: 'Delete items' }).click();
+2 -1
View File
@@ -6,7 +6,8 @@ import { doCachedLogin } from './login';
// Helper function to open the export data dialog // Helper function to open the export data dialog
const openExportDialog = async (page) => { const openExportDialog = async (page) => {
await page.waitForLoadState('networkidle'); await page.waitForLoadState('networkidle');
await page.getByLabel('table-export-data').click();
await page.getByRole('button', { name: 'table-export-data' }).click();
await page.getByText('Export Format *', { exact: true }).waitFor(); await page.getByText('Export Format *', { exact: true }).waitFor();
await page.getByText('Export Plugin *', { exact: true }).waitFor(); await page.getByText('Export Plugin *', { exact: true }).waitFor();
}; };
+3 -3
View File
@@ -26,7 +26,7 @@ test('Importing - Admin Center', async ({ browser }) => {
await page.getByText('Errors exist for one or more').waitFor(); await page.getByText('Errors exist for one or more').waitFor();
await page await page
.getByRole('textbox', { name: 'choice-field-model_type' }) .getByRole('combobox', { name: 'choice-field-model_type' })
.fill('bom'); .fill('bom');
await page.getByRole('option', { name: 'BOM Item', exact: true }).click(); await page.getByRole('option', { name: 'BOM Item', exact: true }).click();
await page.getByRole('button', { name: 'Submit' }).click(); await page.getByRole('button', { name: 'Submit' }).click();
@@ -36,7 +36,7 @@ test('Importing - Admin Center', async ({ browser }) => {
await page.getByText('Existing database identifier for the record').waitFor(); await page.getByText('Existing database identifier for the record').waitFor();
await page await page
.getByRole('textbox', { name: 'import-column-map-reference' }) .getByRole('combobox', { name: 'import-column-map-reference' })
.click(); .click();
await page.getByRole('option', { name: 'Ignore this field' }).click(); await page.getByRole('option', { name: 'Ignore this field' }).click();
@@ -195,7 +195,7 @@ test('Importing - Natural Keys', async ({ browser }) => {
// Select different columns for data import // Select different columns for data import
// We will use the "SKU" field to map to the supplier part // We will use the "SKU" field to map to the supplier part
await page.getByRole('textbox', { name: 'import-column-map-part' }).click(); await page.getByRole('combobox', { name: 'import-column-map-part' }).click();
await page.getByRole('option', { name: 'SKU' }).click(); await page.getByRole('option', { name: 'SKU' }).click();
// Other import fields will be left as default // Other import fields will be left as default
+12 -5
View File
@@ -47,14 +47,17 @@ test('Machines - Activation', async ({ browser }) => {
.getByRole('textbox', { name: 'text-field-name' }) .getByRole('textbox', { name: 'text-field-name' })
.fill('my-dummy-machine'); .fill('my-dummy-machine');
await page await page
.getByRole('textbox', { name: 'choice-field-machine_type' }) .getByRole('combobox', { name: 'choice-field-machine_type' })
.fill('label'); .fill('label');
await page.getByRole('option', { name: 'Label Printer' }).click(); await page.getByRole('option', { name: 'Label Printer' }).click();
await page.getByRole('textbox', { name: 'choice-field-driver' }).click(); await page.getByRole('combobox', { name: 'choice-field-driver' }).click();
await page.waitForTimeout(200);
await page await page
.getByRole('option', { name: 'Sample Label Printer Driver' }) .getByRole('option', { name: 'Sample Label Printer Driver' })
.click(); .click();
await page.waitForTimeout(200);
await page.getByRole('button', { name: 'Submit' }).click(); await page.getByRole('button', { name: 'Submit' }).click();
} else { } else {
// Machine already exists - just click on it to open the "machine drawer" // Machine already exists - just click on it to open the "machine drawer"
@@ -64,10 +67,13 @@ test('Machines - Activation', async ({ browser }) => {
// Creating the new machine opens the "machine drawer" // Creating the new machine opens the "machine drawer"
// Check for "machine type" settings // Check for "machine type" settings
await page.getByText('Scope the printer to a specific location').waitFor(); await page
.getByText('Scope the printer to a specific location')
.first()
.waitFor();
// Check for "machine driver" settings // Check for "machine driver" settings
await page.getByText('Custom string for connecting').waitFor(); await page.getByText('Custom string for connecting').first().waitFor();
// Edit the available setting // Edit the available setting
await page.getByRole('button', { name: 'edit-setting-CONNECTION' }).click(); await page.getByRole('button', { name: 'edit-setting-CONNECTION' }).click();
@@ -94,6 +100,7 @@ test('Machines - Activation', async ({ browser }) => {
// Let's print something with the machine // Let's print something with the machine
await navigate(page, 'stock/location/1/stock-items'); await navigate(page, 'stock/location/1/stock-items');
await page.getByText('Blue plastic enclosure').first().waitFor();
await page.getByRole('checkbox', { name: 'Select all records' }).check(); await page.getByRole('checkbox', { name: 'Select all records' }).check();
await page await page
@@ -110,7 +117,7 @@ test('Machines - Activation', async ({ browser }) => {
await page.getByText('InvenTreeLabelMachine').click(); await page.getByText('InvenTreeLabelMachine').click();
await page await page
.getByRole('textbox', { name: 'choice-field-machine' }) .getByRole('combobox', { name: 'choice-field-machine' })
.fill('dummy'); .fill('dummy');
await page.getByRole('option', { name: 'my-dummy-machine' }).click(); await page.getByRole('option', { name: 'my-dummy-machine' }).click();
+5 -5
View File
@@ -45,9 +45,9 @@ test('Settings - User theme', async ({ browser }) => {
await page.getByRole('menuitem', { name: 'User settings' }).click(); await page.getByRole('menuitem', { name: 'User settings' }).click();
// loader // loader
await page.getByRole('textbox', { name: 'Loader Type Selector' }).click(); await page.getByRole('combobox', { name: 'Loader Type Selector' }).click();
await page.getByRole('option', { name: 'Oval' }).click(); await page.getByRole('option', { name: 'Oval' }).click();
await page.getByRole('textbox', { name: 'Loader Type Selector' }).click(); await page.getByRole('combobox', { name: 'Loader Type Selector' }).click();
await page.getByRole('option', { name: 'Bars' }).click(); await page.getByRole('option', { name: 'Bars' }).click();
// dark / light mode // dark / light mode
@@ -92,7 +92,7 @@ test('Settings - User', async ({ browser }) => {
await page.getByText('Profile Details').waitFor(); await page.getByText('Profile Details').waitFor();
// Language selection // Language selection
await page.getByRole('textbox', { name: 'Select language' }).click(); await page.getByRole('combobox', { name: 'Select language' }).click();
await page.getByRole('option', { name: 'العربية' }).waitFor(); await page.getByRole('option', { name: 'العربية' }).waitFor();
await page.getByRole('option', { name: 'Deutsch' }).waitFor(); await page.getByRole('option', { name: 'Deutsch' }).waitFor();
await page.getByRole('option', { name: 'English' }).waitFor(); await page.getByRole('option', { name: 'English' }).waitFor();
@@ -321,8 +321,8 @@ test('Settings - Admin - Background Tasks', async ({ browser }) => {
// Background worker should be running, and idle // Background worker should be running, and idle
await page.getByText('Background worker running').waitFor(); await page.getByText('Background worker running').waitFor();
await page.getByText('Failed Tasks0').waitFor(); await page.getByText(/Failed Tasks\d+/).waitFor();
await page.getByText('Pending Tasks0').waitFor(); await page.getByText(/Pending Tasks\d+/).waitFor();
// Expand the "scheduled tasks" view // Expand the "scheduled tasks" view
await page.getByRole('button', { name: 'Scheduled Tasks' }).click(); await page.getByRole('button', { name: 'Scheduled Tasks' }).click();
+79 -93
View File
@@ -874,7 +874,7 @@
dependencies: dependencies:
"@floating-ui/dom" "^1.7.6" "@floating-ui/dom" "^1.7.6"
"@floating-ui/react@^0.27.16": "@floating-ui/react@^0.27.19":
version "0.27.19" version "0.27.19"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.27.19.tgz#d8d5d895b7cb97dac370bfbf55f3e630878fdf1f" resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.27.19.tgz#d8d5d895b7cb97dac370bfbf55f3e630878fdf1f"
integrity sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog== integrity sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==
@@ -1169,84 +1169,84 @@
"@babel/runtime" "^7.20.13" "@babel/runtime" "^7.20.13"
"@lingui/core" "5.9.3" "@lingui/core" "5.9.3"
"@mantine/carousel@^8.2.7": "@mantine/carousel@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/carousel/-/carousel-8.3.18.tgz#55489c09de7a7ccb3b758d3386eef56db91cec4b" resolved "https://registry.yarnpkg.com/@mantine/carousel/-/carousel-9.2.1.tgz#bc470ae1c2801d66b40de9d1e76eee869995fb1c"
integrity sha512-jrXqpoiL+/GTTgW4/UWLqvo5pWnUEvuwaUlvD/Yl8XC+iyBiZHgzyq0erEMIRGertVvpet4vsiZjye7ycPHlfA== integrity sha512-b0ZBDnKhzztEFWNWbbFpPMqaV/4k/lclX8KB0PfU+qhu3Fa6wjS1VIn4XLZu9N3vgkzPkNXwDzpcwFShA9Uvqw==
"@mantine/charts@^8.2.7": "@mantine/charts@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/charts/-/charts-8.3.18.tgz#329db5b50c12cbda2be3609d359a068ad8ac2f47" resolved "https://registry.yarnpkg.com/@mantine/charts/-/charts-9.2.1.tgz#64811e07c1094b2b9bc67f04230e5dcebfe76be1"
integrity sha512-oudif3EUH7Nb9DPm0abAPxpFYDWWjR3k2S5ll0/CcB+pJzlhwaBG19QwpOJaRA6VAvAXDDKOXCO4mi9XCEN78g== integrity sha512-AVP0VEfcsbWwLBeU8rbEsN2gz3GebQECTreRq5AJ0Z5V1eWmTO7XFkRP9C5C6oHnkY4Vppa1x8cRYgyTsc/YbQ==
"@mantine/core@^8.2.7": "@mantine/core@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/core/-/core-8.3.18.tgz#25d206caef1110a50671139520b1434b581a62ca" resolved "https://registry.yarnpkg.com/@mantine/core/-/core-9.2.1.tgz#6bed4beac0b8c5f385f8923e4f158eb7472b633d"
integrity sha512-9tph1lTVogKPjTx02eUxDUOdXacPzK62UuSqb4TdGliI54/Xgxftq0Dfqu6XuhCxn9J5MDJaNiLDvL/1KRkYqA== integrity sha512-CicPg9i2dM2pGp1jj+dMiR/63OFDsPjgJke4v5+0nbfJ+C7gn4C+7ltrp4RIETDMZHcj0fFuDRG0qtbiyBxvWA==
dependencies: dependencies:
"@floating-ui/react" "^0.27.16" "@floating-ui/react" "^0.27.19"
clsx "^2.1.1" clsx "^2.1.1"
react-number-format "^5.4.4" react-number-format "^5.4.5"
react-remove-scroll "^2.7.1" react-remove-scroll "^2.7.2"
react-textarea-autosize "8.5.9" type-fest "^5.6.0"
type-fest "^4.41.0"
"@mantine/dates@^8.2.7": "@mantine/dates@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/dates/-/dates-8.3.18.tgz#e73b1847c4903eba20b80907162dbf93c99e4956" resolved "https://registry.yarnpkg.com/@mantine/dates/-/dates-9.2.1.tgz#47c9eb8a854a7f1e6fa3993e9966babb793e9e4a"
integrity sha512-FHx5teJOhupI0gO2o5evtVYQEdqOjayOkLRhEQfB5Nc5DvcysfPfmNILGkc1Nrp9ZQeQWKLT9qr+CkcCXwHOaw== integrity sha512-cyRC8bnZ6W+SzQf/RM+/eDeWhZTZF148z+OcgqiERdkAujh0q88Ddybv/Hshv1EOf0rCe6oiHSZWxIxmUf7KAg==
dependencies: dependencies:
clsx "^2.1.1" clsx "^2.1.1"
"@mantine/dropzone@^8.2.7": "@mantine/dropzone@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/dropzone/-/dropzone-8.3.18.tgz#abc9e4b35fc4c18041ba6f6f3b574935a1d6e03f" resolved "https://registry.yarnpkg.com/@mantine/dropzone/-/dropzone-9.2.1.tgz#3003d745da2f08246fc055137d250beb4f743283"
integrity sha512-GaYUUl/382R7hl1g6heTCZ5a6T5x6qYPg0oID6ik/J0j7e5+XMZyTH5ITpaqpsBQ09GKKsF5y3iNehpSby8Kew== integrity sha512-rgebEz2bUubqA5jQpsh0SZ9/4DJFnUSF+a4p8uzAVlgNfo6aU/r+I4lEsD8gzLKuxrfN0TgkwR1qmAjLWIy0yQ==
dependencies: dependencies:
react-dropzone "15.0.0" react-dropzone "15.0.0"
"@mantine/form@^8.2.7": "@mantine/form@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/form/-/form-8.3.18.tgz#c0e6ed7f564a69cbb157ed5d9925a5244057029c" resolved "https://registry.yarnpkg.com/@mantine/form/-/form-9.2.1.tgz#f75d82a16d82a1aa3105ff86328a032b6ff712bb"
integrity sha512-r5OGLJWTkmIruFjRZRZy9oA7maNYlyt50jB4Pmd2X5360WOmJLd4KH8MFhHZQC7vN+z8/rmBl3t3XGAR2I8xig== integrity sha512-PV0dcbmsKhZkn3Ryztqp8Kb1N16v2nWXO71p4utTmN7E/lHWHO1ccj7Y72sIteWJo1YeNHzzkssaYRSnxgOvYA==
dependencies: dependencies:
"@standard-schema/spec" "^1.1.0"
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
klona "^2.0.6" klona "^2.0.6"
"@mantine/hooks@^8.2.7": "@mantine/hooks@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-8.3.18.tgz#7e5601c0683ed2411e3aee8fcb81a151e5341ad2" resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-9.2.1.tgz#8f81303b5eab68b10f4b3d7c99795b70932bc2db"
integrity sha512-QoWr9+S8gg5050TQ06aTSxtlpGjYOpIllRbjYYXlRvZeTsUqiTbVfvQROLexu4rEaK+yy9Wwriwl9PMRgbLqPw== integrity sha512-IX/ztVG9eWmQTRsN7G8odyW4JckNvN8qv5A2ULzXyazjtAKLuaUpuMz0c6XhRp10J0g4bVfV3rhrTgWeImqxqg==
"@mantine/modals@^8.2.7": "@mantine/modals@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/modals/-/modals-8.3.18.tgz#e17a49253944bbb5de4142fd0d15cc944ca63fc2" resolved "https://registry.yarnpkg.com/@mantine/modals/-/modals-9.2.1.tgz#257c09088372b38848fb08beeb1124ed3fbe63b3"
integrity sha512-JfPDS4549L314SxFPC1x6CbKwzh82OdnIzwgMxPCVNsWLKV2vEHHUH/fzUYj4Wli6IBrsW4cufjMj9BTj3hm3Q== integrity sha512-YvZ85ZtMg6arFserkmmP18gJRD9ztLLT3vz8UrkwCdVwTGGr14X93hhS0UtR5X96ANXUzC8fU/S2ncQmNqw+NQ==
"@mantine/notifications@^8.2.7": "@mantine/notifications@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-8.3.18.tgz#50b9463afab3af0ea3de375dd92cf2fa60aed964" resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-9.2.1.tgz#de99ed4fc64e05024cb5a66e2d619a248ac2e042"
integrity sha512-IpQ0lmwbigTBbZCR6iSYWqIOKEx1tlcd7PcEJ5M5X1qeVSY/N3mmDQt1eJmObvcyDeL5cTJMbSA9UPqhRqo9jw== integrity sha512-H6lSsKUPdWPYcXFeOcqqcegUl72Iua9yD369s/gchfDvI+PrL4IM5UuWNHTeABJ1eb9FbTOw8B5RDSMZjTZNSA==
dependencies: dependencies:
"@mantine/store" "8.3.18" "@mantine/store" "9.2.1"
react-transition-group "4.4.5" react-transition-group "4.4.5"
"@mantine/spotlight@^8.2.7": "@mantine/spotlight@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/spotlight/-/spotlight-8.3.18.tgz#1747a6afdd2b0aab3cf03c364fbc7b3ea3c4ae4b" resolved "https://registry.yarnpkg.com/@mantine/spotlight/-/spotlight-9.2.1.tgz#4157cef5a9a1b9ac5f6025b57435a8dc484b58b9"
integrity sha512-yFoEYG0wKduxbnv6+1CUOXc91lmQ5DN4QvEShYO2ftDm0kXhxeOvJFtGOBYK80tpmSCsaT253p9E3J3DcaOt2w== integrity sha512-D6/BJ8/ftUBwiSFGTtYUM/LqlBk40XZqZFlmEFRM8chDPzEId1O+5FU7mj8KapPR9zRgoAcdT4LMI6JaLp0c0A==
dependencies: dependencies:
"@mantine/store" "8.3.18" "@mantine/store" "9.2.1"
"@mantine/store@8.3.18": "@mantine/store@9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/store/-/store-8.3.18.tgz#fda5e84bb58d4fa4a86cf4e158e166904867caf6" resolved "https://registry.yarnpkg.com/@mantine/store/-/store-9.2.1.tgz#27a3548c4cc1567baa2613490d7dcb9300b391ba"
integrity sha512-i+QRTLmZzLldea0egtUVnGALd6UMIu8jd44nrNWBSNIXJU/8B6rMlC6gyX+l4szopZSuOaaNJIXkqRdC1gQsVg== integrity sha512-sBTHt9ilfSZAeXQlqFkm8nRm22RunhevxuOUtdSwS9HhuMuS8T27dRRgbdKH2oEFUbaccdQSy5bHbmGbEgVO8w==
"@mantine/vanilla-extract@^8.2.7": "@mantine/vanilla-extract@^9.2.1":
version "8.3.18" version "9.2.1"
resolved "https://registry.yarnpkg.com/@mantine/vanilla-extract/-/vanilla-extract-8.3.18.tgz#ba7aaa0fd2ebf547821ef7138a8f12f4149b089d" resolved "https://registry.yarnpkg.com/@mantine/vanilla-extract/-/vanilla-extract-9.2.1.tgz#ba86032eaaf278153d3dd110c8e4327457cc0606"
integrity sha512-W7YwNJMeYJKsW+m1ntq+D0bt/b2GmGDWdHUxD+REjVlrXPhsKcb7nFX59ot/sgl35ak4yT2SFM1aPkUvgOQMCg== integrity sha512-5cNesX5kdmClWCMFqxDIVZF2JRCne2OrIj5UE3tuyGJAY3IGkchBnAf/bEx3c5QDSXxq4+6y97hs5gQBgODePA==
"@marijn/find-cluster-break@^1.0.0": "@marijn/find-cluster-break@^1.0.0":
version "1.0.2" version "1.0.2"
@@ -1799,7 +1799,7 @@
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.10.tgz#beefe675f1853f73676aecc915b2bd2ac98c4fc6" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.10.tgz#beefe675f1853f73676aecc915b2bd2ac98c4fc6"
integrity sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA== integrity sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==
"@standard-schema/spec@^1.0.0": "@standard-schema/spec@^1.0.0", "@standard-schema/spec@^1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8"
integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==
@@ -3811,15 +3811,15 @@ make-dir@^4.0.0:
dependencies: dependencies:
semver "^7.5.3" semver "^7.5.3"
mantine-contextmenu@^8.2.0: mantine-contextmenu@^9.2.1:
version "8.3.13" version "9.2.1"
resolved "https://registry.yarnpkg.com/mantine-contextmenu/-/mantine-contextmenu-8.3.13.tgz#f9194d806f29dae76dc5d6c63ab50c1e6b50d038" resolved "https://registry.yarnpkg.com/mantine-contextmenu/-/mantine-contextmenu-9.2.1.tgz#d1d4dab0ca1eeaba7f1bba6cf2cad8cf31636709"
integrity sha512-Idbgaou23M18NxCnPfC1A5l6ShS43UbYwJ4u5z6N30HRXXzUPe479/v50H/UfTZQUbBsrl+qnaYjogIvU3boog== integrity sha512-oqFPT9qOQBPgf2eSuiujGQp38QgV0WsIIMxQkPfkjVc8FWAKcnXv7OGxGN1ctRrcMqUbsS34rjI0SvNdmFwG8A==
mantine-datatable@^8.2.0: mantine-datatable@^9.2.0:
version "8.3.13" version "9.2.0"
resolved "https://registry.yarnpkg.com/mantine-datatable/-/mantine-datatable-8.3.13.tgz#7bbb2878a5ead7cb6e61996f0f03335718107d69" resolved "https://registry.yarnpkg.com/mantine-datatable/-/mantine-datatable-9.2.0.tgz#936f2307462d5420dc1ba7b520ae825dc6b2d97c"
integrity sha512-MQ7FNSyKCPeijiWHSeVmFU38m7MgHkomOm/VXm1XFpCAjVxdK8ZCVHGA2e5GK/dIkPX9Sdiwt812YDOhchPiHQ== integrity sha512-TK6SZ6dH/PQUedfhkJuSLMcd4P4m5L6kMJWfAF9cS4wBeoAtBWGpLs+n/E8yI6w1rYAtLsGQvFA6S9Xnfw0JFw==
marked@^4.1.0: marked@^4.1.0:
version "4.3.0" version "4.3.0"
@@ -4367,7 +4367,7 @@ react-is@^19.2.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135"
integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==
react-number-format@^5.4.4: react-number-format@^5.4.5:
version "5.4.5" version "5.4.5"
resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.4.5.tgz#5855f2ae4e4153e6231d3a36b635c38be6824c8a" resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.4.5.tgz#5855f2ae4e4153e6231d3a36b635c38be6824c8a"
integrity sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw== integrity sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw==
@@ -4393,7 +4393,7 @@ react-remove-scroll-bar@^2.3.7:
react-style-singleton "^2.2.2" react-style-singleton "^2.2.2"
tslib "^2.0.0" tslib "^2.0.0"
react-remove-scroll@^2.7.1: react-remove-scroll@^2.7.2:
version "2.7.2" version "2.7.2"
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz#6442da56791117661978ae99cd29be9026fecca0" resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz#6442da56791117661978ae99cd29be9026fecca0"
integrity sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q== integrity sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==
@@ -4457,15 +4457,6 @@ react-style-singleton@^2.2.2, react-style-singleton@^2.2.3:
get-nonce "^1.0.0" get-nonce "^1.0.0"
tslib "^2.0.0" tslib "^2.0.0"
react-textarea-autosize@8.5.9:
version "8.5.9"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz#ab8627b09aa04d8a2f45d5b5cd94c84d1d4a8893"
integrity sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==
dependencies:
"@babel/runtime" "^7.20.13"
use-composed-ref "^1.3.0"
use-latest "^1.2.1"
react-transition-group@4.4.5, react-transition-group@^4.3.0: react-transition-group@4.4.5, react-transition-group@^4.3.0:
version "4.4.5" version "4.4.5"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
@@ -4928,6 +4919,11 @@ tabbable@^6.0.0:
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.4.0.tgz#36eb7a06d80b3924a22095daf45740dea3bf5581" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.4.0.tgz#36eb7a06d80b3924a22095daf45740dea3bf5581"
integrity sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg== integrity sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==
tagged-tag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/tagged-tag/-/tagged-tag-1.0.0.tgz#a0b5917c2864cba54841495abfa3f6b13edcf4d6"
integrity sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==
test-exclude@^8.0.0: test-exclude@^8.0.0:
version "8.0.0" version "8.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-8.0.0.tgz#85891add3fa46bb822b1b1579c7f8c9a3d04ebd8" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-8.0.0.tgz#85891add3fa46bb822b1b1579c7f8c9a3d04ebd8"
@@ -4991,10 +4987,12 @@ type-fest@^0.8.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
type-fest@^4.41.0: type-fest@^5.6.0:
version "4.41.0" version "5.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-5.6.0.tgz#502f7a003b7309e96a7e17052cc2ab2c7e5c7a31"
integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== integrity sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==
dependencies:
tagged-tag "^1.0.0"
typedarray-to-buffer@^3.1.5: typedarray-to-buffer@^3.1.5:
version "3.1.5" version "3.1.5"
@@ -5066,23 +5064,11 @@ use-callback-ref@^1.3.3:
dependencies: dependencies:
tslib "^2.0.0" tslib "^2.0.0"
use-composed-ref@^1.3.0: use-isomorphic-layout-effect@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.4.0.tgz#09e023bf798d005286ad85cd20674bdf5770653b"
integrity sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==
use-isomorphic-layout-effect@^1.1.1, use-isomorphic-layout-effect@^1.2.0:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz#2f11a525628f56424521c748feabc2ffcc962fce" resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz#2f11a525628f56424521c748feabc2ffcc962fce"
integrity sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA== integrity sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==
use-latest@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.3.0.tgz#549b9b0d4c1761862072f0899c6f096eb379137a"
integrity sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==
dependencies:
use-isomorphic-layout-effect "^1.1.1"
use-sidecar@^1.1.3: use-sidecar@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb"