From 480536a0239b9db475714d74f3c66d4ac8d3b6ad Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 18 Feb 2025 13:48:44 +1100 Subject: [PATCH] PartThumbTable updates (#9094) - Change columns based on viewport width - Use debounced value - Enable pagination - Fix pagination on backend API --- src/backend/InvenTree/part/api.py | 5 +- .../src/components/details/DetailsImage.tsx | 2 +- .../src/tables/part/PartThumbTable.tsx | 113 +++++++++++------- 3 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index 7f31aebca4..d7acfe8dbf 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -522,7 +522,10 @@ class PartThumbs(ListAPI): data = serializer.data - return Response(data) + if page is not None: + return self.get_paginated_response(data) + else: + return Response(data) filter_backends = [InvenTreeSearchFilter] diff --git a/src/frontend/src/components/details/DetailsImage.tsx b/src/frontend/src/components/details/DetailsImage.tsx index 89a61f4159..d5001b31be 100644 --- a/src/frontend/src/components/details/DetailsImage.tsx +++ b/src/frontend/src/components/details/DetailsImage.tsx @@ -305,7 +305,7 @@ function ImageActionButtons({ modals.open({ title: {t`Select Image`}, - size: 'xxl', + size: '80%', children: }); }} diff --git a/src/frontend/src/tables/part/PartThumbTable.tsx b/src/frontend/src/tables/part/PartThumbTable.tsx index 1120af09fd..29ee2d8777 100644 --- a/src/frontend/src/tables/part/PartThumbTable.tsx +++ b/src/frontend/src/tables/part/PartThumbTable.tsx @@ -4,6 +4,7 @@ import { Button, Divider, Group, + Pagination, Paper, SimpleGrid, Skeleton, @@ -11,12 +12,13 @@ import { Text, TextInput } from '@mantine/core'; -import { useHover } from '@mantine/hooks'; +import { useDebouncedValue, useHover } from '@mantine/hooks'; import { modals } from '@mantine/modals'; import { useQuery } from '@tanstack/react-query'; import type React from 'react'; -import { Suspense, useEffect, useState } from 'react'; +import { Suspense, useState } from 'react'; +import { IconX } from '@tabler/icons-react'; import { api } from '../../App'; import { Thumbnail } from '../../components/images/Thumbnail'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; @@ -27,9 +29,6 @@ import { apiUrl } from '../../states/ApiState'; */ export type ThumbTableProps = { pk: string; - limit?: number; - offset?: number; - search?: string; setImage: (image: string) => void; }; @@ -122,37 +121,42 @@ async function setNewImage( /** * Renders a "table" of thumbnails */ -export function PartThumbTable({ - limit = 24, - offset = 0, - search = '', - pk, - setImage -}: Readonly) { +export function PartThumbTable({ pk, setImage }: Readonly) { + const limit = 24; + const [thumbImage, setThumbImage] = useState(null); const [filterInput, setFilterInput] = useState(''); - const [filterQuery, setFilterQuery] = useState(search); + + const [page, setPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); // Keep search filters from updating while user is typing - useEffect(() => { - const timeoutId = setTimeout(() => setFilterQuery(filterInput), 500); - return () => clearTimeout(timeoutId); - }, [filterInput]); + const [searchText] = useDebouncedValue(filterInput, 500); // Fetch thumbnails from API const thumbQuery = useQuery({ - queryKey: [ - ApiEndpoints.part_thumbs_list, - { limit: limit, offset: offset, search: filterQuery } - ], + queryKey: [ApiEndpoints.part_thumbs_list, page, searchText], queryFn: async () => { - return api.get(apiUrl(ApiEndpoints.part_thumbs_list), { - params: { - offset: offset, - limit: limit, - search: filterQuery - } - }); + const offset = Math.max(0, page - 1) * limit; + + return api + .get(apiUrl(ApiEndpoints.part_thumbs_list), { + params: { + offset: offset, + limit: limit, + search: searchText + } + }) + .then((response) => { + const records = response?.data?.count ?? 1; + setTotalPages(Math.ceil(records / limit)); + return response.data?.results ?? response.data; + }) + .catch((error) => { + setTotalPages(1); + setPage(1); + return []; + }); } }); @@ -161,18 +165,20 @@ export function PartThumbTable({ - + {!thumbQuery.isFetching - ? thumbQuery.data?.data.map( - (data: ImageElement, index: number) => ( - - ) - ) + ? thumbQuery?.data.map((data: ImageElement, index: number) => ( + + )) : [...Array(limit)].map((elem, idx) => ( - - { - setFilterInput(event.currentTarget.value); - }} - /> + + + { + setFilterInput(event.currentTarget.value); + }} + rightSection={ + setFilterInput('')} + /> + } + /> + setPage(value)} + /> +