feat(ticket): use assignment detections API for unassigned issues

This commit is contained in:
2026-08-04 15:46:22 +05:30
parent 6169bbdc28
commit bad9c50dbf
11 changed files with 132 additions and 56 deletions

View File

@@ -28,7 +28,6 @@ import {
interface AssignTicketFormProps {
ticketId: string;
videoId?: string;
selectedClassNames: string[];
startPoint: AssignmentRangePoint | null;
endPoint: AssignmentRangePoint | null;
@@ -37,7 +36,6 @@ interface AssignTicketFormProps {
export function AssignTicketForm({
ticketId,
videoId,
selectedClassNames,
startPoint,
endPoint,
@@ -49,7 +47,7 @@ export function AssignTicketForm({
const [dueDate, setDueDate] = useState<Date>();
const [assignNote, setAssignNote] = useState('');
const assignMutation = useAssignTicketMutation(ticketId, videoId);
const assignMutation = useAssignTicketMutation(ticketId);
const today = useMemo(() => {
const date = new Date();
date.setHours(0, 0, 0, 0);

View File

@@ -5,7 +5,10 @@ import dynamic from 'next/dynamic';
import { AlertTriangle, MapPinned, RefreshCw } from 'lucide-react';
import { getDefectVisual } from '@/constants/defectVisualConfig';
import type { DetectionResultItem, VideoDetectionsResponse } from '@/types';
import type {
TicketAssignmentDetectionItem,
TicketAssignmentDetectionsResponse,
} from '@/types';
import { Button } from '@/components/ui/button';
import {
Card,
@@ -18,7 +21,7 @@ import {
import type { AssignmentRangePoint } from './assignmentRange';
type AssignmentIssuesMapProps = {
data?: VideoDetectionsResponse;
data?: TicketAssignmentDetectionsResponse;
isLoading: boolean;
isError: boolean;
startPoint: AssignmentRangePoint | null;
@@ -32,7 +35,6 @@ type AssignmentMapItem = {
display_name: string;
latitude: number;
longitude: number;
confidence: number;
sequence: number;
};
@@ -50,7 +52,10 @@ type ClientAssignmentIssuesMapProps = {
onSelectDetection: (detectionId: number) => void;
};
function toMapItem(item: DetectionResultItem): AssignmentMapItem | null {
function toMapItem(
item: TicketAssignmentDetectionItem,
sequence: number,
): AssignmentMapItem | null {
const { latitude, longitude } = item.location;
if (
typeof latitude !== 'number' ||
@@ -71,8 +76,7 @@ function toMapItem(item: DetectionResultItem): AssignmentMapItem | null {
display_name: item.detection.display_name,
latitude,
longitude,
confidence: item.detection.confidence,
sequence: item.frame.timestamp_seconds,
sequence,
};
}
@@ -263,12 +267,6 @@ const ClientAssignmentIssuesMap = dynamic<ClientAssignmentIssuesMapProps>(
</div>
<dl className="!m-0 space-y-2 py-3 text-sm">
<div className="flex items-center justify-between gap-4">
<dt className="text-muted-foreground">Confidence</dt>
<dd className="!m-0 font-medium tabular-nums">
{Math.round(item.confidence * 100)}%
</dd>
</div>
<div className="flex items-center justify-between gap-4">
<dt className="text-muted-foreground">Latitude</dt>
<dd className="!m-0 font-mono text-xs tabular-nums">
@@ -321,8 +319,8 @@ export function AssignmentIssuesMap({
);
const validItems = useMemo(
() =>
(data?.items ?? []).flatMap((item) => {
const mapItem = toMapItem(item);
(data?.items ?? []).flatMap((item, index) => {
const mapItem = toMapItem(item, index);
return mapItem ? [mapItem] : [];
}),
[data?.items],

View File

@@ -7,9 +7,9 @@ import { MultiSelectPopover } from '@/components/form/MultiSelectPopover';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import type { VideoDetectionsResponse } from '@/types';
import type { TicketAssignmentDetectionsResponse } from '@/types';
import { useTicketDetectionsQuery } from '../../../hooks/useTicketQueries';
import { useTicketAssignmentDetectionsQuery } from '../../../hooks/useTicketQueries';
import {
formatCompactRangeCoordinate,
formatRangeCoordinate,
@@ -28,7 +28,7 @@ interface IssueTypeOption {
}
interface ChooseIssuesSectionProps {
videoId?: string;
ticketId: string;
issueTypes: IssueTypeOption[];
selectedIssueTypes: string[];
cacheRevision: number;
@@ -40,7 +40,7 @@ interface ChooseIssuesSectionProps {
}
export function ChooseIssuesSection({
videoId,
ticketId,
issueTypes,
selectedIssueTypes,
cacheRevision,
@@ -101,22 +101,25 @@ export function ChooseIssuesSection({
const queryParams = useMemo(
() => ({
assignment_status: 'unassigned' as const,
skip,
limit,
class_name:
selectedIssueTypes.length > 0 ? selectedIssueTypes : undefined,
sort: 'timestamp_asc',
}),
[limit, selectedIssueTypes, skip],
);
const detectionsQuery = useTicketDetectionsQuery(videoId, queryParams);
const detectionsQuery = useTicketAssignmentDetectionsQuery(
ticketId,
queryParams,
);
const result = detectionsQuery.data;
const mapCacheKey = useMemo(
() => JSON.stringify([videoId ?? '', selectedIssueTypes, cacheRevision]),
[cacheRevision, selectedIssueTypes, videoId],
() => JSON.stringify([ticketId, selectedIssueTypes, cacheRevision]),
[cacheRevision, selectedIssueTypes, ticketId],
);
const [mapCache, setMapCache] = useState<
Record<string, VideoDetectionsResponse>
Record<string, TicketAssignmentDetectionsResponse>
>({});
useEffect(() => {

View File

@@ -5,17 +5,11 @@ import type { ColumnDef } from '@tanstack/react-table';
import { Badge } from '@/components/ui/badge';
import { getDefectVisual } from '@/constants/defectVisualConfig';
import type { DetectionResultItem } from '@/types';
import type { TicketAssignmentDetectionItem } from '@/types';
import type { AssignmentRangePoint } from './assignmentRange';
import { AssignmentRangeActions } from './AssignmentRangeActions';
function formatTimestamp(seconds: number) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
}
interface UseIssueColumnsOptions {
startPoint: AssignmentRangePoint | null;
endPoint: AssignmentRangePoint | null;
@@ -28,7 +22,7 @@ export function useIssueColumns({
endPoint,
onSetStart,
onSetEnd,
}: UseIssueColumnsOptions): ColumnDef<DetectionResultItem>[] {
}: UseIssueColumnsOptions): ColumnDef<TicketAssignmentDetectionItem>[] {
return useMemo(
() => [
{
@@ -89,13 +83,6 @@ export function useIssueColumns({
);
},
},
{
id: 'timestamp',
header: 'Timestamp',
size: 130,
cell: ({ row }) =>
formatTimestamp(row.original.frame.timestamp_seconds),
},
{
id: 'range_actions',
header: 'Set range',

View File

@@ -4,11 +4,11 @@ import type { ReactNode } from 'react';
import type { ColumnDef } from '@tanstack/react-table';
import { DataTable } from '@/components/data-table';
import type { DetectionResultItem } from '@/types';
import type { TicketAssignmentDetectionItem } from '@/types';
interface IssueTableProps {
columns: ColumnDef<DetectionResultItem>[];
issues: DetectionResultItem[];
columns: ColumnDef<TicketAssignmentDetectionItem>[];
issues: TicketAssignmentDetectionItem[];
isLoading: boolean;
isError: boolean;
toolbar: ReactNode;