From 8ca997f73b894d174751d369e742fa0d6a8130f3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 30 Jun 2026 13:48:06 +0000 Subject: [PATCH] Refactor details grid component --- .../src/components/details/Details.tsx | 14 ++++++----- .../src/components/details/ItemDetails.tsx | 20 ++++++++++++++-- .../details/ParameterDetailsGrid.tsx | 22 ++++++++---------- .../src/components/previews/PreviewType.tsx | 3 +++ src/frontend/src/pages/build/BuildDetail.tsx | 11 +++++---- .../src/pages/company/CompanyDetail.tsx | 3 +-- .../pages/company/ManufacturerPartDetail.tsx | 5 ++-- .../src/pages/company/SupplierPartDetail.tsx | 11 +++++---- src/frontend/src/pages/core/GroupDetail.tsx | 10 ++++---- src/frontend/src/pages/core/UserDetail.tsx | 21 ++++++++--------- .../src/pages/part/CategoryDetail.tsx | 19 ++++++++------- .../src/pages/part/PartDetailsPanel.tsx | 23 +++++++++++-------- .../src/pages/sales/ReturnOrderDetail.tsx | 11 +++++---- .../src/pages/sales/SalesOrderDetail.tsx | 11 +++++---- .../pages/sales/SalesOrderShipmentDetail.tsx | 11 +++++---- .../src/pages/stock/LocationDetail.tsx | 19 ++++++++------- .../src/pages/stock/StockDetailsPanel.tsx | 11 +++++---- .../src/pages/stock/TransferOrderDetail.tsx | 11 +++++---- 18 files changed, 141 insertions(+), 95 deletions(-) diff --git a/src/frontend/src/components/details/Details.tsx b/src/frontend/src/components/details/Details.tsx index 3598deada7..58eb6312bb 100644 --- a/src/frontend/src/components/details/Details.tsx +++ b/src/frontend/src/components/details/Details.tsx @@ -529,17 +529,19 @@ export function DetailsTableField({ ); } +export interface DetailsTableProps { + item: any; + fields: DetailsField[]; + title?: string; + showIcons?: boolean; +} + export function DetailsTable({ item, fields, title, showIcons = true -}: Readonly<{ - item: any; - fields: DetailsField[]; - title?: string; - showIcons?: boolean; -}>) { +}: Readonly) { const visibleFields = useMemo(() => { return fields.filter((field) => !field.hidden); }, [fields]); diff --git a/src/frontend/src/components/details/ItemDetails.tsx b/src/frontend/src/components/details/ItemDetails.tsx index 1d0b79d7da..e8b314c150 100644 --- a/src/frontend/src/components/details/ItemDetails.tsx +++ b/src/frontend/src/components/details/ItemDetails.tsx @@ -1,7 +1,20 @@ import { Paper, SimpleGrid } from '@mantine/core'; import type React from 'react'; +import { useMemo } from 'react'; + +import { DetailsTable, type DetailsTableProps } from './Details'; + +export type { DetailsTableProps }; + +export function ItemDetailsGrid({ + children, + tables +}: React.PropsWithChildren<{ tables?: DetailsTableProps[] }>) { + const visibleTables = useMemo( + () => tables?.filter((t) => t.fields.some((f) => !f.hidden)) ?? [], + [tables] + ); -export function ItemDetailsGrid(props: React.PropsWithChildren<{}>) { return ( ) { spacing='xs' verticalSpacing='xs' > - {props.children} + {children} + {visibleTables.map((props, index) => ( + + ))} ); diff --git a/src/frontend/src/components/details/ParameterDetailsGrid.tsx b/src/frontend/src/components/details/ParameterDetailsGrid.tsx index 4849dcc25a..7ed2aedabc 100644 --- a/src/frontend/src/components/details/ParameterDetailsGrid.tsx +++ b/src/frontend/src/components/details/ParameterDetailsGrid.tsx @@ -5,15 +5,15 @@ import { t } from '@lingui/core/macro'; import { useQuery } from '@tanstack/react-query'; import { useMemo } from 'react'; import { useApi } from '../../contexts/ApiContext'; -import { type DetailsField, DetailsTable } from './Details'; +import type { DetailsField, DetailsTableProps } from './Details'; -export function ParameterDetailsGrid({ +export function useParameterDetailsGrid({ model_type, model_id }: Readonly<{ model_type: ModelType; model_id: number | string | undefined; -}>) { +}>): DetailsTableProps { const api = useApi(); const { data: parameters = [] } = useQuery({ @@ -45,14 +45,10 @@ export function ParameterDetailsGrid({ return { fields, item }; }, [parameters]); - if (fields.length === 0) return null; - - return ( - - ); + return { + title: t`Parameters`, + fields: fields, + item: item, + showIcons: false + }; } diff --git a/src/frontend/src/components/previews/PreviewType.tsx b/src/frontend/src/components/previews/PreviewType.tsx index 16ad538286..18f36069fb 100644 --- a/src/frontend/src/components/previews/PreviewType.tsx +++ b/src/frontend/src/components/previews/PreviewType.tsx @@ -1,6 +1,7 @@ import { ModelType } from '@lib/enums/ModelType'; import type { ReactNode } from 'react'; import { PartPreviewComponent } from './models/PartPreview'; +import { PurchaseOrderPreviewComponent } from './models/PurchaseOrderPreview'; import { StockPreviewComponent } from './models/StockPreview'; export interface PreviewType { @@ -28,6 +29,8 @@ export function getPreviewComponentForModel({ return PartPreviewComponent({ instance, modelId }); case ModelType.stockitem: return StockPreviewComponent({ instance, modelId }); + case ModelType.purchaseorder: + return PurchaseOrderPreviewComponent({ instance, modelId }); default: return null; } diff --git a/src/frontend/src/pages/build/BuildDetail.tsx b/src/frontend/src/pages/build/BuildDetail.tsx index 6b8d8e3459..85689fe3cb 100644 --- a/src/frontend/src/pages/build/BuildDetail.tsx +++ b/src/frontend/src/pages/build/BuildDetail.tsx @@ -439,7 +439,13 @@ export default function BuildDetail() { ]; return ( - + - - - ); }, [build, instanceQuery, partRequirements, partRequirementsQuery]); diff --git a/src/frontend/src/pages/company/CompanyDetail.tsx b/src/frontend/src/pages/company/CompanyDetail.tsx index 2b779d4112..4a55122c77 100644 --- a/src/frontend/src/pages/company/CompanyDetail.tsx +++ b/src/frontend/src/pages/company/CompanyDetail.tsx @@ -155,7 +155,7 @@ export default function CompanyDetail(props: Readonly) { ]; return ( - + ) { - ); }, [company, instanceQuery]); diff --git a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx index 33b00ba470..f9f1b5d57b 100644 --- a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx +++ b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx @@ -134,7 +134,9 @@ export default function ManufacturerPartDetail() { ]; return ( - + - ); }, [manufacturerPart, instanceQuery]); diff --git a/src/frontend/src/pages/company/SupplierPartDetail.tsx b/src/frontend/src/pages/company/SupplierPartDetail.tsx index 4f16295460..e20d942491 100644 --- a/src/frontend/src/pages/company/SupplierPartDetail.tsx +++ b/src/frontend/src/pages/company/SupplierPartDetail.tsx @@ -222,7 +222,13 @@ export default function SupplierPartDetail() { ]; return ( - + - - - ); }, [supplierPart, instanceQuery.isFetching]); diff --git a/src/frontend/src/pages/core/GroupDetail.tsx b/src/frontend/src/pages/core/GroupDetail.tsx index 4126e3f68c..86a80b45b1 100644 --- a/src/frontend/src/pages/core/GroupDetail.tsx +++ b/src/frontend/src/pages/core/GroupDetail.tsx @@ -7,10 +7,7 @@ import { Paper, Skeleton, Stack } from '@mantine/core'; import { IconInfoCircle } from '@tabler/icons-react'; import { useMemo } from 'react'; import { useParams } from 'react-router-dom'; -import { - type DetailsField, - DetailsTable -} from '../../components/details/Details'; +import type { DetailsField } from '../../components/details/Details'; import { ItemDetailsGrid } from '../../components/details/ItemDetails'; import {} from '../../components/items/ActionDropdown'; import { RoleTable, type RuleSet } from '../../components/items/RoleTable'; @@ -48,8 +45,9 @@ export default function GroupDetail() { ]; return ( - - + {t`Group Roles`} diff --git a/src/frontend/src/pages/core/UserDetail.tsx b/src/frontend/src/pages/core/UserDetail.tsx index 87b4192219..4c5c1f131b 100644 --- a/src/frontend/src/pages/core/UserDetail.tsx +++ b/src/frontend/src/pages/core/UserDetail.tsx @@ -6,10 +6,7 @@ import { Badge, Group, Skeleton, Stack } from '@mantine/core'; import { IconInfoCircle } from '@tabler/icons-react'; import { type ReactNode, useMemo } from 'react'; import { useParams } from 'react-router-dom'; -import { - type DetailsField, - DetailsTable -} from '../../components/details/Details'; +import type { DetailsField } from '../../components/details/Details'; import { ItemDetailsGrid } from '../../components/details/ItemDetails'; import {} from '../../components/items/ActionDropdown'; import InstanceDetail from '../../components/nav/InstanceDetail'; @@ -171,13 +168,15 @@ export default function UserDetail() { instance.location; return ( - - - - {hasProfile && settings.isSet('DISPLAY_PROFILE_INFO') && ( - - )} - + ); }, [instance, userGroups, instanceQuery]); diff --git a/src/frontend/src/pages/part/CategoryDetail.tsx b/src/frontend/src/pages/part/CategoryDetail.tsx index 9d0077e9c8..6e7ec43c90 100644 --- a/src/frontend/src/pages/part/CategoryDetail.tsx +++ b/src/frontend/src/pages/part/CategoryDetail.tsx @@ -20,10 +20,7 @@ import type { PanelType } from '@lib/types/Panel'; import { useLocalStorage } from '@mantine/hooks'; import AdminButton from '../../components/buttons/AdminButton'; import StarredToggleButton from '../../components/buttons/StarredToggleButton'; -import { - type DetailsField, - DetailsTable -} from '../../components/details/Details'; +import type { DetailsField } from '../../components/details/Details'; import { ItemDetailsGrid } from '../../components/details/ItemDetails'; import { DeleteItemAction, @@ -169,10 +166,16 @@ export default function CategoryDetail() { ]; return ( - - {id && category?.pk && } - {id && category?.pk && } - + ); }, [category, instanceQuery]); diff --git a/src/frontend/src/pages/part/PartDetailsPanel.tsx b/src/frontend/src/pages/part/PartDetailsPanel.tsx index 4f83264447..15100eb889 100644 --- a/src/frontend/src/pages/part/PartDetailsPanel.tsx +++ b/src/frontend/src/pages/part/PartDetailsPanel.tsx @@ -14,7 +14,7 @@ import { } from '../../components/details/Details'; import { DetailsImage } from '../../components/details/DetailsImage'; import { ItemDetailsGrid } from '../../components/details/ItemDetails'; -import { ParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid'; +import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid'; import { formatPriceRange } from '../../defaults/formatters'; import { useInstance } from '../../hooks/UseInstance'; @@ -322,10 +322,22 @@ export function PartDetailsPanel({ return fields; }, [instance, data.latest_serial_number]); + const parametersTable = useParameterDetailsGrid({ + model_type: ModelType.part, + model_id: instance?.pk + }); + if (!instance?.pk) return ; return ( - + {additionalContent} - - - - ); } diff --git a/src/frontend/src/pages/sales/ReturnOrderDetail.tsx b/src/frontend/src/pages/sales/ReturnOrderDetail.tsx index b094826d7c..b62d341e1c 100644 --- a/src/frontend/src/pages/sales/ReturnOrderDetail.tsx +++ b/src/frontend/src/pages/sales/ReturnOrderDetail.tsx @@ -297,7 +297,13 @@ export default function ReturnOrderDetail() { ]; return ( - + - - - ); }, [order, instanceQuery]); diff --git a/src/frontend/src/pages/sales/SalesOrderDetail.tsx b/src/frontend/src/pages/sales/SalesOrderDetail.tsx index 959ea726d1..086400a0f8 100644 --- a/src/frontend/src/pages/sales/SalesOrderDetail.tsx +++ b/src/frontend/src/pages/sales/SalesOrderDetail.tsx @@ -288,7 +288,13 @@ export default function SalesOrderDetail() { ]; return ( - + - - - ); }, [order, orderCurrency, instanceQuery]); diff --git a/src/frontend/src/pages/sales/SalesOrderShipmentDetail.tsx b/src/frontend/src/pages/sales/SalesOrderShipmentDetail.tsx index 7d181814e8..83d956b817 100644 --- a/src/frontend/src/pages/sales/SalesOrderShipmentDetail.tsx +++ b/src/frontend/src/pages/sales/SalesOrderShipmentDetail.tsx @@ -222,7 +222,13 @@ export default function SalesOrderShipmentDetail() { return ( <> - + - - - ); diff --git a/src/frontend/src/pages/stock/LocationDetail.tsx b/src/frontend/src/pages/stock/LocationDetail.tsx index 0b13ba9f23..24fd34a0a2 100644 --- a/src/frontend/src/pages/stock/LocationDetail.tsx +++ b/src/frontend/src/pages/stock/LocationDetail.tsx @@ -24,10 +24,7 @@ import { useBarcodeScanDialog } from '../../components/barcodes/BarcodeScanDialo import AdminButton from '../../components/buttons/AdminButton'; import { PrintingActions } from '../../components/buttons/PrintingActions'; import OrderCalendar from '../../components/calendar/OrderCalendar'; -import { - type DetailsField, - DetailsTable -} from '../../components/details/Details'; +import type { DetailsField } from '../../components/details/Details'; import { ItemDetailsGrid } from '../../components/details/ItemDetails'; import { BarcodeActionDropdown, @@ -185,10 +182,16 @@ export default function Stock() { ]; return ( - - {id && location?.pk && } - {id && location?.pk && } - + ); }, [location, instanceQuery]); diff --git a/src/frontend/src/pages/stock/StockDetailsPanel.tsx b/src/frontend/src/pages/stock/StockDetailsPanel.tsx index 009ffdb5ea..00c23cc8f1 100644 --- a/src/frontend/src/pages/stock/StockDetailsPanel.tsx +++ b/src/frontend/src/pages/stock/StockDetailsPanel.tsx @@ -377,7 +377,13 @@ export function StockDetailsPanel({ return ( <> {showSerialNav && findBySerialNumber.modal} - + - - - ); diff --git a/src/frontend/src/pages/stock/TransferOrderDetail.tsx b/src/frontend/src/pages/stock/TransferOrderDetail.tsx index 39dd9d3354..41efe4e8b4 100644 --- a/src/frontend/src/pages/stock/TransferOrderDetail.tsx +++ b/src/frontend/src/pages/stock/TransferOrderDetail.tsx @@ -234,7 +234,13 @@ export default function TransferOrderDetail() { ]; return ( - + {/* TODO: what image do we show for a Transfer Order? */} @@ -250,9 +256,6 @@ export default function TransferOrderDetail() { - - - ); }, [order, instanceQuery]);