feat(platform): harden telemetry pipeline and unify Semi UI workspaces
This commit is contained in:
@@ -18,7 +18,13 @@ describe('monitor query params', () => {
|
||||
it('drops stale viewport bounds for a direct vehicle search', () => {
|
||||
const viewport = { zoom: 13, bounds: '103,29,105,31' };
|
||||
expect(monitorMapQueryParams({ keyword: '粤A1', protocol: '', status: '' }, viewport).has('bounds')).toBe(false);
|
||||
expect(monitorMapQueryParams({ keyword: '', protocol: '', status: '' }, viewport).get('bounds')).toBe(viewport.bounds);
|
||||
expect(monitorMapQueryParams({ keyword: '', protocol: '', status: '' }, viewport).get('bounds')).toBe('103.000000,29.000000,105.000000,31.000000');
|
||||
});
|
||||
|
||||
it('omits wrapped or out-of-range viewport hints instead of failing the map query', () => {
|
||||
for (const bounds of ['105,31,103,29', '-181,0,181,1', '103,29,Infinity,31']) {
|
||||
expect(monitorMapQueryParams({ keyword: '', protocol: '', status: '' }, { zoom: 5, bounds }).has('bounds')).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('normalizes pasted plates, removes duplicates, and sends one batch parameter', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { api } from '../../api/client';
|
||||
import type { MonitorMapResponse, MonitorSummary, MonitorWorkspaceResponse, Page, VehicleRealtimeRow } from '../../api/types';
|
||||
import { normalizeMonitorBounds } from '../domain/monitor';
|
||||
import { LIVE_QUERY_POLICY, QUERY_MEMORY, retainPreviousPageWithinScope } from '../queryPolicy';
|
||||
|
||||
export type MonitorFilters = {
|
||||
@@ -116,7 +117,8 @@ export function monitorQueryParams(filters: MonitorFilters, limit: number) {
|
||||
export function monitorMapQueryParams(filters: MonitorFilters, viewport: MonitorViewport) {
|
||||
const params = monitorQueryParams(filters, 10_000);
|
||||
params.set('zoom', String(viewport.zoom));
|
||||
if (viewport.bounds && parseMonitorSearchTerms(filters.keyword).length === 0) params.set('bounds', viewport.bounds);
|
||||
const bounds = normalizeMonitorBounds(viewport.bounds);
|
||||
if (bounds && parseMonitorSearchTerms(filters.keyword).length === 0) params.set('bounds', bounds);
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -203,6 +205,15 @@ export function useMonitorVehicleCard(vin: string, vehicle?: VehicleRealtimeRow,
|
||||
gcTime: QUERY_MEMORY.highVolumeGcTime,
|
||||
...LIVE_QUERY_POLICY
|
||||
});
|
||||
const telemetry = useQuery({
|
||||
queryKey: ['monitor', 'vehicle-card', 'telemetry', vin],
|
||||
queryFn: ({ signal }) => api.latestTelemetry(vin, signal),
|
||||
enabled: enabled && vehicle?.primaryProtocol === 'GB32960',
|
||||
staleTime: 10_000,
|
||||
refetchInterval: enabled && activelyTracked && vehicle?.primaryProtocol === 'GB32960' ? 20_000 : false,
|
||||
gcTime: QUERY_MEMORY.highVolumeGcTime,
|
||||
...LIVE_QUERY_POLICY
|
||||
});
|
||||
const address = useQuery({
|
||||
queryKey: ['monitor', 'vehicle-card', 'address', addressPoint?.vin, addressPoint?.key],
|
||||
queryFn: ({ signal }) => api.reverseGeocode(new URLSearchParams({
|
||||
@@ -214,5 +225,5 @@ export function useMonitorVehicleCard(vin: string, vehicle?: VehicleRealtimeRow,
|
||||
gcTime: QUERY_MEMORY.highVolumeGcTime
|
||||
});
|
||||
|
||||
return { detail, activeAlerts, address };
|
||||
return { detail, activeAlerts, telemetry, address };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
export function useSideSheetA11y(open: boolean, selector: string, id: string, label: string, closeLabel: string) {
|
||||
useLayoutEffect(() => {
|
||||
if (!open) return;
|
||||
const apply = () => {
|
||||
const dialog = document.querySelector(`${selector} .semi-sidesheet-inner`);
|
||||
dialog?.setAttribute('id', id);
|
||||
dialog?.setAttribute('aria-label', label);
|
||||
dialog?.querySelector('.semi-sidesheet-close')?.setAttribute('aria-label', closeLabel);
|
||||
};
|
||||
apply();
|
||||
const frame = window.requestAnimationFrame(apply);
|
||||
return () => window.cancelAnimationFrame(frame);
|
||||
}, [closeLabel, id, label, open, selector]);
|
||||
}
|
||||
Reference in New Issue
Block a user