diff --git a/src/frontend/src/pages/build/BuildDetail.tsx b/src/frontend/src/pages/build/BuildDetail.tsx index 8f8bf0e60e..eb3b84117e 100644 --- a/src/frontend/src/pages/build/BuildDetail.tsx +++ b/src/frontend/src/pages/build/BuildDetail.tsx @@ -5,6 +5,7 @@ import { IconCircleCheck, IconClipboardCheck, IconClipboardList, + IconExclamationCircle, IconInfoCircle, IconList, IconListCheck, @@ -88,6 +89,13 @@ function BuildLinesPanel({ isLoading: boolean; hasItems: boolean; }>) { + const bomInformation = useInstance({ + endpoint: ApiEndpoints.bom_validate, + pk: build?.part, + hasPrimaryKey: true, + refetchOnMount: true + }); + const buildLocation = useInstance({ endpoint: ApiEndpoints.stock_location_list, pk: build?.take_from, @@ -105,6 +113,16 @@ function BuildLinesPanel({ return ( + {bomInformation?.isLoaded && + bomInformation?.instance?.bom_validated == false && ( + } + title={t`BOM Not Validated`} + > + {t`The Bill of Materials for this assembly has not been validated.`} + + )} {buildLocation.instance.pk && ( } title={t`Source Location`}> diff --git a/src/frontend/src/pages/part/PartDetail.tsx b/src/frontend/src/pages/part/PartDetail.tsx index afc8e133be..2dfc6e1784 100644 --- a/src/frontend/src/pages/part/PartDetail.tsx +++ b/src/frontend/src/pages/part/PartDetail.tsx @@ -89,7 +89,7 @@ import { useDeleteApiFormModal, useEditApiFormModal } from '../../hooks/UseForm'; -import { useInstance } from '../../hooks/UseInstance'; +import { type UseInstanceResult, useInstance } from '../../hooks/UseInstance'; import { useStockAdjustActions } from '../../hooks/UseStockAdjustActions'; import { useGlobalSettingsState, @@ -158,18 +158,12 @@ function RevisionSelector({ * A hover-over component which displays information about the BOM validation for a given part */ function BomValidationInformation({ + bomInformation, partId }: { + bomInformation: UseInstanceResult; partId: number; }) { - const { instance: bomInformation, instanceQuery: bomInformationQuery } = - useInstance({ - endpoint: ApiEndpoints.bom_validate, - pk: partId, - hasPrimaryKey: true, - refetchOnMount: true - }); - const [taskId, setTaskId] = useState(''); useBackgroundTask({ @@ -177,7 +171,7 @@ function BomValidationInformation({ message: t`Validating BOM`, successMessage: t`BOM validated`, onComplete: () => { - bomInformationQuery.refetch(); + bomInformation.instanceQuery.refetch(); } }); @@ -203,12 +197,12 @@ function BomValidationInformation({ if (response.task_id) { setTaskId(response.task_id); } else { - bomInformationQuery.refetch(); + bomInformation.instanceQuery.refetch(); } } }); - if (bomInformationQuery.isFetching) { + if (bomInformation.instanceQuery.isFetching) { return ; } @@ -217,12 +211,12 @@ function BomValidationInformation({ let title = ''; let description = ''; - if (bomInformation?.bom_validated) { + if (bomInformation.instance?.bom_validated) { color = 'green'; icon = ; title = t`BOM Validated`; description = t`The Bill of Materials for this part has been validated`; - } else if (bomInformation?.bom_checked_date) { + } else if (bomInformation.instance?.bom_checked_date) { color = 'yellow'; icon = ; title = t`BOM Not Validated`; @@ -238,7 +232,7 @@ function BomValidationInformation({ <> {validateBom.modal} - {!bomInformation.bom_validated && ( + {!bomInformation.instance?.bom_validated && ( } color='green' @@ -260,16 +254,17 @@ function BomValidationInformation({ {description} - {bomInformation?.bom_checked_date && ( + {bomInformation.instance?.bom_checked_date && ( - {t`Validated On`}: {bomInformation.bom_checked_date} + {t`Validated On`}:{' '} + {bomInformation.instance.bom_checked_date} )} - {bomInformation?.bom_checked_by_detail && ( + {bomInformation.instance?.bom_checked_by_detail && ( {t`Validated By`}: )} @@ -297,6 +292,13 @@ export default function PartDetail() { const globalSettings = useGlobalSettingsState(); const userSettings = useUserSettingsState(); + const bomInformation = useInstance({ + endpoint: ApiEndpoints.bom_validate, + pk: id, + hasPrimaryKey: true, + refetchOnMount: true + }); + const { instance: serials } = useInstance({ endpoint: ApiEndpoints.part_serial_numbers, pk: id, @@ -802,11 +804,31 @@ export default function PartDetail() { { name: 'bom', label: t`Bill of Materials`, - controls: , + controls: ( + + ), icon: , hidden: !part.assembly, content: part?.pk ? ( - + + {bomInformation.isLoaded && + bomInformation.instance?.bom_validated === false && ( + } + title={t`BOM Not Validated`} + > + {t`The Bill of Materials for this assembly has not been validated.`} + + )} + + ) : ( ) @@ -950,7 +972,15 @@ export default function PartDetail() { has_note: !!part?.notes }) ]; - }, [id, part, user, globalSettings, userSettings, detailsPanel]); + }, [ + id, + part, + user, + globalSettings, + userSettings, + detailsPanel, + bomInformation + ]); const breadcrumbs = useMemo(() => { return [ diff --git a/src/frontend/src/tables/bom/BomSubassemblyTable.tsx b/src/frontend/src/tables/bom/BomSubassemblyTable.tsx index 71373e599d..5e3bc9ea3f 100644 --- a/src/frontend/src/tables/bom/BomSubassemblyTable.tsx +++ b/src/frontend/src/tables/bom/BomSubassemblyTable.tsx @@ -77,9 +77,12 @@ export default function BomSubassemblyTable({ IPNColumn({ accessor: 'sub_part_detail.IPN' }), - ReferenceColumn({}), + ReferenceColumn({ + width: 400 + }), { - accessor: 'quantity' + accessor: 'quantity', + width: 250 } ]; }, [table.isRowExpanded, userSettings]);