diff --git a/src/frontend/src/tables/InvenTreeTable.tsx b/src/frontend/src/tables/InvenTreeTable.tsx index fc0395ffce..9bcabdd738 100644 --- a/src/frontend/src/tables/InvenTreeTable.tsx +++ b/src/frontend/src/tables/InvenTreeTable.tsx @@ -255,16 +255,22 @@ export function InvenTreeTableInternal>({ // Update column visibility when hiddenColumns change const dataColumns: any = useMemo(() => { - let cols: TableColumn[] = columns.filter((col) => col?.hidden != true); + // Include all columns (even prop-hidden ones) so useDataTableColumns always + // has the full ordered list. This prevents dynamic columns (e.g. ones whose + // hidden flag depends on async data) from being treated as brand-new columns + // after load, which would place them after the ACTIONS column. + let cols: TableColumn[] = [...columns]; cols = cols.map((col) => { - // If the column is *not* switchable, it is always visible - // Otherwise, check if it is "default hidden" - + // Prop-level hidden takes priority (e.g. hidden: !hasTrackedItems). + // For switchable columns, visibility is driven by tableState.hiddenColumns. + // Non-switchable columns are always visible (unless hidden by props). const hidden: boolean = - col.switchable == false - ? false - : (tableState.hiddenColumns?.includes(col.accessor) ?? false); + col.hidden === true + ? true + : col.switchable == false + ? false + : (tableState.hiddenColumns?.includes(col.accessor) ?? false); // Wrap the render function with CopyableCell if copyable is enabled const originalRender = col.render; @@ -382,11 +388,17 @@ export function InvenTreeTableInternal>({ getInitialValueInEffect: false }); - // Reset column ordering and custom widths when the component is mounted + // Reset column ordering when the column set changes (columns added/removed). // Ref: https://github.com/icflorescu/mantine-datatable/issues/759 useEffect(() => { - tableColumns.setColumnsOrder(dataColumns.map((col: any) => col.accessor)); - }, [tableColumnNames]); + const savedOrder = tableColumns.columnsOrder.join(','); + + if (savedOrder != tableColumnNames) { + // This is covering the edge case where the column order is not updated automatically + tableColumns.resetColumnsOrder(); + tableColumns.setColumnsOrder(tableColumnNames.split(',')); + } + }, [tableColumnNames, tableColumns.columnsOrder]); // Reset the pagination state when the search term changes useEffect(() => { diff --git a/src/frontend/src/tables/build/BuildOutputTable.tsx b/src/frontend/src/tables/build/BuildOutputTable.tsx index 6367564acb..42d3cac02a 100644 --- a/src/frontend/src/tables/build/BuildOutputTable.tsx +++ b/src/frontend/src/tables/build/BuildOutputTable.tsx @@ -170,7 +170,8 @@ export default function BuildOutputTable({ // Fetch the test templates associated with the partId const { data: testTemplates, refetch: refetchTestTemplates } = useQuery({ - queryKey: ['buildoutputtests', partId, build], + staleTime: 60 * 1000, // Cache for 1 minute + queryKey: ['buildoutputtests', partId, buildId], queryFn: async () => { if (!partId || partId < 0) { return []; @@ -200,6 +201,7 @@ export default function BuildOutputTable({ // Fetch the "tracked" BOM items associated with the partId const { data: trackedItems, refetch: refetchTrackedItems } = useQuery({ + staleTime: 60 * 1000, // Cache for 1 minute queryKey: ['trackeditems', buildId], queryFn: async () => { if (!buildId || buildId < 0) {