mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	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:
		| @@ -10,9 +10,11 @@ import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { formatPriceRange } from '../../../defaults/formatters'; | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { bomItemFields } from '../../../forms/BomForms'; | ||||
| import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -364,7 +366,8 @@ export function BomTable({ | ||||
|           sub_part_detail: true | ||||
|         }, | ||||
|         tableFilters: tableFilters, | ||||
|         onRowClick: (row) => navigate(`/part/${row.sub_part}`), | ||||
|         onRowClick: (row) => | ||||
|           navigate(getDetailUrl(ModelType.part, row.sub_part)), | ||||
|         rowActions: rowActions | ||||
|       }} | ||||
|     /> | ||||
|   | ||||
| @@ -3,6 +3,8 @@ import { useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { PartHoverCard } from '../../images/Thumbnail'; | ||||
| @@ -93,7 +95,7 @@ export function UsedInTable({ | ||||
|           sub_part_detail: true | ||||
|         }, | ||||
|         tableFilters: tableFilters, | ||||
|         onRowClick: (row) => navigate(`/part/${row.part}`) | ||||
|         onRowClick: (row) => navigate(getDetailUrl(ModelType.part, row.part)) | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
|   | ||||
| @@ -9,6 +9,8 @@ import { useCallback, useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -233,7 +235,7 @@ export default function BuildLineTable({ params = {} }: { params?: any }) { | ||||
|         rowActions: rowActions, | ||||
|         onRowClick: (row: any) => { | ||||
|           if (row?.part_detail?.pk) { | ||||
|             navigate(`/part/${row.part_detail.pk}`); | ||||
|             navigate(getDetailUrl(ModelType.part, row.part_detail.pk)); | ||||
|           } | ||||
|         } | ||||
|       }} | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { useNavigate } from 'react-router-dom'; | ||||
| import { renderDate } from '../../../defaults/formatters'; | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { PartHoverCard } from '../../images/Thumbnail'; | ||||
| @@ -144,7 +145,7 @@ export function BuildOrderTable({ params = {} }: { params?: any }) { | ||||
|           part_detail: true | ||||
|         }, | ||||
|         tableFilters: tableFilters, | ||||
|         onRowClick: (row) => navigate(`/build/${row.pk}`) | ||||
|         onRowClick: (row) => navigate(getDetailUrl(ModelType.build, row.pk)) | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
|   | ||||
| @@ -3,9 +3,11 @@ import { useCallback, useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { partCategoryFields } from '../../../forms/PartForms'; | ||||
| import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -140,9 +142,8 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) { | ||||
|         tableFilters: tableFilters, | ||||
|         tableActions: tableActions, | ||||
|         rowActions: rowActions, | ||||
|         onRowClick: (record, index, event) => { | ||||
|           navigate(`/part/category/${record.pk}`); | ||||
|         } | ||||
|         onRowClick: (record, index, event) => | ||||
|           navigate(getDetailUrl(ModelType.partcategory, record.pk)) | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
|   | ||||
| @@ -5,7 +5,9 @@ import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { formatPriceRange } from '../../../defaults/formatters'; | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { shortenString } from '../../../functions/tables'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { Thumbnail } from '../../images/Thumbnail'; | ||||
| @@ -280,9 +282,8 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) { | ||||
|           ...props.params, | ||||
|           category_detail: true | ||||
|         }, | ||||
|         onRowClick: (record, _index, _event) => { | ||||
|           navigate(`/part/${record.pk}/`); | ||||
|         } | ||||
|         onRowClick: (record) => | ||||
|           navigate(getDetailUrl(ModelType.part, record.pk)) | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
|   | ||||
| @@ -3,10 +3,12 @@ import { ReactNode, useCallback, useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { useManufacturerPartFields } from '../../../forms/CompanyForms'; | ||||
| import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; | ||||
| import { notYetImplemented } from '../../../functions/notifications'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -132,7 +134,7 @@ export function ManufacturerPartTable({ params }: { params: any }): ReactNode { | ||||
|         tableActions: tableActions, | ||||
|         onRowClick: (record: any) => { | ||||
|           if (record?.pk) { | ||||
|             navigate(`/purchasing/manufacturer-part/${record.pk}/`); | ||||
|             navigate(getDetailUrl(ModelType.manufacturerpart, record.pk)); | ||||
|           } | ||||
|         } | ||||
|       }} | ||||
|   | ||||
| @@ -2,12 +2,15 @@ import { t } from '@lingui/macro'; | ||||
| import { Text } from '@mantine/core'; | ||||
| import { IconSquareArrowRight } from '@tabler/icons-react'; | ||||
| import { useCallback, useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ProgressBar } from '../../../components/items/ProgressBar'; | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { purchaseOrderLineItemFields } from '../../../forms/PurchaseOrderForms'; | ||||
| import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -41,6 +44,7 @@ export function PurchaseOrderLineItemTable({ | ||||
| }) { | ||||
|   const table = useTable('purchase-order-line-item'); | ||||
|  | ||||
|   const navigate = useNavigate(); | ||||
|   const user = useUserState(); | ||||
|  | ||||
|   const rowActions = useCallback( | ||||
| @@ -260,7 +264,12 @@ export function PurchaseOrderLineItemTable({ | ||||
|           part_detail: true | ||||
|         }, | ||||
|         rowActions: rowActions, | ||||
|         tableActions: tableActions | ||||
|         tableActions: tableActions, | ||||
|         onRowClick: (row: any) => { | ||||
|           if (row.part) { | ||||
|             navigate(getDetailUrl(ModelType.supplierpart, row.part)); | ||||
|           } | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { notYetImplemented } from '../../../functions/notifications'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -127,7 +128,7 @@ export function PurchaseOrderTable({ params }: { params?: any }) { | ||||
|         tableActions: tableActions, | ||||
|         onRowClick: (row: any) => { | ||||
|           if (row.pk) { | ||||
|             navigate(`/purchasing/purchase-order/${row.pk}`); | ||||
|             navigate(getDetailUrl(ModelType.purchaseorder, row.pk)); | ||||
|           } | ||||
|         } | ||||
|       }} | ||||
|   | ||||
| @@ -4,9 +4,11 @@ import { ReactNode, useCallback, useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { useSupplierPartFields } from '../../../forms/CompanyForms'; | ||||
| import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useCreateApiFormModal } from '../../../hooks/UseForm'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| @@ -234,7 +236,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode { | ||||
|           tableActions: tableActions, | ||||
|           onRowClick: (record: any) => { | ||||
|             if (record?.pk) { | ||||
|               navigate(`/purchasing/supplier-part/${record.pk}/`); | ||||
|               navigate(getDetailUrl(ModelType.supplierpart, record.pk)); | ||||
|             } | ||||
|           } | ||||
|         }} | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { notYetImplemented } from '../../../functions/notifications'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -123,7 +124,7 @@ export function ReturnOrderTable({ params }: { params?: any }) { | ||||
|         tableActions: tableActions, | ||||
|         onRowClick: (row: any) => { | ||||
|           if (row.pk) { | ||||
|             navigate(`/sales/return-order/${row.pk}/`); | ||||
|             navigate(getDetailUrl(ModelType.returnorder, row.pk)); | ||||
|           } | ||||
|         } | ||||
|       }} | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { notYetImplemented } from '../../../functions/notifications'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -124,7 +125,7 @@ export function SalesOrderTable({ params }: { params?: any }) { | ||||
|         tableActions: tableActions, | ||||
|         onRowClick: (row: any) => { | ||||
|           if (row.pk) { | ||||
|             navigate(`/sales/sales-order/${row.pk}/`); | ||||
|             navigate(getDetailUrl(ModelType.salesorder, row.pk)); | ||||
|           } | ||||
|         } | ||||
|       }} | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom'; | ||||
| import { formatCurrency, renderDate } from '../../../defaults/formatters'; | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { TableColumn } from '../Column'; | ||||
| @@ -345,7 +346,8 @@ export function StockItemTable({ params = {} }: { params?: any }) { | ||||
|         enableDownload: true, | ||||
|         enableSelection: true, | ||||
|         tableFilters: tableFilters, | ||||
|         onRowClick: (record) => navigate(`/stock/item/${record.pk}`), | ||||
|         onRowClick: (record) => | ||||
|           navigate(getDetailUrl(ModelType.stockitem, record.pk)), | ||||
|         params: { | ||||
|           ...params, | ||||
|           part_detail: true, | ||||
|   | ||||
| @@ -3,9 +3,11 @@ import { useCallback, useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiPaths } from '../../../enums/ApiEndpoints'; | ||||
| import { ModelType } from '../../../enums/ModelType'; | ||||
| import { UserRoles } from '../../../enums/Roles'; | ||||
| import { stockLocationFields } from '../../../forms/StockForms'; | ||||
| import { openCreateApiForm, openEditApiForm } from '../../../functions/forms'; | ||||
| import { getDetailUrl } from '../../../functions/urls'; | ||||
| import { useTable } from '../../../hooks/UseTable'; | ||||
| import { apiUrl } from '../../../states/ApiState'; | ||||
| import { useUserState } from '../../../states/UserState'; | ||||
| @@ -164,7 +166,7 @@ export function StockLocationTable({ parentId }: { parentId?: any }) { | ||||
|         tableActions: tableActions, | ||||
|         rowActions: rowActions, | ||||
|         onRowClick: (record) => { | ||||
|           navigate(`/stock/location/${record.pk}`); | ||||
|           navigate(getDetailUrl(ModelType.stocklocation, record.pk)); | ||||
|         } | ||||
|         // TODO: allow for "tree view" with cascade | ||||
|       }} | ||||
|   | ||||
							
								
								
									
										16
									
								
								src/frontend/src/functions/urls.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/frontend/src/functions/urls.tsx
									
									
									
									
									
										Normal 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 ''; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user