diff options
Diffstat (limited to 'web/app/App.jsx')
-rw-r--r-- | web/app/App.jsx | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/web/app/App.jsx b/web/app/App.jsx new file mode 100644 index 0000000..a31545e --- /dev/null +++ b/web/app/App.jsx @@ -0,0 +1,97 @@ +import React, { useState, useEffect } from "react" +import { Map, Marker, GeoJson, ZoomControl } from "pigeon-maps" +import useLocalState from "@phntms/use-local-state"; +import moment from "moment"; +import 'moment/min/locales'; + +export default () => { + moment.locale('ru'); + const [point, setPoint] = useState({ + "id": "", + "user_id": "", + "lat": 0, + "lon": 0, + "time": "2024-08-15T09:54:00.666Z", + "speed": 0, + "direction": 0, + "accuracy": 0, + }); + const [selected, setSelected] = useState(null) + const [points, setPoints] = useState([]) + const [loaded, setLoaded] = useState(false); + const [zoom, setZoom] = useLocalState("zoom", 5); + const [center, setCenter] = useState([]) + const [page, setPage] = useState(0) + const updatePosition = () => { + fetch("/api/points/last").then(x => x.json()).then((p) => { + setPoint(p); + setCenter([point.lat, point.lon]) + setLoaded(true); + }) + fetch("/api/points").then(x => x.json()).then(p => { + setPoints(p.reverse()) + }) + } + useEffect(updatePosition, []) + if (!loaded) { + return <h1>Загрузка карты</h1> + } + const path = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { + type: "LineString", + coordinates: points.map((pp) => [pp.lon, pp.lat]), + }, + properties: { prop0: "value0" }, + }, + ], + }; + + return ( + <div className="container"> + <div className="column"> + <Map height={'100vh'} center={center} defaultCenter={[point.lat, point.lon]} defaultZoom={zoom} onBoundsChanged={({ center, zoom, bounds, initial, }) => {setZoom(zoom); setCenter(center)}}> + <ZoomControl /> + <GeoJson + data={path} + styleCallback={(feature, hover) => { + return { strokeWidth: "2", stroke: "red" }; + }} + /> + <Marker + width={32} + anchor={[point.lat, point.lon]} + /> + {selected ? (<Marker + width={32} + anchor={[selected.lat, selected.lon]} + />):null} + </Map> + </div> + <div className="column list"> + <div>Дата последней точки: <b>{moment(point.time).format("LTS L")}</b></div> + <div>Точность: <b>{point.accuracy.toFixed(2)}м</b></div> + <div> + <button onClick={updatePosition}>Обновить</button> + <button onClick={() => setCenter([point.lat, point.lon])}>Перейти к последней позиции</button> + </div> + <div>История перемещения:</div> + <ul> + {points.slice(page*50, (page+1)*50).map(p => <li key={p.id}> + <a href="#" onClick={(ev) => {setSelected(p);setCenter([p.lat, p.lon]);ev.preventDefault(); }}> + {moment(p.time).format("LTS L")} — + {p.lat.toFixed(5)}, + {p.lon.toFixed(5)} + (точность: {p.accuracy.toFixed(2)}м) + </a> + </li>)} + </ul> + <button onClick={() => setPage(Math.max(0, page-1))}>←</button> + <button onClick={() => setPage(Math.min(page+1, Math.floor(points.length / 50)))}>→</button> + </div> + </div > + ) +}
\ No newline at end of file |