2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 11:36:44 +00:00

PartThumbTable updates (#9094)

- Change columns based on viewport width
- Use debounced value
- Enable pagination
- Fix pagination on backend API
This commit is contained in:
Oliver 2025-02-18 13:48:44 +11:00 committed by GitHub
parent a3ffc01d88
commit 480536a023
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 48 deletions

View File

@ -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]

View File

@ -305,7 +305,7 @@ function ImageActionButtons({
modals.open({
title: <StylishText size='xl'>{t`Select Image`}</StylishText>,
size: 'xxl',
size: '80%',
children: <PartThumbTable pk={pk} setImage={setImage} />
});
}}

View File

@ -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<ThumbTableProps>) {
export function PartThumbTable({ pk, setImage }: Readonly<ThumbTableProps>) {
const limit = 24;
const [thumbImage, setThumbImage] = useState<string | null>(null);
const [filterInput, setFilterInput] = useState<string>('');
const [filterQuery, setFilterQuery] = useState<string>(search);
const [page, setPage] = useState<number>(1);
const [totalPages, setTotalPages] = useState<number>(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({
<Suspense>
<Divider />
<Paper p='sm'>
<SimpleGrid cols={8}>
<SimpleGrid
cols={{ base: 2, '450px': 3, '600px': 4, '900px': 6 }}
type='container'
spacing='xs'
>
{!thumbQuery.isFetching
? thumbQuery.data?.data.map(
(data: ImageElement, index: number) => (
<PartThumbComponent
element={data}
key={index}
selected={thumbImage}
selectImage={setThumbImage}
/>
)
)
? thumbQuery?.data.map((data: ImageElement, index: number) => (
<PartThumbComponent
element={data}
key={index}
selected={thumbImage}
selectImage={setThumbImage}
/>
))
: [...Array(limit)].map((elem, idx) => (
<Skeleton
height={150}
@ -188,13 +194,28 @@ export function PartThumbTable({
<Divider />
<Paper p='sm'>
<Group justify='space-between'>
<TextInput
placeholder={t`Search...`}
onChange={(event) => {
setFilterInput(event.currentTarget.value);
}}
/>
<Group justify='space-between' gap='xs'>
<Group justify='left' gap='xs'>
<TextInput
placeholder={t`Search...`}
value={filterInput}
onChange={(event) => {
setFilterInput(event.currentTarget.value);
}}
rightSection={
<IconX
size='1rem'
color='red'
onClick={() => setFilterInput('')}
/>
}
/>
<Pagination
total={totalPages}
value={page}
onChange={(value) => setPage(value)}
/>
</Group>
<Button
disabled={!thumbImage}
onClick={() => setNewImage(thumbImage, pk, setImage)}