mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-03 11:28:49 +00:00
Merge commit '546958a1cb02125b96365e7fedda75610fed46ec' into block-notes
This commit is contained in:
+49
-49
@@ -49,16 +49,16 @@
|
||||
"@codemirror/state": "^6.6.0",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@codemirror/view": "^6.40.0",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.0.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^7.0.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.0",
|
||||
"@fortawesome/react-fontawesome": "^3.0.1",
|
||||
"@fullcalendar/core": "^6.1.15",
|
||||
"@fullcalendar/daygrid": "^6.1.15",
|
||||
"@fullcalendar/interaction": "^6.1.15",
|
||||
"@fullcalendar/react": "^6.1.15",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.2.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^7.2.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.2.0",
|
||||
"@fortawesome/react-fontawesome": "^3.3.1",
|
||||
"@fullcalendar/core": "^6.1.20",
|
||||
"@fullcalendar/daygrid": "^6.1.20",
|
||||
"@fullcalendar/interaction": "^6.1.20",
|
||||
"@fullcalendar/react": "^6.1.20",
|
||||
"@github/webauthn-json": "^2.1.1",
|
||||
"@lingui/core": "^5.9.2",
|
||||
"@lingui/react": "^5.9.2",
|
||||
@@ -76,9 +76,6 @@
|
||||
"@mantine/utils": "^6.0.22",
|
||||
"@mantine/vanilla-extract": "^9.2.1",
|
||||
"@messageformat/date-skeleton": "^1.1.0",
|
||||
"@sentry/react": "^10.43.0",
|
||||
"@tabler/icons-react": "^3.17.0",
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"@tiptap/core": "^3.23.6",
|
||||
"@tiptap/extension-image": "^3.23.6",
|
||||
"@tiptap/extension-link": "^3.23.6",
|
||||
@@ -86,68 +83,71 @@
|
||||
"@tiptap/pm": "^3.23.6",
|
||||
"@tiptap/react": "^3.23.6",
|
||||
"@tiptap/starter-kit": "^3.23.6",
|
||||
"@sentry/react": "^10.57.0",
|
||||
"@tabler/icons-react": "^3.44.0",
|
||||
"@tanstack/react-query": "^5.101.0",
|
||||
"@uiw/codemirror-theme-vscode": "^4.25.8",
|
||||
"@uiw/react-codemirror": "^4.25.8",
|
||||
"@uiw/react-split": "^5.9.4",
|
||||
"@vanilla-extract/css": "^1.18.0",
|
||||
"axios": "^1.13.6",
|
||||
"@vanilla-extract/css": "^1.20.1",
|
||||
"axios": "^1.17.0",
|
||||
"clsx": "^2.1.1",
|
||||
"codemirror": "^6.0.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"dompurify": "^3.2.4",
|
||||
"embla-carousel": "^8.5.2",
|
||||
"embla-carousel-react": "^8.5.2",
|
||||
"fuse.js": "^7.0.0",
|
||||
"dayjs": "^1.11.21",
|
||||
"dompurify": "^3.4.8",
|
||||
"embla-carousel": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"fuse.js": "^7.4.2",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"mantine-contextmenu": "^9.2.1",
|
||||
"mantine-datatable": "^9.2.0",
|
||||
"mantine-datatable": "^9.2.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-grid-layout": "1.4.4",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"react-is": "^19.2.4",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-select": "^5.9.0",
|
||||
"react-window": "1.8.11",
|
||||
"recharts": "^3.1.2",
|
||||
"styled-components": "^6.1.14",
|
||||
"tiptap-extension-resizable-image": "^2.1.0",
|
||||
"undici": "^6.24.0",
|
||||
"zustand": "^5.0.8"
|
||||
"react-hook-form": "^7.78.0",
|
||||
"react-is": "^19.2.7",
|
||||
"react-router-dom": "^6.30.4",
|
||||
"react-select": "^5.10.2",
|
||||
"react-window": "1.8.11",
|
||||
"recharts": "^3.8.1",
|
||||
"styled-components": "^6.4.2",
|
||||
"undici": "^8.4.1",
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
"@babel/preset-react": "^7.28.5",
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@codecov/vite-plugin": "^1.9.1",
|
||||
"@babel/core": "^7.29.7",
|
||||
"@babel/preset-react": "^7.29.7",
|
||||
"@babel/preset-typescript": "^7.29.7",
|
||||
"@babel/runtime": "^7.29.7",
|
||||
"@codecov/vite-plugin": "^2.0.1",
|
||||
"@flakiness/playwright": "^1.13.0",
|
||||
"@lingui/babel-plugin-lingui-macro": "^5.9.2",
|
||||
"@lingui/cli": "^5.9.2",
|
||||
"@lingui/macro": "^5.9.2",
|
||||
"@playwright/test": "^1.16.0",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^19.2.14",
|
||||
"@lingui/babel-plugin-lingui-macro": "^5.9.5",
|
||||
"@lingui/cli": "^5.9.5",
|
||||
"@lingui/macro": "^5.9.5",
|
||||
"@playwright/test": "^1.60.0",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-grid-layout": "^1.3.5",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@vanilla-extract/vite-plugin": "^5.1.4",
|
||||
"@vanilla-extract/vite-plugin": "^5.2.2",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"nyc": "^18.0.0",
|
||||
"otpauth": "^9.4.1",
|
||||
"otpauth": "^9.5.1",
|
||||
"path": "^0.12.7",
|
||||
"rollup": "^4.59.0",
|
||||
"rollup-plugin-license": "^3.7.0",
|
||||
"rollup": "^4.61.1",
|
||||
"rollup-plugin-license": "^3.7.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.4.2",
|
||||
"vite-plugin-babel-macros": "^1.0.6",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-dts": "^5.0.2",
|
||||
"vite-plugin-externals": "^0.6.2",
|
||||
"vite-plugin-istanbul": "^8.0.0"
|
||||
"vite-plugin-istanbul": "^9.0.1"
|
||||
},
|
||||
"overrides": {
|
||||
},
|
||||
|
||||
@@ -68,7 +68,7 @@ export function ApiFormField({
|
||||
: definition.value
|
||||
);
|
||||
}
|
||||
}, [definition.value]);
|
||||
}, [definition.value, definition.field_type]);
|
||||
|
||||
const fieldDefinition: ApiFormFieldType = useMemo(() => {
|
||||
return {
|
||||
|
||||
@@ -179,6 +179,55 @@ function ImporterDefaultField({
|
||||
);
|
||||
}
|
||||
|
||||
function ImporterLookupFieldSelector({
|
||||
column,
|
||||
session
|
||||
}: Readonly<{ column: any; session: ImportSessionState }>) {
|
||||
const api = useApi();
|
||||
|
||||
const fieldDef = session.availableFields[column.field];
|
||||
const lookupFields: string[] = fieldDef?.lookup_fields ?? [];
|
||||
|
||||
const [selected, setSelected] = useState<string>(column.lookup_field ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
setSelected(column.lookup_field ?? '');
|
||||
}, [column.lookup_field]);
|
||||
|
||||
if (lookupFields.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const options = [
|
||||
{ value: '', label: t`Auto` },
|
||||
...lookupFields.map((f: string) => ({ value: f, label: f }))
|
||||
];
|
||||
|
||||
const onChange = useCallback(
|
||||
(value: string | null) => {
|
||||
const next = value ?? '';
|
||||
api
|
||||
.patch(
|
||||
apiUrl(ApiEndpoints.import_session_column_mapping_list, column.pk),
|
||||
{ lookup_field: next || null }
|
||||
)
|
||||
.then(() => setSelected(next))
|
||||
.catch(() => {});
|
||||
},
|
||||
[column.pk]
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
aria-label={`import-lookup-field-${column.field}`}
|
||||
data={options}
|
||||
value={selected}
|
||||
onChange={onChange}
|
||||
size='sm'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ImporterColumnTableRow({
|
||||
session,
|
||||
column,
|
||||
@@ -210,6 +259,9 @@ function ImporterColumnTableRow({
|
||||
<Table.Td>
|
||||
<ImporterColumn column={column} options={options} />
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<ImporterLookupFieldSelector column={column} session={session} />
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<ImporterDefaultField
|
||||
fieldName={column.field}
|
||||
@@ -285,6 +337,7 @@ export default function ImporterColumnSelector({
|
||||
<Table.Th>{t`Database Field`}</Table.Th>
|
||||
<Table.Th>{t`Field Description`}</Table.Th>
|
||||
<Table.Th>{t`Imported Column`}</Table.Th>
|
||||
<Table.Th>{t`Lookup Field`}</Table.Th>
|
||||
<Table.Th>{t`Default Value`}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||
import { ModelType } from '@lib/enums/ModelType';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||
import type { ApiFormFieldSet, ApiFormFieldType } from '@lib/types/Forms';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import type {
|
||||
StatusCodeInterface,
|
||||
@@ -117,32 +117,44 @@ export function useParameterTemplateFields(): ApiFormFieldSet {
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function useParameterFields({
|
||||
modelType,
|
||||
modelId
|
||||
}: {
|
||||
modelType: ModelType;
|
||||
modelId: number;
|
||||
}): ApiFormFieldSet {
|
||||
/**
|
||||
* Shared hook for the dynamic "value" field on parameter forms.
|
||||
*
|
||||
* When the user selects a parameter template, the field type for the
|
||||
* corresponding value input (data / default_value) must change to match the
|
||||
* template's data type (boolean, choice, related-field selection list, or
|
||||
* plain string). This hook encapsulates that state so it can be reused
|
||||
* across the "Add Parameter" and "Add Category Parameter" forms.
|
||||
*
|
||||
* @param resetDep - When this value changes all internal state is reset to
|
||||
* defaults. Pass a stringified key derived from the form's context (e.g.
|
||||
* `${modelType}-${modelId}`) so the field resets when the context switches.
|
||||
*/
|
||||
export function useDynamicParameterValueField(resetDep?: any): {
|
||||
onTemplateValueChange: (value: any, record: any) => void;
|
||||
valueFieldConfig: ApiFormFieldType;
|
||||
reset: () => void;
|
||||
} {
|
||||
const api = useApi();
|
||||
|
||||
const user = useUserState.getState();
|
||||
|
||||
const templateCreateFields = useParameterTemplateFields();
|
||||
|
||||
const [selectionListId, setSelectionListId] = useState<number | null>(null);
|
||||
|
||||
// Valid field choices
|
||||
const [choices, setChoices] = useState<any[]>([]);
|
||||
|
||||
// Field type for "data" input
|
||||
const [fieldType, setFieldType] = useState<
|
||||
'string' | 'boolean' | 'choice' | 'related field'
|
||||
>('string');
|
||||
|
||||
// Memoized value for the "data" field
|
||||
const [data, setData] = useState<string>('');
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setSelectionListId(null);
|
||||
setFieldType('string');
|
||||
setChoices([]);
|
||||
setData('');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
reset();
|
||||
}, [resetDep, reset]);
|
||||
|
||||
const fetchSelectionEntry = useCallback(
|
||||
(value: any) => {
|
||||
if (!value || !selectionListId) {
|
||||
@@ -151,9 +163,7 @@ export function useParameterFields({
|
||||
|
||||
return api
|
||||
.get(apiUrl(ApiEndpoints.selectionentry_list, selectionListId), {
|
||||
params: {
|
||||
value: value
|
||||
}
|
||||
params: { value: value }
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data && response.data.length == 1) {
|
||||
@@ -166,13 +176,102 @@ export function useParameterFields({
|
||||
[selectionListId]
|
||||
);
|
||||
|
||||
// Reset the field type and choices when the model changes
|
||||
useEffect(() => {
|
||||
setSelectionListId(null);
|
||||
setFieldType('string');
|
||||
setChoices([]);
|
||||
setData('');
|
||||
}, [modelType, modelId]);
|
||||
const onTemplateValueChange = useCallback(
|
||||
(value: any, record: any) => {
|
||||
setSelectionListId(record?.selectionlist || null);
|
||||
setData('');
|
||||
|
||||
if (record?.checkbox) {
|
||||
setChoices([]);
|
||||
setFieldType('boolean');
|
||||
setData('false');
|
||||
} else if (record?.choices) {
|
||||
const _choices: string[] = record.choices.split(',');
|
||||
|
||||
if (_choices.length > 0) {
|
||||
setChoices(
|
||||
_choices.map((choice) => ({
|
||||
display_name: choice.trim(),
|
||||
value: choice.trim()
|
||||
}))
|
||||
);
|
||||
setFieldType('choice');
|
||||
} else {
|
||||
setChoices([]);
|
||||
setFieldType('string');
|
||||
setData('');
|
||||
}
|
||||
} else if (record?.selectionlist) {
|
||||
setFieldType('related field');
|
||||
setData('');
|
||||
} else {
|
||||
setFieldType('string');
|
||||
setData('');
|
||||
}
|
||||
},
|
||||
[setFieldType, setData, setChoices]
|
||||
);
|
||||
|
||||
const valueFieldConfig: ApiFormFieldType = useMemo(
|
||||
() => ({
|
||||
value: data,
|
||||
onValueChange: (value: any, record: any) => {
|
||||
if (fieldType === 'related field' && selectionListId) {
|
||||
// For related fields, store the primary key value (not the string representation)
|
||||
setData(record?.value ?? value);
|
||||
} else {
|
||||
setData(value);
|
||||
}
|
||||
},
|
||||
field_type: fieldType,
|
||||
choices: fieldType === 'choice' ? choices : undefined,
|
||||
default: fieldType === 'boolean' ? false : undefined,
|
||||
pk_field:
|
||||
fieldType === 'related field' && selectionListId ? 'value' : undefined,
|
||||
model:
|
||||
fieldType === 'related field' && selectionListId
|
||||
? ModelType.selectionentry
|
||||
: undefined,
|
||||
api_url:
|
||||
fieldType === 'related field' && selectionListId
|
||||
? apiUrl(ApiEndpoints.selectionentry_list, selectionListId)
|
||||
: undefined,
|
||||
filters: fieldType === 'related field' ? { active: true } : undefined,
|
||||
adjustValue: (value: any) => {
|
||||
let v: string = value.toString().trim();
|
||||
|
||||
if (fieldType === 'boolean') {
|
||||
if (v.toLowerCase() !== 'true') {
|
||||
v = 'false';
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
},
|
||||
singleFetchFunction: fetchSelectionEntry
|
||||
}),
|
||||
[data, fieldType, choices, selectionListId, fetchSelectionEntry]
|
||||
);
|
||||
|
||||
return { onTemplateValueChange, valueFieldConfig, reset };
|
||||
}
|
||||
|
||||
export function useParameterFields({
|
||||
modelType,
|
||||
modelId
|
||||
}: {
|
||||
modelType: ModelType;
|
||||
modelId: number;
|
||||
}): ApiFormFieldSet {
|
||||
const user = useUserState.getState();
|
||||
const templateCreateFields = useParameterTemplateFields();
|
||||
|
||||
const resetKey = useMemo(
|
||||
() => `${modelType}-${modelId}`,
|
||||
[modelType, modelId]
|
||||
);
|
||||
const { onTemplateValueChange, valueFieldConfig } =
|
||||
useDynamicParameterValueField(resetKey);
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
@@ -189,97 +288,17 @@ export function useParameterFields({
|
||||
for_model: modelType,
|
||||
enabled: true
|
||||
},
|
||||
onValueChange: (value: any, record: any) => {
|
||||
setSelectionListId(record?.selectionlist || null);
|
||||
|
||||
// Adjust the type of the "data" field based on the selected template
|
||||
if (record?.checkbox) {
|
||||
// This is a "checkbox" field
|
||||
setChoices([]);
|
||||
setFieldType('boolean');
|
||||
setData('false');
|
||||
} else if (record?.choices) {
|
||||
const _choices: string[] = record.choices.split(',');
|
||||
|
||||
if (_choices.length > 0) {
|
||||
setChoices(
|
||||
_choices.map((choice) => {
|
||||
return {
|
||||
display_name: choice.trim(),
|
||||
value: choice.trim()
|
||||
};
|
||||
})
|
||||
);
|
||||
setFieldType('choice');
|
||||
} else {
|
||||
setChoices([]);
|
||||
setFieldType('string');
|
||||
}
|
||||
} else if (record?.selectionlist) {
|
||||
setFieldType('related field');
|
||||
} else {
|
||||
// Default to a simple string field
|
||||
setFieldType('string');
|
||||
}
|
||||
},
|
||||
onValueChange: onTemplateValueChange,
|
||||
addCreateFields: user.isStaff() ? templateCreateFields : undefined
|
||||
},
|
||||
data: {
|
||||
value: data,
|
||||
onValueChange: (value: any, record: any) => {
|
||||
if (fieldType === 'related field' && selectionListId) {
|
||||
// For related fields, we need to store the selected primary key value (not the string representation)
|
||||
setData(record?.value ?? value);
|
||||
} else {
|
||||
setData(value);
|
||||
}
|
||||
},
|
||||
type: fieldType,
|
||||
field_type: fieldType,
|
||||
choices: fieldType === 'choice' ? choices : undefined,
|
||||
default: fieldType === 'boolean' ? false : undefined,
|
||||
pk_field:
|
||||
fieldType === 'related field' && selectionListId
|
||||
? 'value'
|
||||
: undefined,
|
||||
model:
|
||||
fieldType === 'related field' && selectionListId
|
||||
? ModelType.selectionentry
|
||||
: undefined,
|
||||
api_url:
|
||||
fieldType === 'related field' && selectionListId
|
||||
? apiUrl(ApiEndpoints.selectionentry_list, selectionListId)
|
||||
: undefined,
|
||||
filters:
|
||||
fieldType === 'related field'
|
||||
? {
|
||||
active: true
|
||||
}
|
||||
: undefined,
|
||||
adjustValue: (value: any) => {
|
||||
// Coerce boolean value into a string (required by backend)
|
||||
|
||||
let v: string = value.toString().trim();
|
||||
|
||||
if (fieldType === 'boolean') {
|
||||
if (v.toLowerCase() !== 'true') {
|
||||
v = 'false';
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
},
|
||||
singleFetchFunction: fetchSelectionEntry
|
||||
},
|
||||
data: valueFieldConfig,
|
||||
note: {}
|
||||
};
|
||||
}, [
|
||||
data,
|
||||
modelType,
|
||||
fieldType,
|
||||
choices,
|
||||
modelId,
|
||||
selectionListId,
|
||||
onTemplateValueChange,
|
||||
valueFieldConfig,
|
||||
templateCreateFields,
|
||||
user
|
||||
]);
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { TableFilter } from '@lib/types/Filters';
|
||||
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||
import type { TableColumn } from '@lib/types/Tables';
|
||||
import { IconInfoCircle } from '@tabler/icons-react';
|
||||
import { useDynamicParameterValueField } from '../../forms/CommonForms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
@@ -32,16 +33,21 @@ export default function PartCategoryTemplateTable({
|
||||
const table = useTable('part-category-parameter-templates');
|
||||
const user = useUserState();
|
||||
|
||||
const { onTemplateValueChange, valueFieldConfig, reset } =
|
||||
useDynamicParameterValueField(categoryId);
|
||||
|
||||
const formFields: ApiFormFieldSet = useMemo(() => {
|
||||
return {
|
||||
category: {
|
||||
value: categoryId,
|
||||
disabled: categoryId !== undefined
|
||||
},
|
||||
template: {},
|
||||
default_value: {}
|
||||
template: {
|
||||
onValueChange: onTemplateValueChange
|
||||
},
|
||||
default_value: valueFieldConfig
|
||||
};
|
||||
}, [categoryId]);
|
||||
}, [categoryId, onTemplateValueChange, valueFieldConfig]);
|
||||
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<number>(0);
|
||||
|
||||
@@ -49,6 +55,7 @@ export default function PartCategoryTemplateTable({
|
||||
url: ApiEndpoints.category_parameter_list,
|
||||
title: t`Add Category Parameter`,
|
||||
fields: useMemo(() => ({ ...formFields }), [formFields]),
|
||||
onOpen: reset,
|
||||
table: table
|
||||
});
|
||||
|
||||
@@ -57,6 +64,7 @@ export default function PartCategoryTemplateTable({
|
||||
pk: selectedTemplate,
|
||||
title: t`Edit Category Parameter`,
|
||||
fields: useMemo(() => ({ ...formFields }), [formFields]),
|
||||
onOpen: reset,
|
||||
table: table
|
||||
});
|
||||
|
||||
|
||||
@@ -98,11 +98,13 @@ export default function PluginListTable() {
|
||||
BooleanColumn({
|
||||
accessor: 'is_builtin',
|
||||
sortable: false,
|
||||
filter: 'builtin',
|
||||
title: t`Builtin`
|
||||
}),
|
||||
BooleanColumn({
|
||||
accessor: 'is_mandatory',
|
||||
sortable: false,
|
||||
filter: 'mandatory',
|
||||
title: t`Mandatory`
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -123,6 +123,7 @@ export const test = baseTest.extend<{}, {}>({
|
||||
msg.text() !=
|
||||
'Failed to load resource: the server responded with a status of 400 (Bad Request)' &&
|
||||
!msg.text().includes('/this/does/not/exist.js') &&
|
||||
!msg.text().includes('Error removing trace ID:') &&
|
||||
!url.includes('/this/does/not/exist.js') &&
|
||||
!url.includes('/api/user/me/') &&
|
||||
!url.includes('/api/user/me/token/') &&
|
||||
|
||||
@@ -903,4 +903,16 @@ test('Build Order - BOM Quantity', async ({ browser }) => {
|
||||
.locator('div');
|
||||
const row2 = await getRowFromCell(line);
|
||||
await row2.getByText('1,175').first().waitFor();
|
||||
|
||||
// Test table filtering against the "Required Parts" table
|
||||
await clearTableFilters(page);
|
||||
await page.getByText('1 - 7 / 7').waitFor();
|
||||
|
||||
// Filter by "available" stock
|
||||
await setTableChoiceFilter(page, 'Available', 'Yes');
|
||||
await page.getByText('1 - 3 / 3').waitFor();
|
||||
|
||||
await clearTableFilters(page);
|
||||
await setTableChoiceFilter(page, 'Available', 'No');
|
||||
await page.getByText('1 - 4 / 4').waitFor();
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ test('Forms - Hover', async ({ browser }) => {
|
||||
await page.getByLabel('related-field-supplier').fill('mou');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(250);
|
||||
await page.getByRole('option', { name: 'Mouser Electronics' }).hover();
|
||||
await page.getByText('Mouser Electronics').first().hover();
|
||||
|
||||
// Check for hover info
|
||||
await page.getByText('Company[ID: 2]').waitFor();
|
||||
|
||||
@@ -60,7 +60,7 @@ test('Importing - Admin Center', async ({ browser }) => {
|
||||
await page.getByRole('button', { name: 'Close' }).click();
|
||||
|
||||
// Confirmation of full import success
|
||||
await page.getByRole('cell', { name: '3 / 3' }).first().waitFor();
|
||||
// await page.getByRole('cell', { name: '3 / 3' }).first().waitFor();
|
||||
|
||||
// Manually delete records
|
||||
await page.getByRole('checkbox', { name: 'Select all records' }).check();
|
||||
|
||||
+335
-604
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user