perf(ticket): paginate and cache detection previews
This commit is contained in:
@@ -262,6 +262,7 @@ export function TicketAssignmentsCard({
|
|||||||
{videoId ? (
|
{videoId ? (
|
||||||
<div className={'mt-4 border-t pt-4'}>
|
<div className={'mt-4 border-t pt-4'}>
|
||||||
<TicketDetectionPreview
|
<TicketDetectionPreview
|
||||||
|
key={`${videoId}:${selectedAssignment.id}`}
|
||||||
ticketId={ticketId}
|
ticketId={ticketId}
|
||||||
videoId={videoId}
|
videoId={videoId}
|
||||||
assignmentId={selectedAssignment.id}
|
assignmentId={selectedAssignment.id}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ interface TicketDetectionPreviewProps {
|
|||||||
onDetectionCountChange?: (count: number | undefined) => void;
|
onDetectionCountChange?: (count: number | undefined) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DETECTION_PAGE_SIZE = 10;
|
||||||
|
|
||||||
function DetectionMetadataBar({
|
function DetectionMetadataBar({
|
||||||
items,
|
items,
|
||||||
}: {
|
}: {
|
||||||
@@ -102,6 +104,8 @@ export function TicketDetectionPreview({
|
|||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
const [isDiscardDialogOpen, setIsDiscardDialogOpen] = useState(false);
|
const [isDiscardDialogOpen, setIsDiscardDialogOpen] = useState(false);
|
||||||
const isUnassignedPreview = assignmentStatus === 'unassigned';
|
const isUnassignedPreview = assignmentStatus === 'unassigned';
|
||||||
|
const pageStart =
|
||||||
|
Math.floor(currentIndex / DETECTION_PAGE_SIZE) * DETECTION_PAGE_SIZE;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
@@ -109,23 +113,26 @@ export function TicketDetectionPreview({
|
|||||||
|
|
||||||
const queryParams = useMemo(
|
const queryParams = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
skip: currentIndex,
|
skip: pageStart,
|
||||||
limit: 1,
|
limit: DETECTION_PAGE_SIZE,
|
||||||
assignment_id: isUnassignedPreview ? undefined : assignmentId,
|
assignment_id: isUnassignedPreview ? undefined : assignmentId,
|
||||||
assignment_status: isUnassignedPreview
|
assignment_status: isUnassignedPreview
|
||||||
? ('unassigned' as const)
|
? ('unassigned' as const)
|
||||||
: undefined,
|
: undefined,
|
||||||
sort: 'timestamp_asc',
|
sort: 'timestamp_asc',
|
||||||
}),
|
}),
|
||||||
[assignmentId, currentIndex, isUnassignedPreview],
|
[assignmentId, isUnassignedPreview, pageStart],
|
||||||
);
|
);
|
||||||
|
|
||||||
const detectionsQuery = useTicketDetectionsQuery(
|
const detectionsQuery = useTicketDetectionsQuery(
|
||||||
videoId ?? undefined,
|
videoId ?? undefined,
|
||||||
queryParams,
|
queryParams,
|
||||||
|
{ keepPreviousPage: true },
|
||||||
);
|
);
|
||||||
const detectionResult = detectionsQuery.data;
|
const detectionResult = detectionsQuery.data;
|
||||||
const activeDetection = detectionResult?.items[0];
|
const activeDetection = detectionsQuery.isPlaceholderData
|
||||||
|
? undefined
|
||||||
|
: detectionResult?.items[currentIndex - pageStart];
|
||||||
const confirmedDetectionCount = detectionResult?.total;
|
const confirmedDetectionCount = detectionResult?.total;
|
||||||
const detectionCount = confirmedDetectionCount ?? 0;
|
const detectionCount = confirmedDetectionCount ?? 0;
|
||||||
const activeVisual = getDefectVisual(
|
const activeVisual = getDefectVisual(
|
||||||
@@ -156,12 +163,15 @@ export function TicketDetectionPreview({
|
|||||||
}, [confirmedDetectionCount, currentIndex]);
|
}, [confirmedDetectionCount, currentIndex]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onDetectionCountChange?.(confirmedDetectionCount);
|
onDetectionCountChange?.(detectionCount);
|
||||||
}, [confirmedDetectionCount, onDetectionCountChange]);
|
}, [detectionCount, onDetectionCountChange]);
|
||||||
|
|
||||||
const isNavigationDisabled =
|
const isNavigationDisabled =
|
||||||
detectionCount === 0 || detectionsQuery.isLoading;
|
detectionCount === 0 ||
|
||||||
const isInitialLoading = detectionsQuery.isLoading;
|
detectionsQuery.isLoading ||
|
||||||
|
detectionsQuery.isPlaceholderData;
|
||||||
|
const isInitialLoading =
|
||||||
|
detectionsQuery.isLoading || detectionsQuery.isPlaceholderData;
|
||||||
const isError = detectionsQuery.isError;
|
const isError = detectionsQuery.isError;
|
||||||
const retry = () => void detectionsQuery.refetch();
|
const retry = () => void detectionsQuery.refetch();
|
||||||
|
|
||||||
@@ -221,7 +231,7 @@ export function TicketDetectionPreview({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isInitialLoading && !activeDetection ? (
|
{isInitialLoading ? (
|
||||||
<TicketDetectionPreviewSkeleton />
|
<TicketDetectionPreviewSkeleton />
|
||||||
) : isError ? (
|
) : isError ? (
|
||||||
<div className="rounded-lg border border-dashed border-destructive/40 bg-destructive/5 px-4 py-8 text-center">
|
<div className="rounded-lg border border-dashed border-destructive/40 bg-destructive/5 px-4 py-8 text-center">
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function TicketDetectionPreviewCard({
|
|||||||
<CardContent className="p-5">
|
<CardContent className="p-5">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<TicketDetectionPreview
|
<TicketDetectionPreview
|
||||||
|
key={`${videoId}:${assignmentId}`}
|
||||||
ticketId={ticketId}
|
ticketId={ticketId}
|
||||||
videoId={videoId}
|
videoId={videoId}
|
||||||
assignmentId={assignmentId}
|
assignmentId={assignmentId}
|
||||||
|
|||||||
@@ -41,12 +41,13 @@ export function TicketUnassignedIssuesCard({
|
|||||||
results.
|
results.
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant={'default'}>{detectionCount ?? 0} Unassigned</Badge>
|
<Badge variant={'default'}>{detectionCount ?? 0} Unassigned</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className={'p-5'}>
|
<CardContent className={'p-5'}>
|
||||||
<div className={'min-w-0'}>
|
<div className={'min-w-0'}>
|
||||||
<TicketDetectionPreview
|
<TicketDetectionPreview
|
||||||
|
key={`${videoId}:unassigned`}
|
||||||
ticketId={ticketId}
|
ticketId={ticketId}
|
||||||
videoId={videoId}
|
videoId={videoId}
|
||||||
assignmentStatus={'unassigned'}
|
assignmentStatus={'unassigned'}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
|
keepPreviousData,
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
useMutation,
|
useMutation,
|
||||||
useQuery,
|
useQuery,
|
||||||
@@ -28,6 +29,11 @@ import { extensionRequestKeys } from '../../extension-requests/queries/extension
|
|||||||
|
|
||||||
const TICKET_TABLE_REFRESH_MS = 5 * 60 * 1000;
|
const TICKET_TABLE_REFRESH_MS = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
interface TicketDetectionsQueryOptions {
|
||||||
|
enabled?: boolean;
|
||||||
|
keepPreviousPage?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface UseTicketsQueryParams {
|
interface UseTicketsQueryParams {
|
||||||
skip: number;
|
skip: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
@@ -113,8 +119,9 @@ export function useTicketAssignmentsQuery(
|
|||||||
export function useTicketDetectionsQuery(
|
export function useTicketDetectionsQuery(
|
||||||
videoId: string | undefined,
|
videoId: string | undefined,
|
||||||
params: VideoDetectionsParams,
|
params: VideoDetectionsParams,
|
||||||
enabled = true,
|
options: TicketDetectionsQueryOptions = {},
|
||||||
) {
|
) {
|
||||||
|
const { enabled = true, keepPreviousPage = false } = options;
|
||||||
const queryParams = useMemo(
|
const queryParams = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
skip: params.skip ?? 0,
|
skip: params.skip ?? 0,
|
||||||
@@ -143,6 +150,7 @@ export function useTicketDetectionsQuery(
|
|||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
videoService.getVideoDetections(videoId as string, queryParams),
|
videoService.getVideoDetections(videoId as string, queryParams),
|
||||||
enabled: Boolean(enabled && videoId),
|
enabled: Boolean(enabled && videoId),
|
||||||
|
placeholderData: keepPreviousPage ? keepPreviousData : undefined,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user