177 lines
4.5 KiB
TypeScript
177 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useMemo, useRef } from 'react';
|
|
import { useQueryClient } from '@tanstack/react-query';
|
|
|
|
import { API_ROUTES } from '@/constants/apiRoutes';
|
|
import { useSseWithToken } from '@/hooks/sse/useSseWithToken';
|
|
import { ticketService } from '@/services/api';
|
|
import type {
|
|
TicketListItem,
|
|
TicketListParams,
|
|
TicketListResponse,
|
|
TicketTableStatusEvent,
|
|
} from '@/types';
|
|
|
|
import { ticketKeys } from '../queries/ticketKeys';
|
|
|
|
function patchTicketLists(
|
|
current: TicketListResponse | undefined,
|
|
event: TicketTableStatusEvent,
|
|
params?: TicketListParams,
|
|
) {
|
|
if (!current || !event.id) return current;
|
|
|
|
const nextRow = buildTicketListItem(event);
|
|
const existingIndex = current.items.findIndex(
|
|
(ticket) => ticket.id === event.id,
|
|
);
|
|
|
|
if (existingIndex >= 0) {
|
|
return {
|
|
...current,
|
|
items: current.items.map((ticket) =>
|
|
ticket.id === event.id ? mergeTicketListItem(ticket, event) : ticket,
|
|
),
|
|
};
|
|
}
|
|
|
|
if (event.kind !== 'created') return current;
|
|
if (!shouldCountCreatedTicket(event, params)) return current;
|
|
|
|
if (!nextRow || !isFirstPage(params)) {
|
|
return {
|
|
...current,
|
|
total: current.total + 1,
|
|
};
|
|
}
|
|
|
|
const pageLimit = params?.limit ?? current.items.length + 1;
|
|
|
|
return {
|
|
...current,
|
|
items: [nextRow, ...current.items].slice(0, pageLimit),
|
|
total: current.total + 1,
|
|
};
|
|
}
|
|
|
|
function shouldCountCreatedTicket(
|
|
event: TicketTableStatusEvent,
|
|
params?: TicketListParams,
|
|
) {
|
|
if (params?.chainage_id && event.chainage_id !== params.chainage_id) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function isFirstPage(params?: TicketListParams) {
|
|
return (params?.skip ?? 0) === 0;
|
|
}
|
|
|
|
function getTicketListParams(queryKey: readonly unknown[]) {
|
|
const params = queryKey[2];
|
|
|
|
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
return undefined;
|
|
}
|
|
|
|
return params as TicketListParams;
|
|
}
|
|
|
|
function buildTicketListItem(
|
|
event: TicketTableStatusEvent,
|
|
): TicketListItem | null {
|
|
if (
|
|
!event.id ||
|
|
!event.ticket_name ||
|
|
event.detection_count === undefined ||
|
|
!event.updated_at
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
id: event.id,
|
|
ticket_name: event.ticket_name,
|
|
chainage_id: event.chainage_id ?? null,
|
|
chainage_name: event.chainage_name ?? null,
|
|
default_defect_class: event.default_defect_class ?? null,
|
|
detection_count: event.detection_count,
|
|
created_by_name: event.created_by_name ?? null,
|
|
created_at: event.created_at ?? event.updated_at,
|
|
updated_at: event.updated_at,
|
|
};
|
|
}
|
|
|
|
function mergeTicketListItem(
|
|
current: TicketListItem,
|
|
event: TicketTableStatusEvent,
|
|
): TicketListItem {
|
|
return {
|
|
...current,
|
|
ticket_name: event.ticket_name ?? current.ticket_name,
|
|
chainage_id: event.chainage_id ?? current.chainage_id,
|
|
chainage_name: event.chainage_name ?? current.chainage_name,
|
|
default_defect_class:
|
|
event.default_defect_class ?? current.default_defect_class,
|
|
detection_count: event.detection_count ?? current.detection_count,
|
|
created_by_name: event.created_by_name ?? current.created_by_name,
|
|
created_at: event.created_at ?? current.created_at,
|
|
updated_at: event.updated_at ?? current.updated_at,
|
|
};
|
|
}
|
|
|
|
export function useTenantTicketTableEvents(enabled = true) {
|
|
const queryClient = useQueryClient();
|
|
const hasInvalidatedAfterConnectionErrorRef = useRef(false);
|
|
|
|
const events = useMemo(
|
|
() => ({
|
|
ticket_status: (event: TicketTableStatusEvent) => {
|
|
queryClient
|
|
.getQueryCache()
|
|
.findAll({ queryKey: ticketKeys.lists() })
|
|
.forEach((query) => {
|
|
queryClient.setQueryData<TicketListResponse>(
|
|
query.queryKey,
|
|
(current) =>
|
|
patchTicketLists(
|
|
current,
|
|
event,
|
|
getTicketListParams(query.queryKey),
|
|
),
|
|
);
|
|
});
|
|
},
|
|
}),
|
|
[queryClient],
|
|
);
|
|
|
|
const getPath = useCallback(
|
|
(token: string) => API_ROUTES.TICKET_EVENTS.TENANT(token),
|
|
[],
|
|
);
|
|
|
|
const onConnectionError = useCallback(() => {
|
|
if (hasInvalidatedAfterConnectionErrorRef.current) return;
|
|
|
|
hasInvalidatedAfterConnectionErrorRef.current = true;
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
}, [queryClient]);
|
|
|
|
const onConnectionOpen = useCallback(() => {
|
|
hasInvalidatedAfterConnectionErrorRef.current = false;
|
|
}, []);
|
|
|
|
useSseWithToken({
|
|
enabled,
|
|
getToken: ticketService.createTenantTicketEventsToken,
|
|
getPath,
|
|
events,
|
|
onConnectionError,
|
|
onConnectionOpen,
|
|
});
|
|
}
|