URL nav improvements (#6356)

* Implement getDetailUrl method

* Add nav link for PurchaseOrderLineItem table

* URL cleanup

- Replace hard-coded URLs with lookup
This commit is contained in:
Oliver 2024-01-29 16:51:54 +11:00 committed by GitHub
parent b42f8a620b
commit 0f7d385755
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 64 additions and 18 deletions

View File

@ -10,9 +10,11 @@ import { useNavigate } from 'react-router-dom';
import { formatPriceRange } from '../../../defaults/formatters'; import { formatPriceRange } from '../../../defaults/formatters';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { bomItemFields } from '../../../forms/BomForms'; import { bomItemFields } from '../../../forms/BomForms';
import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -364,7 +366,8 @@ export function BomTable({
sub_part_detail: true sub_part_detail: true
}, },
tableFilters: tableFilters, tableFilters: tableFilters,
onRowClick: (row) => navigate(`/part/${row.sub_part}`), onRowClick: (row) =>
navigate(getDetailUrl(ModelType.part, row.sub_part)),
rowActions: rowActions rowActions: rowActions
}} }}
/> />

View File

@ -3,6 +3,8 @@ import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { PartHoverCard } from '../../images/Thumbnail'; import { PartHoverCard } from '../../images/Thumbnail';
@ -93,7 +95,7 @@ export function UsedInTable({
sub_part_detail: true sub_part_detail: true
}, },
tableFilters: tableFilters, tableFilters: tableFilters,
onRowClick: (row) => navigate(`/part/${row.part}`) onRowClick: (row) => navigate(getDetailUrl(ModelType.part, row.part))
}} }}
/> />
); );

View File

@ -9,6 +9,8 @@ import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -233,7 +235,7 @@ export default function BuildLineTable({ params = {} }: { params?: any }) {
rowActions: rowActions, rowActions: rowActions,
onRowClick: (row: any) => { onRowClick: (row: any) => {
if (row?.part_detail?.pk) { if (row?.part_detail?.pk) {
navigate(`/part/${row.part_detail.pk}`); navigate(getDetailUrl(ModelType.part, row.part_detail.pk));
} }
} }
}} }}

View File

@ -5,6 +5,7 @@ import { useNavigate } from 'react-router-dom';
import { renderDate } from '../../../defaults/formatters'; import { renderDate } from '../../../defaults/formatters';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { PartHoverCard } from '../../images/Thumbnail'; import { PartHoverCard } from '../../images/Thumbnail';
@ -144,7 +145,7 @@ export function BuildOrderTable({ params = {} }: { params?: any }) {
part_detail: true part_detail: true
}, },
tableFilters: tableFilters, tableFilters: tableFilters,
onRowClick: (row) => navigate(`/build/${row.pk}`) onRowClick: (row) => navigate(getDetailUrl(ModelType.build, row.pk))
}} }}
/> />
); );

View File

@ -3,9 +3,11 @@ import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { partCategoryFields } from '../../../forms/PartForms'; import { partCategoryFields } from '../../../forms/PartForms';
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -140,9 +142,8 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
tableFilters: tableFilters, tableFilters: tableFilters,
tableActions: tableActions, tableActions: tableActions,
rowActions: rowActions, rowActions: rowActions,
onRowClick: (record, index, event) => { onRowClick: (record, index, event) =>
navigate(`/part/category/${record.pk}`); navigate(getDetailUrl(ModelType.partcategory, record.pk))
}
}} }}
/> />
); );

View File

@ -5,7 +5,9 @@ import { useNavigate } from 'react-router-dom';
import { formatPriceRange } from '../../../defaults/formatters'; import { formatPriceRange } from '../../../defaults/formatters';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { shortenString } from '../../../functions/tables'; import { shortenString } from '../../../functions/tables';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { Thumbnail } from '../../images/Thumbnail'; import { Thumbnail } from '../../images/Thumbnail';
@ -280,9 +282,8 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) {
...props.params, ...props.params,
category_detail: true category_detail: true
}, },
onRowClick: (record, _index, _event) => { onRowClick: (record) =>
navigate(`/part/${record.pk}/`); navigate(getDetailUrl(ModelType.part, record.pk))
}
}} }}
/> />
); );

View File

@ -3,10 +3,12 @@ import { ReactNode, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { useManufacturerPartFields } from '../../../forms/CompanyForms'; import { useManufacturerPartFields } from '../../../forms/CompanyForms';
import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms';
import { notYetImplemented } from '../../../functions/notifications'; import { notYetImplemented } from '../../../functions/notifications';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -132,7 +134,7 @@ export function ManufacturerPartTable({ params }: { params: any }): ReactNode {
tableActions: tableActions, tableActions: tableActions,
onRowClick: (record: any) => { onRowClick: (record: any) => {
if (record?.pk) { if (record?.pk) {
navigate(`/purchasing/manufacturer-part/${record.pk}/`); navigate(getDetailUrl(ModelType.manufacturerpart, record.pk));
} }
} }
}} }}

View File

@ -2,12 +2,15 @@ import { t } from '@lingui/macro';
import { Text } from '@mantine/core'; import { Text } from '@mantine/core';
import { IconSquareArrowRight } from '@tabler/icons-react'; import { IconSquareArrowRight } from '@tabler/icons-react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { ProgressBar } from '../../../components/items/ProgressBar'; import { ProgressBar } from '../../../components/items/ProgressBar';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms'; import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms';
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -41,6 +44,7 @@ export function PurchaseOrderLineItemTable({
}) { }) {
const table = useTable('purchase-order-line-item'); const table = useTable('purchase-order-line-item');
const navigate = useNavigate();
const user = useUserState(); const user = useUserState();
const rowActions = useCallback( const rowActions = useCallback(
@ -260,7 +264,12 @@ export function PurchaseOrderLineItemTable({
part_detail: true part_detail: true
}, },
rowActions: rowActions, rowActions: rowActions,
tableActions: tableActions tableActions: tableActions,
onRowClick: (row: any) => {
if (row.part) {
navigate(getDetailUrl(ModelType.supplierpart, row.part));
}
}
}} }}
/> />
); );

View File

@ -6,6 +6,7 @@ import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { notYetImplemented } from '../../../functions/notifications'; import { notYetImplemented } from '../../../functions/notifications';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -127,7 +128,7 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
tableActions: tableActions, tableActions: tableActions,
onRowClick: (row: any) => { onRowClick: (row: any) => {
if (row.pk) { if (row.pk) {
navigate(`/purchasing/purchase-order/${row.pk}`); navigate(getDetailUrl(ModelType.purchaseorder, row.pk));
} }
} }
}} }}

View File

@ -4,9 +4,11 @@ import { ReactNode, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { useSupplierPartFields } from '../../../forms/CompanyForms'; import { useSupplierPartFields } from '../../../forms/CompanyForms';
import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms';
import { getDetailUrl } from '../../../functions/urls';
import { useCreateApiFormModal } from '../../../hooks/UseForm'; import { useCreateApiFormModal } from '../../../hooks/UseForm';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
@ -234,7 +236,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode {
tableActions: tableActions, tableActions: tableActions,
onRowClick: (record: any) => { onRowClick: (record: any) => {
if (record?.pk) { if (record?.pk) {
navigate(`/purchasing/supplier-part/${record.pk}/`); navigate(getDetailUrl(ModelType.supplierpart, record.pk));
} }
} }
}} }}

View File

@ -6,6 +6,7 @@ import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { notYetImplemented } from '../../../functions/notifications'; import { notYetImplemented } from '../../../functions/notifications';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -123,7 +124,7 @@ export function ReturnOrderTable({ params }: { params?: any }) {
tableActions: tableActions, tableActions: tableActions,
onRowClick: (row: any) => { onRowClick: (row: any) => {
if (row.pk) { if (row.pk) {
navigate(`/sales/return-order/${row.pk}/`); navigate(getDetailUrl(ModelType.returnorder, row.pk));
} }
} }
}} }}

View File

@ -6,6 +6,7 @@ import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { notYetImplemented } from '../../../functions/notifications'; import { notYetImplemented } from '../../../functions/notifications';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -124,7 +125,7 @@ export function SalesOrderTable({ params }: { params?: any }) {
tableActions: tableActions, tableActions: tableActions,
onRowClick: (row: any) => { onRowClick: (row: any) => {
if (row.pk) { if (row.pk) {
navigate(`/sales/sales-order/${row.pk}/`); navigate(getDetailUrl(ModelType.salesorder, row.pk));
} }
} }
}} }}

View File

@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom';
import { formatCurrency, renderDate } from '../../../defaults/formatters'; import { formatCurrency, renderDate } from '../../../defaults/formatters';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { TableColumn } from '../Column'; import { TableColumn } from '../Column';
@ -345,7 +346,8 @@ export function StockItemTable({ params = {} }: { params?: any }) {
enableDownload: true, enableDownload: true,
enableSelection: true, enableSelection: true,
tableFilters: tableFilters, tableFilters: tableFilters,
onRowClick: (record) => navigate(`/stock/item/${record.pk}`), onRowClick: (record) =>
navigate(getDetailUrl(ModelType.stockitem, record.pk)),
params: { params: {
...params, ...params,
part_detail: true, part_detail: true,

View File

@ -3,9 +3,11 @@ import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ApiPaths } from '../../../enums/ApiEndpoints'; import { ApiPaths } from '../../../enums/ApiEndpoints';
import { ModelType } from '../../../enums/ModelType';
import { UserRoles } from '../../../enums/Roles'; import { UserRoles } from '../../../enums/Roles';
import { stockLocationFields } from '../../../forms/StockForms'; import { stockLocationFields } from '../../../forms/StockForms';
import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; import { openCreateApiForm, openEditApiForm } from '../../../functions/forms';
import { getDetailUrl } from '../../../functions/urls';
import { useTable } from '../../../hooks/UseTable'; import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
@ -164,7 +166,7 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
tableActions: tableActions, tableActions: tableActions,
rowActions: rowActions, rowActions: rowActions,
onRowClick: (record) => { onRowClick: (record) => {
navigate(`/stock/location/${record.pk}`); navigate(getDetailUrl(ModelType.stocklocation, record.pk));
} }
// TODO: allow for "tree view" with cascade // TODO: allow for "tree view" with cascade
}} }}

View File

@ -0,0 +1,16 @@
import { ModelInformationDict } from '../components/render/ModelType';
import { ModelType } from '../enums/ModelType';
/**
* Returns the detail view URL for a given model type
*/
export function getDetailUrl(model: ModelType, pk: number | string): string {
const modelInfo = ModelInformationDict[model];
if (modelInfo && modelInfo.url_detail) {
return modelInfo.url_detail.replace(':pk', pk.toString());
}
console.error(`No detail URL found for model ${model}!`);
return '';
}