feat(ticket): use assignment detections API for unassigned issues
This commit is contained in:
@@ -28,7 +28,6 @@ import {
|
|||||||
|
|
||||||
interface AssignTicketFormProps {
|
interface AssignTicketFormProps {
|
||||||
ticketId: string;
|
ticketId: string;
|
||||||
videoId?: string;
|
|
||||||
selectedClassNames: string[];
|
selectedClassNames: string[];
|
||||||
startPoint: AssignmentRangePoint | null;
|
startPoint: AssignmentRangePoint | null;
|
||||||
endPoint: AssignmentRangePoint | null;
|
endPoint: AssignmentRangePoint | null;
|
||||||
@@ -37,7 +36,6 @@ interface AssignTicketFormProps {
|
|||||||
|
|
||||||
export function AssignTicketForm({
|
export function AssignTicketForm({
|
||||||
ticketId,
|
ticketId,
|
||||||
videoId,
|
|
||||||
selectedClassNames,
|
selectedClassNames,
|
||||||
startPoint,
|
startPoint,
|
||||||
endPoint,
|
endPoint,
|
||||||
@@ -49,7 +47,7 @@ export function AssignTicketForm({
|
|||||||
const [dueDate, setDueDate] = useState<Date>();
|
const [dueDate, setDueDate] = useState<Date>();
|
||||||
const [assignNote, setAssignNote] = useState('');
|
const [assignNote, setAssignNote] = useState('');
|
||||||
|
|
||||||
const assignMutation = useAssignTicketMutation(ticketId, videoId);
|
const assignMutation = useAssignTicketMutation(ticketId);
|
||||||
const today = useMemo(() => {
|
const today = useMemo(() => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setHours(0, 0, 0, 0);
|
date.setHours(0, 0, 0, 0);
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import dynamic from 'next/dynamic';
|
|||||||
import { AlertTriangle, MapPinned, RefreshCw } from 'lucide-react';
|
import { AlertTriangle, MapPinned, RefreshCw } from 'lucide-react';
|
||||||
|
|
||||||
import { getDefectVisual } from '@/constants/defectVisualConfig';
|
import { getDefectVisual } from '@/constants/defectVisualConfig';
|
||||||
import type { DetectionResultItem, VideoDetectionsResponse } from '@/types';
|
import type {
|
||||||
|
TicketAssignmentDetectionItem,
|
||||||
|
TicketAssignmentDetectionsResponse,
|
||||||
|
} from '@/types';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -18,7 +21,7 @@ import {
|
|||||||
import type { AssignmentRangePoint } from './assignmentRange';
|
import type { AssignmentRangePoint } from './assignmentRange';
|
||||||
|
|
||||||
type AssignmentIssuesMapProps = {
|
type AssignmentIssuesMapProps = {
|
||||||
data?: VideoDetectionsResponse;
|
data?: TicketAssignmentDetectionsResponse;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isError: boolean;
|
isError: boolean;
|
||||||
startPoint: AssignmentRangePoint | null;
|
startPoint: AssignmentRangePoint | null;
|
||||||
@@ -32,7 +35,6 @@ type AssignmentMapItem = {
|
|||||||
display_name: string;
|
display_name: string;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
confidence: number;
|
|
||||||
sequence: number;
|
sequence: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,7 +52,10 @@ type ClientAssignmentIssuesMapProps = {
|
|||||||
onSelectDetection: (detectionId: number) => void;
|
onSelectDetection: (detectionId: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function toMapItem(item: DetectionResultItem): AssignmentMapItem | null {
|
function toMapItem(
|
||||||
|
item: TicketAssignmentDetectionItem,
|
||||||
|
sequence: number,
|
||||||
|
): AssignmentMapItem | null {
|
||||||
const { latitude, longitude } = item.location;
|
const { latitude, longitude } = item.location;
|
||||||
if (
|
if (
|
||||||
typeof latitude !== 'number' ||
|
typeof latitude !== 'number' ||
|
||||||
@@ -71,8 +76,7 @@ function toMapItem(item: DetectionResultItem): AssignmentMapItem | null {
|
|||||||
display_name: item.detection.display_name,
|
display_name: item.detection.display_name,
|
||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
confidence: item.detection.confidence,
|
sequence,
|
||||||
sequence: item.frame.timestamp_seconds,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,12 +267,6 @@ const ClientAssignmentIssuesMap = dynamic<ClientAssignmentIssuesMapProps>(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<dl className="!m-0 space-y-2 py-3 text-sm">
|
<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">
|
<div className="flex items-center justify-between gap-4">
|
||||||
<dt className="text-muted-foreground">Latitude</dt>
|
<dt className="text-muted-foreground">Latitude</dt>
|
||||||
<dd className="!m-0 font-mono text-xs tabular-nums">
|
<dd className="!m-0 font-mono text-xs tabular-nums">
|
||||||
@@ -321,8 +319,8 @@ export function AssignmentIssuesMap({
|
|||||||
);
|
);
|
||||||
const validItems = useMemo(
|
const validItems = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(data?.items ?? []).flatMap((item) => {
|
(data?.items ?? []).flatMap((item, index) => {
|
||||||
const mapItem = toMapItem(item);
|
const mapItem = toMapItem(item, index);
|
||||||
return mapItem ? [mapItem] : [];
|
return mapItem ? [mapItem] : [];
|
||||||
}),
|
}),
|
||||||
[data?.items],
|
[data?.items],
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { MultiSelectPopover } from '@/components/form/MultiSelectPopover';
|
|||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
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 {
|
import {
|
||||||
formatCompactRangeCoordinate,
|
formatCompactRangeCoordinate,
|
||||||
formatRangeCoordinate,
|
formatRangeCoordinate,
|
||||||
@@ -28,7 +28,7 @@ interface IssueTypeOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ChooseIssuesSectionProps {
|
interface ChooseIssuesSectionProps {
|
||||||
videoId?: string;
|
ticketId: string;
|
||||||
issueTypes: IssueTypeOption[];
|
issueTypes: IssueTypeOption[];
|
||||||
selectedIssueTypes: string[];
|
selectedIssueTypes: string[];
|
||||||
cacheRevision: number;
|
cacheRevision: number;
|
||||||
@@ -40,7 +40,7 @@ interface ChooseIssuesSectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ChooseIssuesSection({
|
export function ChooseIssuesSection({
|
||||||
videoId,
|
ticketId,
|
||||||
issueTypes,
|
issueTypes,
|
||||||
selectedIssueTypes,
|
selectedIssueTypes,
|
||||||
cacheRevision,
|
cacheRevision,
|
||||||
@@ -101,22 +101,25 @@ export function ChooseIssuesSection({
|
|||||||
|
|
||||||
const queryParams = useMemo(
|
const queryParams = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
assignment_status: 'unassigned' as const,
|
||||||
skip,
|
skip,
|
||||||
limit,
|
limit,
|
||||||
class_name:
|
class_name:
|
||||||
selectedIssueTypes.length > 0 ? selectedIssueTypes : undefined,
|
selectedIssueTypes.length > 0 ? selectedIssueTypes : undefined,
|
||||||
sort: 'timestamp_asc',
|
|
||||||
}),
|
}),
|
||||||
[limit, selectedIssueTypes, skip],
|
[limit, selectedIssueTypes, skip],
|
||||||
);
|
);
|
||||||
const detectionsQuery = useTicketDetectionsQuery(videoId, queryParams);
|
const detectionsQuery = useTicketAssignmentDetectionsQuery(
|
||||||
|
ticketId,
|
||||||
|
queryParams,
|
||||||
|
);
|
||||||
const result = detectionsQuery.data;
|
const result = detectionsQuery.data;
|
||||||
const mapCacheKey = useMemo(
|
const mapCacheKey = useMemo(
|
||||||
() => JSON.stringify([videoId ?? '', selectedIssueTypes, cacheRevision]),
|
() => JSON.stringify([ticketId, selectedIssueTypes, cacheRevision]),
|
||||||
[cacheRevision, selectedIssueTypes, videoId],
|
[cacheRevision, selectedIssueTypes, ticketId],
|
||||||
);
|
);
|
||||||
const [mapCache, setMapCache] = useState<
|
const [mapCache, setMapCache] = useState<
|
||||||
Record<string, VideoDetectionsResponse>
|
Record<string, TicketAssignmentDetectionsResponse>
|
||||||
>({});
|
>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -5,17 +5,11 @@ import type { ColumnDef } from '@tanstack/react-table';
|
|||||||
|
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { getDefectVisual } from '@/constants/defectVisualConfig';
|
import { getDefectVisual } from '@/constants/defectVisualConfig';
|
||||||
import type { DetectionResultItem } from '@/types';
|
import type { TicketAssignmentDetectionItem } from '@/types';
|
||||||
|
|
||||||
import type { AssignmentRangePoint } from './assignmentRange';
|
import type { AssignmentRangePoint } from './assignmentRange';
|
||||||
import { AssignmentRangeActions } from './AssignmentRangeActions';
|
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 {
|
interface UseIssueColumnsOptions {
|
||||||
startPoint: AssignmentRangePoint | null;
|
startPoint: AssignmentRangePoint | null;
|
||||||
endPoint: AssignmentRangePoint | null;
|
endPoint: AssignmentRangePoint | null;
|
||||||
@@ -28,7 +22,7 @@ export function useIssueColumns({
|
|||||||
endPoint,
|
endPoint,
|
||||||
onSetStart,
|
onSetStart,
|
||||||
onSetEnd,
|
onSetEnd,
|
||||||
}: UseIssueColumnsOptions): ColumnDef<DetectionResultItem>[] {
|
}: UseIssueColumnsOptions): ColumnDef<TicketAssignmentDetectionItem>[] {
|
||||||
return useMemo(
|
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',
|
id: 'range_actions',
|
||||||
header: 'Set range',
|
header: 'Set range',
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import type { ReactNode } from 'react';
|
|||||||
import type { ColumnDef } from '@tanstack/react-table';
|
import type { ColumnDef } from '@tanstack/react-table';
|
||||||
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import type { DetectionResultItem } from '@/types';
|
import type { TicketAssignmentDetectionItem } from '@/types';
|
||||||
|
|
||||||
interface IssueTableProps {
|
interface IssueTableProps {
|
||||||
columns: ColumnDef<DetectionResultItem>[];
|
columns: ColumnDef<TicketAssignmentDetectionItem>[];
|
||||||
issues: DetectionResultItem[];
|
issues: TicketAssignmentDetectionItem[];
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isError: boolean;
|
isError: boolean;
|
||||||
toolbar: ReactNode;
|
toolbar: ReactNode;
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ export default function TicketAssignmentPage() {
|
|||||||
const [assignmentRevision, setAssignmentRevision] = useState(0);
|
const [assignmentRevision, setAssignmentRevision] = useState(0);
|
||||||
const overviewQuery = useTicketOverviewQuery(ticketId);
|
const overviewQuery = useTicketOverviewQuery(ticketId);
|
||||||
const overview = overviewQuery.data;
|
const overview = overviewQuery.data;
|
||||||
const videoId = overview?.video_id ?? overview?.video?.id ?? undefined;
|
|
||||||
const ticketDetailUrl = ROUTES.TICKET_DETAIL(ticketId);
|
const ticketDetailUrl = ROUTES.TICKET_DETAIL(ticketId);
|
||||||
const backToTicket = () => router.push(ticketDetailUrl);
|
const backToTicket = () => router.push(ticketDetailUrl);
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ export default function TicketAssignmentPage() {
|
|||||||
<div className="grid min-w-0 items-start gap-5 xl:min-h-0 xl:flex-1 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-stretch xl:overflow-hidden">
|
<div className="grid min-w-0 items-start gap-5 xl:min-h-0 xl:flex-1 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-stretch xl:overflow-hidden">
|
||||||
<div className="min-w-0 xl:h-full xl:min-h-0 xl:overflow-y-auto xl:overscroll-contain xl:pr-2">
|
<div className="min-w-0 xl:h-full xl:min-h-0 xl:overflow-y-auto xl:overscroll-contain xl:pr-2">
|
||||||
<ChooseIssuesSection
|
<ChooseIssuesSection
|
||||||
videoId={videoId}
|
ticketId={ticketId}
|
||||||
issueTypes={overview.ai_result.detections_by_class}
|
issueTypes={overview.ai_result.detections_by_class}
|
||||||
selectedIssueTypes={selectedIssueTypes}
|
selectedIssueTypes={selectedIssueTypes}
|
||||||
cacheRevision={assignmentRevision}
|
cacheRevision={assignmentRevision}
|
||||||
@@ -146,7 +145,6 @@ export default function TicketAssignmentPage() {
|
|||||||
>
|
>
|
||||||
<AssignTicketForm
|
<AssignTicketForm
|
||||||
ticketId={ticketId}
|
ticketId={ticketId}
|
||||||
videoId={videoId}
|
|
||||||
selectedClassNames={selectedIssueTypes}
|
selectedClassNames={selectedIssueTypes}
|
||||||
startPoint={startPoint}
|
startPoint={startPoint}
|
||||||
endPoint={endPoint}
|
endPoint={endPoint}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import type {
|
|||||||
RequestTicketExtensionPayload,
|
RequestTicketExtensionPayload,
|
||||||
ReviewDetectionRepairProofPayload,
|
ReviewDetectionRepairProofPayload,
|
||||||
TicketAssignmentSummary,
|
TicketAssignmentSummary,
|
||||||
|
TicketAssignmentDetectionsParams,
|
||||||
TicketListParams,
|
TicketListParams,
|
||||||
DetectionProofStatus,
|
DetectionProofStatus,
|
||||||
VideoDetectionsParams,
|
VideoDetectionsParams,
|
||||||
@@ -136,6 +137,32 @@ export function useTicketClassDetectionsQuery(
|
|||||||
return useTicketDetectionsQuery(videoId, params);
|
return useTicketDetectionsQuery(videoId, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useTicketAssignmentDetectionsQuery(
|
||||||
|
ticketId: string | undefined,
|
||||||
|
params: TicketAssignmentDetectionsParams,
|
||||||
|
) {
|
||||||
|
const queryParams = useMemo(
|
||||||
|
() => ({
|
||||||
|
assignment_status: params.assignment_status ?? 'unassigned',
|
||||||
|
class_name: params.class_name,
|
||||||
|
skip: params.skip ?? 0,
|
||||||
|
limit: params.limit ?? 500,
|
||||||
|
}),
|
||||||
|
[params.assignment_status, params.class_name, params.limit, params.skip],
|
||||||
|
);
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ticketKeys.assignmentDetections(ticketId ?? '', queryParams),
|
||||||
|
queryFn: () =>
|
||||||
|
ticketService.getTicketAssignmentDetections(
|
||||||
|
ticketId as string,
|
||||||
|
queryParams,
|
||||||
|
),
|
||||||
|
enabled: Boolean(ticketId),
|
||||||
|
staleTime: Infinity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function useTicketReviewDetectionsQuery(
|
export function useTicketReviewDetectionsQuery(
|
||||||
videoId: string | undefined,
|
videoId: string | undefined,
|
||||||
proofStatus?: DetectionProofStatus,
|
proofStatus?: DetectionProofStatus,
|
||||||
@@ -262,7 +289,7 @@ export function useAssignableTicketUsersQuery(enabled: boolean) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAssignTicketMutation(ticketId: string, videoId?: string) {
|
export function useAssignTicketMutation(ticketId: string) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@@ -278,13 +305,12 @@ export function useAssignTicketMutation(ticketId: string, videoId?: string) {
|
|||||||
queryKey: ticketKeys.timeline(ticketId),
|
queryKey: ticketKeys.timeline(ticketId),
|
||||||
}),
|
}),
|
||||||
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() }),
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() }),
|
||||||
...(videoId
|
|
||||||
? [
|
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ticketKeys.classDetectionLists(videoId),
|
queryKey: ticketKeys.assignmentDetectionLists(ticketId),
|
||||||
|
}),
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ticketKeys.assignments(ticketId),
|
||||||
}),
|
}),
|
||||||
]
|
|
||||||
: []),
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
onError: () => toast.error('Failed to assign ticket'),
|
onError: () => toast.error('Failed to assign ticket'),
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import type { TicketListParams, VideoDetectionsParams } from '@/types';
|
import type {
|
||||||
|
TicketAssignmentDetectionsParams,
|
||||||
|
TicketListParams,
|
||||||
|
VideoDetectionsParams,
|
||||||
|
} from '@/types';
|
||||||
|
|
||||||
export const ticketKeys = {
|
export const ticketKeys = {
|
||||||
all: ['tickets'] as const,
|
all: ['tickets'] as const,
|
||||||
@@ -19,6 +23,12 @@ export const ticketKeys = {
|
|||||||
'review',
|
'review',
|
||||||
proofStatus ?? 'all',
|
proofStatus ?? 'all',
|
||||||
] as const,
|
] as const,
|
||||||
|
assignmentDetectionLists: (ticketId: string) =>
|
||||||
|
[...ticketKeys.details(), ticketId, 'assignment-detections'] as const,
|
||||||
|
assignmentDetections: (
|
||||||
|
ticketId: string,
|
||||||
|
params: TicketAssignmentDetectionsParams,
|
||||||
|
) => [...ticketKeys.assignmentDetectionLists(ticketId), params] as const,
|
||||||
assignments: (ticketId: string) =>
|
assignments: (ticketId: string) =>
|
||||||
[...ticketKeys.details(), ticketId, 'assignments'] as const,
|
[...ticketKeys.details(), ticketId, 'assignments'] as const,
|
||||||
extensionRequest: (ticketId: string, assignmentId: number | undefined) =>
|
extensionRequest: (ticketId: string, assignmentId: number | undefined) =>
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ export const API_ROUTES = {
|
|||||||
TICKETS: {
|
TICKETS: {
|
||||||
BASE: '/biz/api/v1/tickets',
|
BASE: '/biz/api/v1/tickets',
|
||||||
DETAIL: (id: string) => `/biz/api/v1/tickets/${id}`,
|
DETAIL: (id: string) => `/biz/api/v1/tickets/${id}`,
|
||||||
|
ASSIGNMENT_DETECTIONS: (id: string) =>
|
||||||
|
`/biz/api/v1/tickets/${id}/assignment-detections`,
|
||||||
TIMELINE: (id: string) => `/biz/api/v1/tickets/${id}/timeline`,
|
TIMELINE: (id: string) => `/biz/api/v1/tickets/${id}/timeline`,
|
||||||
ASSIGNMENTS: (id: string) => `/biz/api/v1/tickets/${id}/assignments`,
|
ASSIGNMENTS: (id: string) => `/biz/api/v1/tickets/${id}/assignments`,
|
||||||
REQUEST_EXTENSION: (ticketId: string, assignmentId: number) =>
|
REQUEST_EXTENSION: (ticketId: string, assignmentId: number) =>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import type {
|
|||||||
ReviewTicketExtensionPayload,
|
ReviewTicketExtensionPayload,
|
||||||
SseTokenResponse,
|
SseTokenResponse,
|
||||||
TicketAssignmentSummary,
|
TicketAssignmentSummary,
|
||||||
|
TicketAssignmentDetectionsParams,
|
||||||
|
TicketAssignmentDetectionsResponse,
|
||||||
TicketAssignmentsResponse,
|
TicketAssignmentsResponse,
|
||||||
TicketExtensionRequestsResponse,
|
TicketExtensionRequestsResponse,
|
||||||
TicketExtensionRequestsParams,
|
TicketExtensionRequestsParams,
|
||||||
@@ -52,6 +54,27 @@ export const ticketService = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getTicketAssignmentDetections: async (
|
||||||
|
ticketId: string,
|
||||||
|
params?: TicketAssignmentDetectionsParams,
|
||||||
|
): Promise<TicketAssignmentDetectionsResponse> => {
|
||||||
|
const response = await axiosClient.get<TicketAssignmentDetectionsResponse>(
|
||||||
|
API_ROUTES.TICKETS.ASSIGNMENT_DETECTIONS(ticketId),
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
assignment_status: params?.assignment_status ?? 'unassigned',
|
||||||
|
class_name: params?.class_name,
|
||||||
|
skip: params?.skip ?? 0,
|
||||||
|
limit: params?.limit ?? 500,
|
||||||
|
},
|
||||||
|
paramsSerializer: {
|
||||||
|
indexes: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
getTicketTimeline: async (
|
getTicketTimeline: async (
|
||||||
ticketId: string,
|
ticketId: string,
|
||||||
): Promise<TicketTimelineResponse> => {
|
): Promise<TicketTimelineResponse> => {
|
||||||
|
|||||||
@@ -104,6 +104,37 @@ export type VideoDetectionsResponse = {
|
|||||||
items: DetectionResultItem[];
|
items: DetectionResultItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AssignmentDetectionStatus = 'assigned' | 'unassigned';
|
||||||
|
|
||||||
|
export type TicketAssignmentDetectionsParams = {
|
||||||
|
assignment_status?: AssignmentDetectionStatus;
|
||||||
|
class_name?: string | string[];
|
||||||
|
skip?: number;
|
||||||
|
limit?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TicketAssignmentDetectionItem = {
|
||||||
|
id: string;
|
||||||
|
detection: {
|
||||||
|
id: number;
|
||||||
|
class_name: string;
|
||||||
|
display_name: string;
|
||||||
|
};
|
||||||
|
location: {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TicketAssignmentDetectionsResponse = {
|
||||||
|
ticket_id: string;
|
||||||
|
assignment_status: AssignmentDetectionStatus;
|
||||||
|
items: TicketAssignmentDetectionItem[];
|
||||||
|
total: number;
|
||||||
|
skip: number;
|
||||||
|
limit: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type DetectionDiscardReason =
|
export type DetectionDiscardReason =
|
||||||
| 'not_a_defect'
|
| 'not_a_defect'
|
||||||
| 'wrong_class'
|
| 'wrong_class'
|
||||||
|
|||||||
Reference in New Issue
Block a user