feat(ticket): add issues map to ticket assignment page

This commit is contained in:
2026-07-28 12:19:38 +05:30
parent 20f611f35a
commit 0601322361
9 changed files with 519 additions and 48 deletions

View File

@@ -7,11 +7,16 @@ import { MultiSelectPopover } from '@/components/form/MultiSelectPopover';
import { Input } from '@/components/ui/input';
import { useDebounce } from '@/hooks/useDebounce';
import { useTicketDetectionsQuery } from '../../../hooks/useTicketQueries';
import {
useDetectionCoordinatesQuery,
useTicketDetectionsQuery,
} from '../../../hooks/useTicketQueries';
import { AssignmentIssuesMap } from './AssignmentIssuesMap';
import { useIssueColumns } from './IssueColumns';
import { IssueTable } from './IssueTable';
const DEFAULT_PAGE_SIZE = 10;
const MAP_PAGE_SIZE = 50;
interface IssueTypeOption {
class_name: string;
@@ -59,44 +64,87 @@ export function ChooseIssuesSection({
[debouncedSearch, limit, selectedIssueTypes, skip],
);
const detectionsQuery = useTicketDetectionsQuery(videoId, queryParams);
const mapQueryParams = useMemo(
() => ({
limit: MAP_PAGE_SIZE,
class_name:
selectedIssueTypes.length > 0 ? selectedIssueTypes : undefined,
search: debouncedSearch || undefined,
}),
[debouncedSearch, selectedIssueTypes],
);
const coordinatesQuery = useDetectionCoordinatesQuery(
videoId,
mapQueryParams,
);
const result = detectionsQuery.data;
const mapData = useMemo(() => {
const pages = coordinatesQuery.data?.pages;
if (!pages?.length) return undefined;
const latestPage = pages[pages.length - 1];
return {
...latestPage,
items: pages.flatMap((page) => page.items),
truncated: Boolean(coordinatesQuery.hasNextPage),
};
}, [coordinatesQuery.data?.pages, coordinatesQuery.hasNextPage]);
return (
<IssueTable
columns={columns}
issues={result?.items ?? []}
isLoading={detectionsQuery.isLoading || detectionsQuery.isFetching}
isError={detectionsQuery.isError}
toolbar={
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<div className="w-fit">
<MultiSelectPopover
label="Issue Types"
options={issueTypeOptions}
values={selectedIssueTypes}
onValuesChange={setSelectedIssueTypes}
searchPlaceholder="Search issue types"
emptyMessage="No issue types found."
align="start"
/>
</div>
<div className="relative w-full sm:ml-auto sm:w-72">
<Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
value={detectionSearch}
onChange={(event) => setDetectionSearch(event.target.value)}
placeholder="Search by detection ID"
aria-label="Search by detection ID"
className="pl-9"
/>
</div>
<div className="min-w-0 space-y-5">
<div
aria-label="Issue filters"
className="flex flex-col gap-3 sm:flex-row sm:items-center"
>
<div className="w-fit">
<MultiSelectPopover
label="Issue Types"
options={issueTypeOptions}
values={selectedIssueTypes}
onValuesChange={setSelectedIssueTypes}
searchPlaceholder="Search issue types"
emptyMessage="No issue types found."
align="start"
/>
</div>
}
skip={skip}
limit={limit}
total={result?.total ?? 0}
onPageChange={setSkip}
onLimitChange={setLimit}
/>
<div className="relative w-full sm:ml-auto sm:w-72">
<Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
id="assignment-issue-search"
value={detectionSearch}
onChange={(event) => setDetectionSearch(event.target.value)}
placeholder="Search by detection ID"
aria-label="Search by detection ID"
className="pl-9"
/>
</div>
</div>
<AssignmentIssuesMap
data={mapData}
isLoading={coordinatesQuery.isLoading}
isFetchingMore={coordinatesQuery.isFetchingNextPage}
isError={coordinatesQuery.isError}
hasMore={Boolean(coordinatesQuery.hasNextPage)}
onLoadMore={() => void coordinatesQuery.fetchNextPage()}
onRetry={() => void coordinatesQuery.refetch()}
/>
<IssueTable
columns={columns}
issues={result?.items ?? []}
isLoading={detectionsQuery.isLoading || detectionsQuery.isFetching}
isError={detectionsQuery.isError}
toolbar={null}
skip={skip}
limit={limit}
total={result?.total ?? 0}
onPageChange={setSkip}
onLimitChange={setLimit}
/>
</div>
);
}