Files
iistwin/client/src/pages/gps/SettingsTab.tsx

136 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useState } from 'react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Loader2, Save, SlidersHorizontal } from 'lucide-react';
import { apiRequest } from '@/lib/queryClient';
import { useToast } from '@/hooks/use-toast';
interface GpsSettings {
success: boolean;
jitterMeters: number;
maxJumpSpeedKmh: number;
}
/**
* Таб «Настройки» GPS-модуля.
* Секция «Трекинг»: фильтры входящих точек — дрожание позиции (джиттер)
* и максимальный скачок скорости (LBS-прыжки). Сюда же позже добавятся
* другие настройки модуля — структура секций-Card под это готова.
*/
export function GpsSettingsTab() {
const { toast } = useToast();
const { data, isLoading } = useQuery<GpsSettings>({
queryKey: ['/api/gps/settings'],
});
const [jitterMeters, setJitterMeters] = useState('0');
const [maxJumpSpeedKmh, setMaxJumpSpeedKmh] = useState('0');
useEffect(() => {
if (data) {
setJitterMeters(String(data.jitterMeters));
setMaxJumpSpeedKmh(String(data.maxJumpSpeedKmh));
}
}, [data]);
const saveMutation = useMutation({
mutationFn: async () => {
// Оба поля сохраняются одним PUT (непереданные на сервере сохраняются)
const res = await apiRequest('PUT', '/api/gps/settings', {
jitterMeters: Number(jitterMeters),
maxJumpSpeedKmh: Number(maxJumpSpeedKmh),
});
return res.json();
},
onSuccess: () => {
toast({ title: 'Настройки сохранены' });
},
onError: (err: any) => {
toast({
title: 'Ошибка',
description: err?.message || 'Не удалось сохранить настройки',
variant: 'destructive',
});
},
});
const isIntInRange = (v: string, max: number) => /^\d+$/.test(v.trim()) && Number(v) >= 0 && Number(v) <= max;
const jitterValid = isIntInRange(jitterMeters, 1000);
const jumpValid = isIntInRange(maxJumpSpeedKmh, 5000);
return (
<div className="max-w-2xl space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<SlidersHorizontal className="w-4 h-4" />
Трекинг
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{isLoading ? (
<div className="flex items-center justify-center h-16">
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
</div>
) : (
<>
<div className="space-y-1">
<Label className="text-xs">Фильтр дрожания позиции, метров</Label>
<Input
type="text"
inputMode="numeric"
className="h-8 w-32"
value={jitterMeters}
onChange={(e) => setJitterMeters(e.target.value)}
/>
<p className="text-xs text-muted-foreground">
0 = выключен. Точки ближе N метров при скорости &lt; 5 км/ч не записываются — убирает
«кучу мелких перемещений» у стоящего объекта. Действует на новые точки.
</p>
{!jitterValid && (
<p className="text-xs text-destructive">Целое число от 0 до 1000</p>
)}
</div>
<div className="space-y-1">
<Label className="text-xs">Максимальный скачок, км/ч</Label>
<Input
type="text"
inputMode="numeric"
className="h-8 w-32"
value={maxJumpSpeedKmh}
onChange={(e) => setMaxJumpSpeedKmh(e.target.value)}
/>
<p className="text-xs text-muted-foreground">
0 = выключен. Точки, требующие перемещения быстрее N км/ч от предыдущей,
отбрасываются — отсекает LBS-прыжки на вышки при потере GPS. Действует на новые точки.
</p>
{!jumpValid && (
<p className="text-xs text-destructive">Целое число от 0 до 5000</p>
)}
</div>
<Button
size="sm"
className="h-8"
disabled={saveMutation.isPending || !jitterValid || !jumpValid}
onClick={() => saveMutation.mutate()}
>
{saveMutation.isPending ? (
<Loader2 className="w-3.5 h-3.5 mr-1 animate-spin" />
) : (
<Save className="w-3.5 h-3.5 mr-1" />
)}
Сохранить
</Button>
</>
)}
</CardContent>
</Card>
</div>
);
}