From 1ef9512f188914a2b00313f4c08be8662b14cd81 Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Tue, 30 Apr 2024 01:59:16 +0200 Subject: [PATCH 1/3] [PUI] Add more table tests (#7143) * expand part tests * add stock item tests --- src/frontend/tests/pui_general.spec.ts | 11 ++++++++++- src/frontend/tests/pui_stock.spec.ts | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/frontend/tests/pui_general.spec.ts b/src/frontend/tests/pui_general.spec.ts index eb688a37ba..97c8df588e 100644 --- a/src/frontend/tests/pui_general.spec.ts +++ b/src/frontend/tests/pui_general.spec.ts @@ -27,10 +27,19 @@ test('PUI - Parts', async ({ page }) => { await page.getByText('1551ACLR').click(); await page.getByRole('tab', { name: 'Part Details' }).click(); await page.getByRole('tab', { name: 'Parameters' }).click(); - // await page.getByRole('tab', { name: 'Stock' }).click(); + await page + .getByRole('tab', { name: 'Part Details' }) + .locator('xpath=..') + .getByRole('tab', { name: 'Stock', exact: true }) + .click(); await page.getByRole('tab', { name: 'Allocations' }).click(); await page.getByRole('tab', { name: 'Used In' }).click(); await page.getByRole('tab', { name: 'Pricing' }).click(); + + await page.goto(`${baseUrl}/part/category/index/parts`); + await page.getByText('Blue Chair').click(); + await page.getByRole('tab', { name: 'Bill of Materials' }).click(); + await page.getByRole('tab', { name: 'Build Orders' }).click(); }); test('PUI - Parts - Manufacturer Parts', async ({ page }) => { diff --git a/src/frontend/tests/pui_stock.spec.ts b/src/frontend/tests/pui_stock.spec.ts index cf6ea247f5..976eed1d49 100644 --- a/src/frontend/tests/pui_stock.spec.ts +++ b/src/frontend/tests/pui_stock.spec.ts @@ -16,6 +16,14 @@ test('PUI - Stock', async ({ page }) => { await page.getByRole('tab', { name: 'Stock Locations' }).click(); await page.getByRole('tab', { name: 'Stock Items' }).click(); await page.getByRole('tab', { name: 'Location Details' }).click(); + + await page.goto(`${baseUrl}/stock/item/1194/details`); + await page.getByText('D.123 | Doohickey').waitFor(); + await page.getByText('Batch Code: BX-123-2024-2-7').waitFor(); + await page.getByRole('tab', { name: 'Stock Tracking' }).click(); + await page.getByRole('tab', { name: 'Test Data' }).click(); + await page.getByText('395c6d5586e5fb656901d047be27e1f7').waitFor(); + await page.getByRole('tab', { name: 'Installed Items' }).click(); }); test('PUI - Build', async ({ page }) => { From a9b932cc320946b17ac6f9e1e503834386e04d22 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 30 Apr 2024 16:21:38 +1000 Subject: [PATCH 2/3] PUI tweaks (#7144) * Default progress bars a bit thicker * Implement useFilters hook - Adds "project code" filter for order tables * Add "responsible" filters to backend * Add more filters to tables * Bump API version * Typo fix * Tweak PartTable * Tweaks * remove unused imports --- .../InvenTree/InvenTree/api_version.py | 5 +- src/backend/InvenTree/order/api.py | 4 + src/backend/InvenTree/order/serializers.py | 12 ++- .../src/components/items/ProgressBar.tsx | 5 +- src/frontend/src/hooks/UseFilter.tsx | 91 +++++++++++++++++++ .../src/tables/build/BuildOrderTable.tsx | 47 ++++++++-- .../src/tables/build/BuildOutputTable.tsx | 16 ++-- src/frontend/src/tables/part/PartTable.tsx | 10 +- .../tables/purchasing/PurchaseOrderTable.tsx | 27 +++++- .../src/tables/sales/ReturnOrderTable.tsx | 25 ++++- .../src/tables/sales/SalesOrderTable.tsx | 27 +++++- .../src/tables/stock/StockItemTable.tsx | 2 +- 12 files changed, 226 insertions(+), 45 deletions(-) create mode 100644 src/frontend/src/hooks/UseFilter.tsx diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 5f43e0305c..0d405b6de0 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 192 +INVENTREE_API_VERSION = 193 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v193 - 2024-04-30 : https://github.com/inventree/InvenTree/pull/7144 + - Adds "assigned_to" filter to PurchaseOrder / SalesOrder / ReturnOrder API endpoints + v192 - 2024-04-23 : https://github.com/inventree/InvenTree/pull/7106 - Adds 'trackable' ordering option to BuildLineLabel API endpoint diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index 1d6692793e..9ee4df9974 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -148,6 +148,10 @@ class OrderFilter(rest_filters.FilterSet): return queryset.exclude(project_code=None) return queryset.filter(project_code=None) + assigned_to = rest_filters.ModelChoiceFilter( + queryset=Owner.objects.all(), field_name='responsible' + ) + class LineItemFilter(rest_filters.FilterSet): """Base class for custom API filters for order line item list(s).""" diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index 42f6a3c69e..a1da09f21b 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -77,16 +77,18 @@ class AbstractOrderSerializer(serializers.Serializer): """Abstract serializer class which provides fields common to all order types.""" # Number of line items in this order - line_items = serializers.IntegerField(read_only=True) + line_items = serializers.IntegerField(read_only=True, label=_('Line Items')) # Number of completed line items (this is an annotated field) - completed_lines = serializers.IntegerField(read_only=True) + completed_lines = serializers.IntegerField( + read_only=True, label=_('Completed Lines') + ) # Human-readable status text (read-only) status_text = serializers.CharField(source='get_status_display', read_only=True) # status field cannot be set directly - status = serializers.IntegerField(read_only=True) + status = serializers.IntegerField(read_only=True, label=_('Order Status')) # Reference string is *required* reference = serializers.CharField(required=True) @@ -114,7 +116,9 @@ class AbstractOrderSerializer(serializers.Serializer): barcode_hash = serializers.CharField(read_only=True) - creation_date = serializers.DateField(required=False, allow_null=True) + creation_date = serializers.DateField( + required=False, allow_null=True, label=_('Creation Date') + ) def validate_reference(self, reference): """Custom validation for the reference field.""" diff --git a/src/frontend/src/components/items/ProgressBar.tsx b/src/frontend/src/components/items/ProgressBar.tsx index b2938cfdeb..2369da7faa 100644 --- a/src/frontend/src/components/items/ProgressBar.tsx +++ b/src/frontend/src/components/items/ProgressBar.tsx @@ -6,6 +6,7 @@ export type ProgressBarProps = { maximum?: number; label?: string; progressLabel?: boolean; + size?: string; }; /** @@ -31,8 +32,8 @@ export function ProgressBar(props: ProgressBarProps) { 100 ? 'blue' : 'green'} - size="sm" - radius="xs" + size={props.size ?? 'md'} + radius="sm" /> ); diff --git a/src/frontend/src/hooks/UseFilter.tsx b/src/frontend/src/hooks/UseFilter.tsx new file mode 100644 index 0000000000..9d36a6e426 --- /dev/null +++ b/src/frontend/src/hooks/UseFilter.tsx @@ -0,0 +1,91 @@ +/* + * Custom hook for retrieving a list of items from the API, + * and turning them into "filters" for use in the frontend table framework. + */ +import { useQuery } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; + +import { api } from '../App'; +import { ApiEndpoints } from '../enums/ApiEndpoints'; +import { resolveItem } from '../functions/conversion'; +import { apiUrl } from '../states/ApiState'; +import { TableFilterChoice } from '../tables/Filter'; + +type UseFilterProps = { + url: string; + method?: 'GET' | 'POST' | 'OPTIONS'; + params?: any; + accessor?: string; + transform: (item: any) => TableFilterChoice; +}; + +export function useFilters(props: UseFilterProps) { + const query = useQuery({ + enabled: true, + queryKey: [props.url, props.method, props.params], + queryFn: async () => { + return await api + .request({ + url: props.url, + method: props.method || 'GET', + params: props.params + }) + .then((response) => { + let data = resolveItem(response, props.accessor ?? 'data'); + + if (data == null || data == undefined) { + return []; + } + + return data; + }) + .catch((error) => []); + } + }); + + const choices: TableFilterChoice[] = useMemo(() => { + return query.data?.map(props.transform) ?? []; + }, [props.transform, query.data]); + + const refresh = useCallback(() => { + query.refetch(); + }, []); + + return { + choices, + refresh + }; +} + +// Provide list of project code filters +export function useProjectCodeFilters() { + return useFilters({ + url: apiUrl(ApiEndpoints.project_code_list), + transform: (item) => ({ + value: item.pk, + label: item.code + }) + }); +} + +// Provide list of user filters +export function useUserFilters() { + return useFilters({ + url: apiUrl(ApiEndpoints.user_list), + transform: (item) => ({ + value: item.pk, + label: item.username + }) + }); +} + +// Provide list of owner filters +export function useOwnerFilters() { + return useFilters({ + url: apiUrl(ApiEndpoints.owner_list), + transform: (item) => ({ + value: item.pk, + label: item.name + }) + }); +} diff --git a/src/frontend/src/tables/build/BuildOrderTable.tsx b/src/frontend/src/tables/build/BuildOrderTable.tsx index 34ff1c14a5..c5e730b550 100644 --- a/src/frontend/src/tables/build/BuildOrderTable.tsx +++ b/src/frontend/src/tables/build/BuildOrderTable.tsx @@ -10,6 +10,11 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { useBuildOrderFields } from '../../forms/BuildForms'; +import { + useOwnerFilters, + useProjectCodeFilters, + useUserFilters +} from '../../hooks/UseFilter'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; @@ -92,6 +97,10 @@ export function BuildOrderTable({ }) { const tableColumns = useMemo(() => buildOrderTableColumns(), []); + const projectCodeFilters = useProjectCodeFilters(); + const userFilters = useUserFilters(); + const responsibleFilters = useOwnerFilters(); + const tableFilters: TableFilter[] = useMemo(() => { return [ { @@ -115,18 +124,36 @@ export function BuildOrderTable({ type: 'boolean', label: t`Assigned to me`, description: t`Show orders assigned to me` + }, + { + name: 'project_code', + label: t`Project Code`, + description: t`Filter by project code`, + choices: projectCodeFilters.choices + }, + { + name: 'has_project_code', + label: t`Has Project Code`, + description: t`Filter by whether the purchase order has a project code` + }, + { + name: 'issued_by', + label: t`Issued By`, + description: t`Filter by user who issued this order`, + choices: userFilters.choices + }, + { + name: 'assigned_to', + label: t`Responsible`, + description: t`Filter by responsible owner`, + choices: responsibleFilters.choices } - // TODO: 'assigned to' filter - // TODO: 'issued by' filter - // { - // name: 'has_project_code', - // title: t`Has Project Code`, - // description: t`Show orders with project code`, - // } - // TODO: 'has project code' filter (see table_filters.js) - // TODO: 'project code' filter (see table_filters.js) ]; - }, []); + }, [ + projectCodeFilters.choices, + userFilters.choices, + responsibleFilters.choices + ]); const user = useUserState(); diff --git a/src/frontend/src/tables/build/BuildOutputTable.tsx b/src/frontend/src/tables/build/BuildOutputTable.tsx index dec3deac37..31ccdd8238 100644 --- a/src/frontend/src/tables/build/BuildOutputTable.tsx +++ b/src/frontend/src/tables/build/BuildOutputTable.tsx @@ -116,13 +116,13 @@ export default function BuildOutputTable({ />, } + icon={} color="red" disabled={!table.hasSelectedRecords} />, } + icon={} color="red" disabled={!table.hasSelectedRecords} /> @@ -153,14 +153,14 @@ export default function BuildOutputTable({ { title: t`Scrap`, tooltip: t`Scrap build output`, - color: 'red', - icon: + icon: , + color: 'red' }, { - title: t`Delete`, - tooltip: t`Delete build output`, - color: 'red', - icon: + title: t`Cancel`, + tooltip: t`Cancel build output`, + icon: , + color: 'red' } ]; diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx index 8407aadf2d..0c2151527b 100644 --- a/src/frontend/src/tables/part/PartTable.tsx +++ b/src/frontend/src/tables/part/PartTable.tsx @@ -1,7 +1,6 @@ import { t } from '@lingui/macro'; import { Group, Text } from '@mantine/core'; import { ReactNode, useMemo } from 'react'; -import { useNavigate } from 'react-router-dom'; import { AddItemButton } from '../../components/buttons/AddItemButton'; import { formatPriceRange } from '../../defaults/formatters'; @@ -9,7 +8,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { usePartFields } from '../../forms/PartForms'; -import { shortenString } from '../../functions/tables'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; @@ -43,13 +41,7 @@ function partTableColumns(): TableColumn[] { { accessor: 'category', sortable: true, - - render: function (record: any) { - // TODO: Link to the category detail page - return shortenString({ - str: record.category_detail?.pathstring - }); - } + render: (record: any) => record.category_detail?.pathstring }, { accessor: 'total_in_stock', diff --git a/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx b/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx index dd0df11d7f..845cf77f5c 100644 --- a/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx +++ b/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx @@ -8,6 +8,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms'; +import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; @@ -44,6 +45,9 @@ export function PurchaseOrderTable({ const table = useTable('purchase-order'); const user = useUserState(); + const projectCodeFilters = useProjectCodeFilters(); + const responsibleFilters = useOwnerFilters(); + const tableFilters: TableFilter[] = useMemo(() => { return [ { @@ -54,11 +58,26 @@ export function PurchaseOrderTable({ }, OutstandingFilter(), OverdueFilter(), - AssignedToMeFilter() - // TODO: has_project_code - // TODO: project_code + AssignedToMeFilter(), + { + name: 'project_code', + label: t`Project Code`, + description: t`Filter by project code`, + choices: projectCodeFilters.choices + }, + { + name: 'has_project_code', + label: t`Has Project Code`, + description: t`Filter by whether the purchase order has a project code` + }, + { + name: 'assigned_to', + label: t`Responsible`, + description: t`Filter by responsible owner`, + choices: responsibleFilters.choices + } ]; - }, []); + }, [projectCodeFilters.choices, responsibleFilters.choices]); const tableColumns = useMemo(() => { return [ diff --git a/src/frontend/src/tables/sales/ReturnOrderTable.tsx b/src/frontend/src/tables/sales/ReturnOrderTable.tsx index 73bcbfed3f..b4f77d1413 100644 --- a/src/frontend/src/tables/sales/ReturnOrderTable.tsx +++ b/src/frontend/src/tables/sales/ReturnOrderTable.tsx @@ -8,6 +8,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { useReturnOrderFields } from '../../forms/SalesOrderForms'; +import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; @@ -35,6 +36,9 @@ export function ReturnOrderTable({ params }: { params?: any }) { const table = useTable('return-orders'); const user = useUserState(); + const projectCodeFilters = useProjectCodeFilters(); + const responsibleFilters = useOwnerFilters(); + const tableFilters: TableFilter[] = useMemo(() => { return [ { @@ -45,9 +49,26 @@ export function ReturnOrderTable({ params }: { params?: any }) { }, OutstandingFilter(), OverdueFilter(), - AssignedToMeFilter() + AssignedToMeFilter(), + { + name: 'project_code', + label: t`Project Code`, + description: t`Filter by project code`, + choices: projectCodeFilters.choices + }, + { + name: 'has_project_code', + label: t`Has Project Code`, + description: t`Filter by whether the purchase order has a project code` + }, + { + name: 'assigned_to', + label: t`Responsible`, + description: t`Filter by responsible owner`, + choices: responsibleFilters.choices + } ]; - }, []); + }, [projectCodeFilters.choices, responsibleFilters.choices]); const tableColumns = useMemo(() => { return [ diff --git a/src/frontend/src/tables/sales/SalesOrderTable.tsx b/src/frontend/src/tables/sales/SalesOrderTable.tsx index c3556e6278..d48f72a4b9 100644 --- a/src/frontend/src/tables/sales/SalesOrderTable.tsx +++ b/src/frontend/src/tables/sales/SalesOrderTable.tsx @@ -8,6 +8,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { useSalesOrderFields } from '../../forms/SalesOrderForms'; +import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; @@ -41,6 +42,9 @@ export function SalesOrderTable({ const table = useTable('sales-order'); const user = useUserState(); + const projectCodeFilters = useProjectCodeFilters(); + const responsibleFilters = useOwnerFilters(); + const tableFilters: TableFilter[] = useMemo(() => { return [ { @@ -51,11 +55,26 @@ export function SalesOrderTable({ }, OutstandingFilter(), OverdueFilter(), - AssignedToMeFilter() - // TODO: has_project_code - // TODO: project_code + AssignedToMeFilter(), + { + name: 'project_code', + label: t`Project Code`, + description: t`Filter by project code`, + choices: projectCodeFilters.choices + }, + { + name: 'has_project_code', + label: t`Has Project Code`, + description: t`Filter by whether the purchase order has a project code` + }, + { + name: 'assigned_to', + label: t`Responsible`, + description: t`Filter by responsible owner`, + choices: responsibleFilters.choices + } ]; - }, []); + }, [projectCodeFilters.choices, responsibleFilters.choices]); const salesOrderFields = useSalesOrderFields(); diff --git a/src/frontend/src/tables/stock/StockItemTable.tsx b/src/frontend/src/tables/stock/StockItemTable.tsx index 818620e217..b3c2a84195 100644 --- a/src/frontend/src/tables/stock/StockItemTable.tsx +++ b/src/frontend/src/tables/stock/StockItemTable.tsx @@ -4,7 +4,7 @@ import { ReactNode, useMemo } from 'react'; import { AddItemButton } from '../../components/buttons/AddItemButton'; import { ActionDropdown } from '../../components/items/ActionDropdown'; -import { formatCurrency, renderDate } from '../../defaults/formatters'; +import { formatCurrency } from '../../defaults/formatters'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; From 7e9d2f79ab247f459b483922a553744bb9bf69cd Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Tue, 30 Apr 2024 11:24:52 +0200 Subject: [PATCH 3/3] [PUI] More style fixes (#7142) * fix interface names * use Readonly for props * fix typescript:S3863 * require hashes in package installer too --- contrib/packager.io/functions.sh | 2 +- .../src/components/buttons/AddItemButton.tsx | 2 +- .../src/components/buttons/SplitButton.tsx | 2 +- .../src/components/details/Details.tsx | 10 ++++----- .../src/components/details/DetailsBadge.tsx | 2 +- .../src/components/details/DetailsImage.tsx | 2 +- .../editors/TemplateEditor/TemplateEditor.tsx | 2 +- .../components/forms/fields/ApiFormField.tsx | 3 +-- .../components/forms/fields/ChoiceField.tsx | 3 +-- .../forms/fields/RelatedModelField.tsx | 3 +-- .../src/components/images/Thumbnail.tsx | 4 +--- src/frontend/src/components/items/DocInfo.tsx | 7 +++++- .../src/components/items/DocTooltip.tsx | 2 +- .../src/components/items/ProgressBar.tsx | 2 +- .../src/components/items/TitleWithDoc.tsx | 2 +- .../src/components/nav/DetailDrawer.tsx | 4 ++-- .../src/components/nav/NotificationDrawer.tsx | 7 +++--- .../src/components/nav/PageDetail.tsx | 22 ++++++++++--------- .../src/components/nav/PanelGroup.tsx | 13 ++++++----- .../src/components/nav/SearchDrawer.tsx | 2 +- .../src/components/nav/SettingsHeader.tsx | 18 ++++++++------- src/frontend/src/components/render/Build.tsx | 10 ++++++--- .../src/components/render/Company.tsx | 22 ++++++++++++------- .../src/components/render/Generic.tsx | 6 +++-- .../src/components/render/Instance.tsx | 9 +++++--- src/frontend/src/components/render/Order.tsx | 14 +++++++----- src/frontend/src/components/render/Part.tsx | 10 ++++++--- .../src/components/render/StatusRenderer.tsx | 6 ++--- src/frontend/src/components/render/Stock.tsx | 10 ++++----- src/frontend/src/components/render/User.tsx | 10 ++++++--- .../src/components/widgets/MarkdownEditor.tsx | 3 +-- .../src/components/widgets/WidgetLayout.tsx | 3 ++- src/frontend/src/defaults/formatters.tsx | 17 ++++++++------ src/frontend/src/functions/icons.tsx | 2 +- src/frontend/src/pages/Index/Playground.tsx | 12 +++++++--- src/frontend/src/pages/Index/Scan.tsx | 15 +++++++------ .../src/pages/company/CompanyDetail.tsx | 5 ++--- src/frontend/src/tables/ColumnSelect.tsx | 3 +-- src/frontend/src/tables/DownloadAction.tsx | 3 +-- src/frontend/src/tables/InvenTreeTable.tsx | 11 +++++++--- src/frontend/src/tables/RowActions.tsx | 5 ++--- .../src/tables/general/AttachmentTable.tsx | 3 +-- .../src/tables/part/PartThumbTable.tsx | 6 ++++- .../src/tables/plugin/PluginListTable.tsx | 2 +- 44 files changed, 174 insertions(+), 127 deletions(-) diff --git a/contrib/packager.io/functions.sh b/contrib/packager.io/functions.sh index d150ed75b8..8f7f47cc66 100755 --- a/contrib/packager.io/functions.sh +++ b/contrib/packager.io/functions.sh @@ -90,7 +90,7 @@ function detect_envs() { echo "# Using existing config file: ${INVENTREE_CONFIG_FILE}" # Install parser - pip install -r ${APP_HOME}/.github/requirements.txt -q + pip install --require-hashes -r ${APP_HOME}/.github/requirements.txt -q # Load config local CONF=$(cat ${INVENTREE_CONFIG_FILE} | jc --yaml) diff --git a/src/frontend/src/components/buttons/AddItemButton.tsx b/src/frontend/src/components/buttons/AddItemButton.tsx index a9af3de8f5..adece8195f 100644 --- a/src/frontend/src/components/buttons/AddItemButton.tsx +++ b/src/frontend/src/components/buttons/AddItemButton.tsx @@ -5,6 +5,6 @@ import { ActionButton, ActionButtonProps } from './ActionButton'; /** * A generic icon button which is used to add or create a new item */ -export function AddItemButton(props: ActionButtonProps) { +export function AddItemButton(props: Readonly) { return } />; } diff --git a/src/frontend/src/components/buttons/SplitButton.tsx b/src/frontend/src/components/buttons/SplitButton.tsx index a156e28f3b..1037cbd219 100644 --- a/src/frontend/src/components/buttons/SplitButton.tsx +++ b/src/frontend/src/components/buttons/SplitButton.tsx @@ -52,7 +52,7 @@ export function SplitButton({ selected, setSelected, loading -}: SplitButtonProps) { +}: Readonly) { const [current, setCurrent] = useState(defaultSelected); const { classes } = useStyles(); diff --git a/src/frontend/src/components/details/Details.tsx b/src/frontend/src/components/details/Details.tsx index fddfce0e7d..ab9872afdd 100644 --- a/src/frontend/src/components/details/Details.tsx +++ b/src/frontend/src/components/details/Details.tsx @@ -180,7 +180,7 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) { * If owner is defined, only renders a badge * If user is defined, a badge is rendered in addition to main value */ -function TableStringValue(props: FieldProps) { +function TableStringValue(props: Readonly) { let value = props?.field_value; if (value === undefined) { @@ -217,11 +217,11 @@ function TableStringValue(props: FieldProps) { ); } -function BooleanValue(props: FieldProps) { +function BooleanValue(props: Readonly) { return ; } -function TableAnchorValue(props: FieldProps) { +function TableAnchorValue(props: Readonly) { if (props.field_data.external) { return ( ) { return ( ) { return ( ); diff --git a/src/frontend/src/components/details/DetailsBadge.tsx b/src/frontend/src/components/details/DetailsBadge.tsx index 9aeeef67f7..8265955896 100644 --- a/src/frontend/src/components/details/DetailsBadge.tsx +++ b/src/frontend/src/components/details/DetailsBadge.tsx @@ -7,7 +7,7 @@ export type DetailsBadgeProps = { visible?: boolean; }; -export default function DetailsBadge(props: DetailsBadgeProps) { +export default function DetailsBadge(props: Readonly) { if (props.visible == false) { return null; } diff --git a/src/frontend/src/components/details/DetailsImage.tsx b/src/frontend/src/components/details/DetailsImage.tsx index dc0dd6b808..3792208d5e 100644 --- a/src/frontend/src/components/details/DetailsImage.tsx +++ b/src/frontend/src/components/details/DetailsImage.tsx @@ -322,7 +322,7 @@ function ImageActionButtons({ /** * Renders an image with action buttons for display on Details panels */ -export function DetailsImage(props: DetailImageProps) { +export function DetailsImage(props: Readonly) { // Displays a group of ActionButtons on hover const { hovered, ref } = useHover(); const [img, setImg] = useState(props.src ?? backup_image); diff --git a/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx b/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx index 6fd8d1dab9..1c5af32e48 100644 --- a/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx +++ b/src/frontend/src/components/editors/TemplateEditor/TemplateEditor.tsx @@ -87,7 +87,7 @@ type TemplateEditorProps = { template: TemplateI; }; -export function TemplateEditor(props: TemplateEditorProps) { +export function TemplateEditor(props: Readonly) { const { downloadUrl, editors, previewAreas, preview } = props; const editorRef = useRef(); const previewRef = useRef(); diff --git a/src/frontend/src/components/forms/fields/ApiFormField.tsx b/src/frontend/src/components/forms/fields/ApiFormField.tsx index 9d098f0abb..ae6d57a3e0 100644 --- a/src/frontend/src/components/forms/fields/ApiFormField.tsx +++ b/src/frontend/src/components/forms/fields/ApiFormField.tsx @@ -10,8 +10,7 @@ import { import { UseFormReturnType } from '@mantine/form'; import { useId } from '@mantine/hooks'; import { IconX } from '@tabler/icons-react'; -import { ReactNode, useCallback, useEffect } from 'react'; -import { useMemo } from 'react'; +import { ReactNode, useCallback, useEffect, useMemo } from 'react'; import { Control, FieldValues, useController } from 'react-hook-form'; import { ModelType } from '../../../enums/ModelType'; diff --git a/src/frontend/src/components/forms/fields/ChoiceField.tsx b/src/frontend/src/components/forms/fields/ChoiceField.tsx index d0137d9d3b..36554c2c3d 100644 --- a/src/frontend/src/components/forms/fields/ChoiceField.tsx +++ b/src/frontend/src/components/forms/fields/ChoiceField.tsx @@ -1,7 +1,6 @@ import { Select } from '@mantine/core'; import { useId } from '@mantine/hooks'; -import { useCallback } from 'react'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { FieldValues, UseControllerReturn } from 'react-hook-form'; import { ApiFormFieldType } from './ApiFormField'; diff --git a/src/frontend/src/components/forms/fields/RelatedModelField.tsx b/src/frontend/src/components/forms/fields/RelatedModelField.tsx index 0f615765b2..d02bfcf321 100644 --- a/src/frontend/src/components/forms/fields/RelatedModelField.tsx +++ b/src/frontend/src/components/forms/fields/RelatedModelField.tsx @@ -1,7 +1,6 @@ import { t } from '@lingui/macro'; import { Input, useMantineTheme } from '@mantine/core'; -import { useDebouncedValue } from '@mantine/hooks'; -import { useId } from '@mantine/hooks'; +import { useDebouncedValue, useId } from '@mantine/hooks'; import { useQuery } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { diff --git a/src/frontend/src/components/images/Thumbnail.tsx b/src/frontend/src/components/images/Thumbnail.tsx index a7a8f0a984..33f1628d68 100644 --- a/src/frontend/src/components/images/Thumbnail.tsx +++ b/src/frontend/src/components/images/Thumbnail.tsx @@ -1,7 +1,5 @@ import { t } from '@lingui/macro'; -import { Anchor, Skeleton } from '@mantine/core'; -import { Group } from '@mantine/core'; -import { Text } from '@mantine/core'; +import { Anchor, Group, Skeleton, Text } from '@mantine/core'; import { ReactNode, useMemo } from 'react'; import { ApiImage } from './ApiImage'; diff --git a/src/frontend/src/components/items/DocInfo.tsx b/src/frontend/src/components/items/DocInfo.tsx index d9012d0716..5b5d54d891 100644 --- a/src/frontend/src/components/items/DocInfo.tsx +++ b/src/frontend/src/components/items/DocInfo.tsx @@ -6,7 +6,12 @@ interface DocInfoProps extends BaseDocProps { size?: number; } -export function DocInfo({ size = 18, text, detail, link }: DocInfoProps) { +export function DocInfo({ + size = 18, + text, + detail, + link +}: Readonly) { return ( diff --git a/src/frontend/src/components/items/DocTooltip.tsx b/src/frontend/src/components/items/DocTooltip.tsx index 80b0d5660d..cd8d520efc 100644 --- a/src/frontend/src/components/items/DocTooltip.tsx +++ b/src/frontend/src/components/items/DocTooltip.tsx @@ -21,7 +21,7 @@ export function DocTooltip({ detail, link, docchildren -}: DocTooltipProps) { +}: Readonly) { const { classes } = InvenTreeStyle(); return ( diff --git a/src/frontend/src/components/items/ProgressBar.tsx b/src/frontend/src/components/items/ProgressBar.tsx index 2369da7faa..40bb9d3034 100644 --- a/src/frontend/src/components/items/ProgressBar.tsx +++ b/src/frontend/src/components/items/ProgressBar.tsx @@ -13,7 +13,7 @@ export type ProgressBarProps = { * A progress bar element, built on mantine.Progress * The color of the bar is determined based on the value */ -export function ProgressBar(props: ProgressBarProps) { +export function ProgressBar(props: Readonly) { const progress = useMemo(() => { let maximum = props.maximum ?? 100; let value = Math.max(props.value, 0); diff --git a/src/frontend/src/components/items/TitleWithDoc.tsx b/src/frontend/src/components/items/TitleWithDoc.tsx index d91c7fac97..89ca569e61 100644 --- a/src/frontend/src/components/items/TitleWithDoc.tsx +++ b/src/frontend/src/components/items/TitleWithDoc.tsx @@ -14,7 +14,7 @@ export function TitleWithDoc({ size, text, detail -}: DocTitleProps) { +}: Readonly) { return ( diff --git a/src/frontend/src/components/nav/DetailDrawer.tsx b/src/frontend/src/components/nav/DetailDrawer.tsx index 91f7b3e734..892df54a6b 100644 --- a/src/frontend/src/components/nav/DetailDrawer.tsx +++ b/src/frontend/src/components/nav/DetailDrawer.tsx @@ -43,7 +43,7 @@ function DetailDrawerComponent({ size, closeOnEscape = true, renderContent -}: DrawerProps) { +}: Readonly<DrawerProps>) { const navigate = useNavigate(); const { id } = useParams(); const { classes } = useStyles(); @@ -95,7 +95,7 @@ function DetailDrawerComponent({ ); } -export function DetailDrawer(props: DrawerProps) { +export function DetailDrawer(props: Readonly<DrawerProps>) { return ( <Routes> <Route path=":id?/" element={<DetailDrawerComponent {...props} />} /> diff --git a/src/frontend/src/components/nav/NotificationDrawer.tsx b/src/frontend/src/components/nav/NotificationDrawer.tsx index fdac2ec9f5..9e4e07d894 100644 --- a/src/frontend/src/components/nav/NotificationDrawer.tsx +++ b/src/frontend/src/components/nav/NotificationDrawer.tsx @@ -4,15 +4,16 @@ import { Alert, Divider, Drawer, + Group, LoadingOverlay, Space, + Stack, + Text, Tooltip } from '@mantine/core'; -import { Group, Stack, Text } from '@mantine/core'; import { IconBellCheck, IconBellPlus } from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; -import { useNavigate } from 'react-router-dom'; -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { api } from '../../App'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; diff --git a/src/frontend/src/components/nav/PageDetail.tsx b/src/frontend/src/components/nav/PageDetail.tsx index 9450e733c1..d8629d5498 100644 --- a/src/frontend/src/components/nav/PageDetail.tsx +++ b/src/frontend/src/components/nav/PageDetail.tsx @@ -5,6 +5,17 @@ import { ApiImage } from '../images/ApiImage'; import { StylishText } from '../items/StylishText'; import { Breadcrumb, BreadcrumbList } from './BreadcrumbList'; +interface PageDetailInterface { + title?: string; + subtitle?: string; + imageUrl?: string; + detail?: ReactNode; + badges?: ReactNode[]; + breadcrumbs?: Breadcrumb[]; + breadcrumbAction?: () => void; + actions?: ReactNode[]; +} + /** * Construct a "standard" page detail for common display between pages. * @@ -20,16 +31,7 @@ export function PageDetail({ breadcrumbs, breadcrumbAction, actions -}: { - title?: string; - subtitle?: string; - imageUrl?: string; - detail?: ReactNode; - badges?: ReactNode[]; - breadcrumbs?: Breadcrumb[]; - breadcrumbAction?: () => void; - actions?: ReactNode[]; -}) { +}: Readonly<PageDetailInterface>) { return ( <Stack spacing="xs"> {breadcrumbs && breadcrumbs.length > 0 && ( diff --git a/src/frontend/src/components/nav/PanelGroup.tsx b/src/frontend/src/components/nav/PanelGroup.tsx index 17d959d5cc..687d0f1b32 100644 --- a/src/frontend/src/components/nav/PanelGroup.tsx +++ b/src/frontend/src/components/nav/PanelGroup.tsx @@ -10,8 +10,7 @@ import { IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightCollapse } from '@tabler/icons-react'; -import { ReactNode, useMemo } from 'react'; -import { useEffect, useState } from 'react'; +import { ReactNode, useEffect, useMemo, useState } from 'react'; import { Navigate, Route, @@ -51,7 +50,7 @@ function BasePanelGroup({ onPanelChange, selectedPanel, collapsible = true -}: PanelProps): ReactNode { +}: Readonly<PanelProps>): ReactNode { const navigate = useNavigate(); const { panel } = useParams(); @@ -178,7 +177,11 @@ function BasePanelGroup({ ); } -function IndexPanelComponent({ pageKey, selectedPanel, panels }: PanelProps) { +function IndexPanelComponent({ + pageKey, + selectedPanel, + panels +}: Readonly<PanelProps>) { const lastUsedPanel = useLocalState((state) => { const panelName = selectedPanel || state.lastUsedPanels[pageKey] || panels[0]?.name; @@ -203,7 +206,7 @@ function IndexPanelComponent({ pageKey, selectedPanel, panels }: PanelProps) { * @param onPanelChange - Callback when the active panel changes * @param collapsible - If true, the panel group can be collapsed (defaults to true) */ -export function PanelGroup(props: PanelProps) { +export function PanelGroup(props: Readonly<PanelProps>) { return ( <Routes> <Route index element={<IndexPanelComponent {...props} />} /> diff --git a/src/frontend/src/components/nav/SearchDrawer.tsx b/src/frontend/src/components/nav/SearchDrawer.tsx index b2ae9f0bb5..d6b491f926 100644 --- a/src/frontend/src/components/nav/SearchDrawer.tsx +++ b/src/frontend/src/components/nav/SearchDrawer.tsx @@ -8,6 +8,7 @@ import { Divider, Drawer, Group, + Loader, Menu, Paper, Space, @@ -15,7 +16,6 @@ import { Text, TextInput } from '@mantine/core'; -import { Loader } from '@mantine/core'; import { useDebouncedValue } from '@mantine/hooks'; import { IconAlertCircle, diff --git a/src/frontend/src/components/nav/SettingsHeader.tsx b/src/frontend/src/components/nav/SettingsHeader.tsx index d8e525d14f..8b37713ba8 100644 --- a/src/frontend/src/components/nav/SettingsHeader.tsx +++ b/src/frontend/src/components/nav/SettingsHeader.tsx @@ -3,6 +3,15 @@ import { IconSwitch } from '@tabler/icons-react'; import { ReactNode } from 'react'; import { Link } from 'react-router-dom'; +interface SettingsHeaderInterface { + title: string | ReactNode; + shorthand?: string; + subtitle?: string | ReactNode; + switch_condition?: boolean; + switch_text?: string | ReactNode; + switch_link?: string; +} + /** * Construct a settings page header with interlinks to one other settings page */ @@ -13,14 +22,7 @@ export function SettingsHeader({ switch_condition = true, switch_text, switch_link -}: { - title: string | ReactNode; - shorthand?: string; - subtitle?: string | ReactNode; - switch_condition?: boolean; - switch_text?: string | ReactNode; - switch_link?: string; -}) { +}: Readonly<SettingsHeaderInterface>) { return ( <Stack spacing="0" ml={'sm'}> <Group> diff --git a/src/frontend/src/components/render/Build.tsx b/src/frontend/src/components/render/Build.tsx index fb7f3e76a3..2f40f912e4 100644 --- a/src/frontend/src/components/render/Build.tsx +++ b/src/frontend/src/components/render/Build.tsx @@ -1,13 +1,15 @@ import { ReactNode } from 'react'; import { ModelType } from '../../enums/ModelType'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; import { StatusRenderer } from './StatusRenderer'; /** * Inline rendering of a single BuildOrder instance */ -export function RenderBuildOrder({ instance }: { instance: any }): ReactNode { +export function RenderBuildOrder({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { return ( <RenderInlineModel primary={instance.reference} @@ -24,7 +26,9 @@ export function RenderBuildOrder({ instance }: { instance: any }): ReactNode { /* * Inline rendering of a single BuildLine instance */ -export function RenderBuildLine({ instance }: { instance: any }): ReactNode { +export function RenderBuildLine({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { return ( <RenderInlineModel primary={instance.part_detail.full_name} diff --git a/src/frontend/src/components/render/Company.tsx b/src/frontend/src/components/render/Company.tsx index 6a15a6a515..9dce6888d2 100644 --- a/src/frontend/src/components/render/Company.tsx +++ b/src/frontend/src/components/render/Company.tsx @@ -1,11 +1,13 @@ import { ReactNode } from 'react'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; /** * Inline rendering of a single Address instance */ -export function RenderAddress({ instance }: { instance: any }): ReactNode { +export function RenderAddress({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { let text = [ instance.country, instance.postal_code, @@ -23,7 +25,9 @@ export function RenderAddress({ instance }: { instance: any }): ReactNode { /** * Inline rendering of a single Company instance */ -export function RenderCompany({ instance }: { instance: any }): ReactNode { +export function RenderCompany({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { // TODO: Handle URL return ( @@ -38,14 +42,18 @@ export function RenderCompany({ instance }: { instance: any }): ReactNode { /** * Inline rendering of a single Contact instance */ -export function RenderContact({ instance }: { instance: any }): ReactNode { +export function RenderContact({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { return <RenderInlineModel primary={instance.name} />; } /** * Inline rendering of a single SupplierPart instance */ -export function RenderSupplierPart({ instance }: { instance: any }): ReactNode { +export function RenderSupplierPart({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { // TODO: handle URL let supplier = instance.supplier_detail ?? {}; @@ -66,9 +74,7 @@ export function RenderSupplierPart({ instance }: { instance: any }): ReactNode { */ export function RenderManufacturerPart({ instance -}: { - instance: any; -}): ReactNode { +}: Readonly<InstanceRenderInterface>): ReactNode { let part = instance.part_detail ?? {}; let manufacturer = instance.manufacturer_detail ?? {}; diff --git a/src/frontend/src/components/render/Generic.tsx b/src/frontend/src/components/render/Generic.tsx index fb616aa692..9f9a15aa33 100644 --- a/src/frontend/src/components/render/Generic.tsx +++ b/src/frontend/src/components/render/Generic.tsx @@ -1,8 +1,10 @@ import { ReactNode } from 'react'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; -export function RenderProjectCode({ instance }: { instance: any }): ReactNode { +export function RenderProjectCode({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { return ( instance && ( <RenderInlineModel diff --git a/src/frontend/src/components/render/Instance.tsx b/src/frontend/src/components/render/Instance.tsx index 15b348137f..64ff4e7a0f 100644 --- a/src/frontend/src/components/render/Instance.tsx +++ b/src/frontend/src/components/render/Instance.tsx @@ -1,6 +1,5 @@ import { t } from '@lingui/macro'; -import { Alert, Space } from '@mantine/core'; -import { Group, Text } from '@mantine/core'; +import { Alert, Group, Space, Text } from '@mantine/core'; import { ReactNode } from 'react'; import { ModelType } from '../../enums/ModelType'; @@ -38,7 +37,7 @@ type EnumDictionary<T extends string | symbol | number, U> = { */ const RendererLookup: EnumDictionary< ModelType, - (props: { instance: any }) => ReactNode + (props: Readonly<InstanceRenderInterface>) => ReactNode > = { [ModelType.address]: RenderAddress, [ModelType.build]: RenderBuildOrder, @@ -140,3 +139,7 @@ export function UnknownRenderer({ </Alert> ); } + +export interface InstanceRenderInterface { + instance: any; +} diff --git a/src/frontend/src/components/render/Order.tsx b/src/frontend/src/components/render/Order.tsx index df22461b30..63cdd581e5 100644 --- a/src/frontend/src/components/render/Order.tsx +++ b/src/frontend/src/components/render/Order.tsx @@ -2,7 +2,7 @@ import { t } from '@lingui/macro'; import { ReactNode } from 'react'; import { ModelType } from '../../enums/ModelType'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; import { StatusRenderer } from './StatusRenderer'; /** @@ -10,9 +10,7 @@ import { StatusRenderer } from './StatusRenderer'; */ export function RenderPurchaseOrder({ instance -}: { - instance: any; -}): ReactNode { +}: Readonly<InstanceRenderInterface>): ReactNode { let supplier = instance.supplier_detail || {}; // TODO: Handle URL @@ -32,7 +30,9 @@ export function RenderPurchaseOrder({ /** * Inline rendering of a single ReturnOrder instance */ -export function RenderReturnOrder({ instance }: { instance: any }): ReactNode { +export function RenderReturnOrder({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { let customer = instance.customer_detail || {}; return ( @@ -51,7 +51,9 @@ export function RenderReturnOrder({ instance }: { instance: any }): ReactNode { /** * Inline rendering of a single SalesOrder instance */ -export function RenderSalesOrder({ instance }: { instance: any }): ReactNode { +export function RenderSalesOrder({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { let customer = instance.customer_detail || {}; // TODO: Handle URL diff --git a/src/frontend/src/components/render/Part.tsx b/src/frontend/src/components/render/Part.tsx index 7a3b8a0716..c2e0ed382b 100644 --- a/src/frontend/src/components/render/Part.tsx +++ b/src/frontend/src/components/render/Part.tsx @@ -1,12 +1,14 @@ import { t } from '@lingui/macro'; import { ReactNode } from 'react'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; /** * Inline rendering of a single Part instance */ -export function RenderPart({ instance }: { instance: any }): ReactNode { +export function RenderPart({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { const stock = t`Stock` + `: ${instance.in_stock}`; return ( @@ -22,7 +24,9 @@ export function RenderPart({ instance }: { instance: any }): ReactNode { /** * Inline rendering of a PartCategory instance */ -export function RenderPartCategory({ instance }: { instance: any }): ReactNode { +export function RenderPartCategory({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { // TODO: Handle URL let lvl = '-'.repeat(instance.level || 0); diff --git a/src/frontend/src/components/render/StatusRenderer.tsx b/src/frontend/src/components/render/StatusRenderer.tsx index 78fb3f9414..5c284533dc 100644 --- a/src/frontend/src/components/render/StatusRenderer.tsx +++ b/src/frontend/src/components/render/StatusRenderer.tsx @@ -14,7 +14,7 @@ export interface StatusCodeListInterface { [key: string]: StatusCodeInterface; } -interface renderStatusLabelOptionsInterface { +interface RenderStatusLabelOptionsInterface { size?: MantineSize; } @@ -24,7 +24,7 @@ interface renderStatusLabelOptionsInterface { function renderStatusLabel( key: string | number, codes: StatusCodeListInterface, - options: renderStatusLabelOptionsInterface = {} + options: RenderStatusLabelOptionsInterface = {} ) { let text = null; let color = null; @@ -70,7 +70,7 @@ export const StatusRenderer = ({ }: { status: string | number; type: ModelType | string; - options?: renderStatusLabelOptionsInterface; + options?: RenderStatusLabelOptionsInterface; }) => { const statusCodeList = useGlobalStatusState.getState().status; diff --git a/src/frontend/src/components/render/Stock.tsx b/src/frontend/src/components/render/Stock.tsx index 7738861e98..1f8a60b995 100644 --- a/src/frontend/src/components/render/Stock.tsx +++ b/src/frontend/src/components/render/Stock.tsx @@ -1,16 +1,14 @@ import { t } from '@lingui/macro'; import { ReactNode } from 'react'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; /** * Inline rendering of a single StockLocation instance */ export function RenderStockLocation({ instance -}: { - instance: any; -}): ReactNode { +}: Readonly<InstanceRenderInterface>): ReactNode { return ( <RenderInlineModel primary={instance.name} @@ -19,7 +17,9 @@ export function RenderStockLocation({ ); } -export function RenderStockItem({ instance }: { instance: any }): ReactNode { +export function RenderStockItem({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { let quantity_string = ''; if (instance?.serial !== null && instance?.serial !== undefined) { diff --git a/src/frontend/src/components/render/User.tsx b/src/frontend/src/components/render/User.tsx index 85c508cb70..005351c0d6 100644 --- a/src/frontend/src/components/render/User.tsx +++ b/src/frontend/src/components/render/User.tsx @@ -1,9 +1,11 @@ import { IconUser, IconUsersGroup } from '@tabler/icons-react'; import { ReactNode } from 'react'; -import { RenderInlineModel } from './Instance'; +import { InstanceRenderInterface, RenderInlineModel } from './Instance'; -export function RenderOwner({ instance }: { instance: any }): ReactNode { +export function RenderOwner({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { return ( instance && ( <RenderInlineModel @@ -14,7 +16,9 @@ export function RenderOwner({ instance }: { instance: any }): ReactNode { ); } -export function RenderUser({ instance }: { instance: any }): ReactNode { +export function RenderUser({ + instance +}: Readonly<InstanceRenderInterface>): ReactNode { return ( instance && ( <RenderInlineModel diff --git a/src/frontend/src/components/widgets/MarkdownEditor.tsx b/src/frontend/src/components/widgets/MarkdownEditor.tsx index ee458206a5..02af7cf86f 100644 --- a/src/frontend/src/components/widgets/MarkdownEditor.tsx +++ b/src/frontend/src/components/widgets/MarkdownEditor.tsx @@ -2,8 +2,7 @@ import { t } from '@lingui/macro'; import { showNotification } from '@mantine/notifications'; import EasyMDE from 'easymde'; import 'easymde/dist/easymde.min.css'; -import { ReactNode, useCallback, useMemo } from 'react'; -import { useState } from 'react'; +import { ReactNode, useCallback, useMemo, useState } from 'react'; import SimpleMDE from 'react-simplemde-editor'; import { api } from '../../App'; diff --git a/src/frontend/src/components/widgets/WidgetLayout.tsx b/src/frontend/src/components/widgets/WidgetLayout.tsx index 7549592193..1270a87fd0 100644 --- a/src/frontend/src/components/widgets/WidgetLayout.tsx +++ b/src/frontend/src/components/widgets/WidgetLayout.tsx @@ -4,9 +4,10 @@ import { Container, Group, Indicator, + Menu, + Text, createStyles } from '@mantine/core'; -import { Menu, Text } from '@mantine/core'; import { useDisclosure, useHotkeys } from '@mantine/hooks'; import { IconArrowBackUpDouble, diff --git a/src/frontend/src/defaults/formatters.tsx b/src/frontend/src/defaults/formatters.tsx index 563a67fdaf..242ed31dd2 100644 --- a/src/frontend/src/defaults/formatters.tsx +++ b/src/frontend/src/defaults/formatters.tsx @@ -5,13 +5,13 @@ import { useUserSettingsState } from '../states/SettingsState'; -interface formatDecmimalOptionsType { +interface FormatDecmimalOptionsInterface { digits?: number; minDigits?: number; locale?: string; } -interface formatCurrencyOptionsType { +interface FormatCurrencyOptionsInterface { digits?: number; minDigits?: number; currency?: string; @@ -21,7 +21,7 @@ interface formatCurrencyOptionsType { export function formatDecimal( value: number | null | undefined, - options: formatDecmimalOptionsType = {} + options: FormatDecmimalOptionsInterface = {} ) { let locale = options.locale || navigator.language || 'en-US'; @@ -44,7 +44,7 @@ export function formatDecimal( */ export function formatCurrency( value: number | string | null | undefined, - options: formatCurrencyOptionsType = {} + options: FormatCurrencyOptionsInterface = {} ) { if (value == null || value == undefined) { return null; @@ -89,7 +89,7 @@ export function formatCurrency( export function formatPriceRange( minValue: number | null, maxValue: number | null, - options: formatCurrencyOptionsType = {} + options: FormatCurrencyOptionsInterface = {} ) { // If neither values are provided, return a dash if (minValue == null && maxValue == null) { @@ -116,7 +116,7 @@ export function formatPriceRange( )}`; } -interface renderDateOptionsType { +interface RenderDateOptionsInterface { showTime?: boolean; showSeconds?: boolean; } @@ -127,7 +127,10 @@ interface renderDateOptionsType { * The provided "date" variable is a string, nominally ISO format e.g. 2022-02-22 * The user-configured setting DATE_DISPLAY_FORMAT determines how the date should be displayed. */ -export function renderDate(date: string, options: renderDateOptionsType = {}) { +export function renderDate( + date: string, + options: RenderDateOptionsInterface = {} +) { if (!date) { return '-'; } diff --git a/src/frontend/src/functions/icons.tsx b/src/frontend/src/functions/icons.tsx index 82e6816abf..74f18dc7b3 100644 --- a/src/frontend/src/functions/icons.tsx +++ b/src/frontend/src/functions/icons.tsx @@ -217,7 +217,7 @@ type InvenTreeIconProps = { iconProps?: TablerIconProps; }; -export function InvenTreeIcon(props: InvenTreeIconProps) { +export function InvenTreeIcon(props: Readonly<InvenTreeIconProps>) { let Icon: React.ForwardRefExoticComponent<React.RefAttributes<any>>; if (props.icon in icons) { diff --git a/src/frontend/src/pages/Index/Playground.tsx b/src/frontend/src/pages/Index/Playground.tsx index bdeabf7ef8..a1c9d7c21b 100644 --- a/src/frontend/src/pages/Index/Playground.tsx +++ b/src/frontend/src/pages/Index/Playground.tsx @@ -1,7 +1,13 @@ import { Trans } from '@lingui/macro'; -import { Button, Card, Stack, TextInput } from '@mantine/core'; -import { Group, Text } from '@mantine/core'; -import { Accordion } from '@mantine/core'; +import { + Accordion, + Button, + Card, + Group, + Stack, + Text, + TextInput +} from '@mantine/core'; import { spotlight } from '@mantine/spotlight'; import { IconAlien } from '@tabler/icons-react'; import { ReactNode, useMemo, useState } from 'react'; diff --git a/src/frontend/src/pages/Index/Scan.tsx b/src/frontend/src/pages/Index/Scan.tsx index d525000e70..82c1e23637 100644 --- a/src/frontend/src/pages/Index/Scan.tsx +++ b/src/frontend/src/pages/Index/Scan.tsx @@ -1,9 +1,11 @@ import { Trans, t } from '@lingui/macro'; import { ActionIcon, + Badge, Button, Checkbox, Col, + Container, Grid, Group, ScrollArea, @@ -15,15 +17,14 @@ import { TextInput, rem } from '@mantine/core'; -import { Badge, Container } from '@mantine/core'; import { getHotkeyHandler, randomId, + useDocumentVisibility, useFullscreen, useListState, useLocalStorage } from '@mantine/hooks'; -import { useDocumentVisibility } from '@mantine/hooks'; import { showNotification } from '@mantine/notifications'; import { IconAlertCircle, @@ -36,9 +37,9 @@ import { IconPlus, IconQuestionMark, IconSearch, - IconTrash + IconTrash, + IconX } from '@tabler/icons-react'; -import { IconX } from '@tabler/icons-react'; import { Html5Qrcode } from 'html5-qrcode'; import { CameraDevice } from 'html5-qrcode/camera/core'; import { useEffect, useState } from 'react'; @@ -470,11 +471,11 @@ enum InputMethod { ImageBarcode = 'imageBarcode' } -interface inputProps { +interface ScanInputInterface { action: (items: ScanItem[]) => void; } -function InputManual({ action }: inputProps) { +function InputManual({ action }: Readonly<ScanInputInterface>) { const [value, setValue] = useState<string>(''); function btnAddItem() { @@ -526,7 +527,7 @@ function InputManual({ action }: inputProps) { } /* Input that uses QR code detection from images */ -function InputImageBarcode({ action }: inputProps) { +function InputImageBarcode({ action }: Readonly<ScanInputInterface>) { const [qrCodeScanner, setQrCodeScanner] = useState<Html5Qrcode | null>(null); const [camId, setCamId] = useLocalStorage<CameraDevice | null>({ key: 'camId', diff --git a/src/frontend/src/pages/company/CompanyDetail.tsx b/src/frontend/src/pages/company/CompanyDetail.tsx index 5244a3f648..cbf02088b4 100644 --- a/src/frontend/src/pages/company/CompanyDetail.tsx +++ b/src/frontend/src/pages/company/CompanyDetail.tsx @@ -29,8 +29,7 @@ import { } from '../../components/items/ActionDropdown'; import { Breadcrumb } from '../../components/nav/BreadcrumbList'; import { PageDetail } from '../../components/nav/PageDetail'; -import { PanelGroup } from '../../components/nav/PanelGroup'; -import { PanelType } from '../../components/nav/PanelGroup'; +import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; import { NotesEditor } from '../../components/widgets/MarkdownEditor'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { UserRoles } from '../../enums/Roles'; @@ -57,7 +56,7 @@ export type CompanyDetailProps = { /** * Detail view for a single company instance */ -export default function CompanyDetail(props: CompanyDetailProps) { +export default function CompanyDetail(props: Readonly<CompanyDetailProps>) { const { id } = useParams(); const user = useUserState(); diff --git a/src/frontend/src/tables/ColumnSelect.tsx b/src/frontend/src/tables/ColumnSelect.tsx index 4cf66fe58f..bec95cb426 100644 --- a/src/frontend/src/tables/ColumnSelect.tsx +++ b/src/frontend/src/tables/ColumnSelect.tsx @@ -1,6 +1,5 @@ import { t } from '@lingui/macro'; -import { Checkbox, Menu, Tooltip } from '@mantine/core'; -import { ActionIcon } from '@mantine/core'; +import { ActionIcon, Checkbox, Menu, Tooltip } from '@mantine/core'; import { IconAdjustments } from '@tabler/icons-react'; export function TableColumnSelect({ diff --git a/src/frontend/src/tables/DownloadAction.tsx b/src/frontend/src/tables/DownloadAction.tsx index daed196ede..b17df70285 100644 --- a/src/frontend/src/tables/DownloadAction.tsx +++ b/src/frontend/src/tables/DownloadAction.tsx @@ -1,6 +1,5 @@ import { t } from '@lingui/macro'; -import { ActionIcon, Menu } from '@mantine/core'; -import { Tooltip } from '@mantine/core'; +import { ActionIcon, Menu, Tooltip } from '@mantine/core'; import { IconDownload } from '@tabler/icons-react'; export function DownloadAction({ diff --git a/src/frontend/src/tables/InvenTreeTable.tsx b/src/frontend/src/tables/InvenTreeTable.tsx index fa1391bbb4..8f6e0d64b3 100644 --- a/src/frontend/src/tables/InvenTreeTable.tsx +++ b/src/frontend/src/tables/InvenTreeTable.tsx @@ -3,17 +3,22 @@ import { ActionIcon, Alert, Box, + Group, Indicator, LoadingOverlay, Space, Stack, Tooltip } from '@mantine/core'; -import { Group } from '@mantine/core'; import { modals } from '@mantine/modals'; import { showNotification } from '@mantine/notifications'; -import { IconFilter, IconRefresh, IconTrash } from '@tabler/icons-react'; -import { IconBarcode, IconPrinter } from '@tabler/icons-react'; +import { + IconBarcode, + IconFilter, + IconPrinter, + IconRefresh, + IconTrash +} from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import { DataTable, diff --git a/src/frontend/src/tables/RowActions.tsx b/src/frontend/src/tables/RowActions.tsx index 1b35741fe4..bef25f1f10 100644 --- a/src/frontend/src/tables/RowActions.tsx +++ b/src/frontend/src/tables/RowActions.tsx @@ -1,6 +1,5 @@ import { t } from '@lingui/macro'; -import { ActionIcon, Tooltip } from '@mantine/core'; -import { Menu } from '@mantine/core'; +import { ActionIcon, Menu, Tooltip } from '@mantine/core'; import { IconCopy, IconDots, IconEdit, IconTrash } from '@tabler/icons-react'; import { ReactNode, useMemo, useState } from 'react'; @@ -105,7 +104,7 @@ export function RowActions({ }, [actions]); // Render a single action icon - function RowActionIcon(action: RowAction) { + function RowActionIcon(action: Readonly<RowAction>) { return ( <Tooltip withinPortal={true} diff --git a/src/frontend/src/tables/general/AttachmentTable.tsx b/src/frontend/src/tables/general/AttachmentTable.tsx index 3996578cc6..19722bc1b6 100644 --- a/src/frontend/src/tables/general/AttachmentTable.tsx +++ b/src/frontend/src/tables/general/AttachmentTable.tsx @@ -1,6 +1,5 @@ import { t } from '@lingui/macro'; -import { Badge, Group, Stack, Text, Tooltip } from '@mantine/core'; -import { ActionIcon } from '@mantine/core'; +import { ActionIcon, Badge, Group, Stack, Text, Tooltip } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { notifications } from '@mantine/notifications'; import { IconExternalLink, IconFileUpload } from '@tabler/icons-react'; diff --git a/src/frontend/src/tables/part/PartThumbTable.tsx b/src/frontend/src/tables/part/PartThumbTable.tsx index e110250de8..5bdeffec54 100644 --- a/src/frontend/src/tables/part/PartThumbTable.tsx +++ b/src/frontend/src/tables/part/PartThumbTable.tsx @@ -52,7 +52,11 @@ type ThumbProps = { /** * Renders a single image thumbnail */ -function PartThumbComponent({ selected, element, selectImage }: ThumbProps) { +function PartThumbComponent({ + selected, + element, + selectImage +}: Readonly<ThumbProps>) { const { hovered, ref } = useHover(); const hoverColor = 'rgba(127,127,127,0.2)'; diff --git a/src/frontend/src/tables/plugin/PluginListTable.tsx b/src/frontend/src/tables/plugin/PluginListTable.tsx index eea9ba00d8..ac65a6385a 100644 --- a/src/frontend/src/tables/plugin/PluginListTable.tsx +++ b/src/frontend/src/tables/plugin/PluginListTable.tsx @@ -15,12 +15,12 @@ import { notifications, showNotification } from '@mantine/notifications'; import { IconCircleCheck, IconCircleX, + IconDots, IconHelpCircle, IconInfoCircle, IconPlaylistAdd, IconRefresh } from '@tabler/icons-react'; -import { IconDots } from '@tabler/icons-react'; import { useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom';