A10-A16: datetime-local time parsing, step heading, gps_error state, unified leaflet 1.9.4 icons, drop debug log, dashboard error surfacing, isMobile on resize
This commit is contained in:
+12
-2
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from "react-router-dom";
|
import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from "react-router-dom";
|
||||||
import AdminDashboard from "./pages/AdminDashboard";
|
import AdminDashboard from "./pages/AdminDashboard";
|
||||||
import MobileFormPage from "./pages/MobileFormPage";
|
import MobileFormPage from "./pages/MobileFormPage";
|
||||||
@@ -27,7 +27,17 @@ function ThemeProvider({ children }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) || window.innerWidth < 768;
|
const [isMobile, setIsMobile] = useState(
|
||||||
|
() => /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) || window.innerWidth < 768
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onResize = () => setIsMobile(
|
||||||
|
/iPhone|iPad|iPod|Android/i.test(navigator.userAgent) || window.innerWidth < 768
|
||||||
|
);
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
return () => window.removeEventListener('resize', onResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ const CaseForm = ({ isMobile = false }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
console.log("Case created:", result);
|
|
||||||
|
|
||||||
// Перенаправляем на страницу анализа с ID карточки
|
// Перенаправляем на страницу анализа с ID карточки
|
||||||
window.location.href = `/analysis/${result.id}`;
|
window.location.href = `/analysis/${result.id}`;
|
||||||
|
|||||||
@@ -27,23 +27,23 @@ const Step4Environment = ({ data, updateData }) => {
|
|||||||
|
|
||||||
const handleGetLocation = () => {
|
const handleGetLocation = () => {
|
||||||
if (!navigator.geolocation) {
|
if (!navigator.geolocation) {
|
||||||
alert("Геолокация не поддерживается вашим браузером");
|
updateData({ gps_error: 'Геолокация не поддерживается вашим браузером' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData({ gps_loading: true });
|
updateData({ gps_loading: true, gps_error: null });
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition(
|
navigator.geolocation.getCurrentPosition(
|
||||||
(position) => {
|
(position) => {
|
||||||
updateData({
|
updateData({
|
||||||
tnp_lat: position.coords.latitude.toFixed(6),
|
tnp_lat: position.coords.latitude.toFixed(6),
|
||||||
tnp_lon: position.coords.longitude.toFixed(6),
|
tnp_lon: position.coords.longitude.toFixed(6),
|
||||||
gps_loading: false
|
gps_loading: false,
|
||||||
|
gps_error: null
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
alert("Ошибка получения координат: " + error.message);
|
updateData({ gps_error: 'Ошибка получения координат: ' + error.message, gps_loading: false });
|
||||||
updateData({ gps_loading: false });
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableHighAccuracy: true,
|
enableHighAccuracy: true,
|
||||||
@@ -55,7 +55,7 @@ const Step4Environment = ({ data, updateData }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-step">
|
<div className="form-step">
|
||||||
<h2>Шаг 5: Среда и местность</h2>
|
<h2>Среда и местность</h2>
|
||||||
|
|
||||||
{/* Сезон определяется автоматически, показываем только для информации */}
|
{/* Сезон определяется автоматически, показываем только для информации */}
|
||||||
{data.season && (
|
{data.season && (
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import L from 'leaflet';
|
|||||||
// Fix default marker icon issue with webpack
|
// Fix default marker icon issue with webpack
|
||||||
delete L.Icon.Default.prototype._getIconUrl;
|
delete L.Icon.Default.prototype._getIconUrl;
|
||||||
L.Icon.Default.mergeOptions({
|
L.Icon.Default.mergeOptions({
|
||||||
iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
|
iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
|
||||||
iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
|
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
|
||||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
||||||
});
|
});
|
||||||
|
|
||||||
const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones }) => {
|
const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones }) => {
|
||||||
@@ -183,8 +183,8 @@ const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones }) => {
|
|||||||
<Marker
|
<Marker
|
||||||
position={tnpPosition}
|
position={tnpPosition}
|
||||||
icon={L.icon({
|
icon={L.icon({
|
||||||
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
|
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
|
||||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
||||||
iconSize: [25, 41],
|
iconSize: [25, 41],
|
||||||
iconAnchor: [12, 41],
|
iconAnchor: [12, 41],
|
||||||
popupAnchor: [1, -34],
|
popupAnchor: [1, -34],
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ function AdminDashboard() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name !== 'AbortError') {
|
if (err.name !== 'AbortError') {
|
||||||
setDashboard(null);
|
setDashboard(null);
|
||||||
|
setError(err.message || 'Ошибка загрузки дашборда');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setDashboardLoading(false);
|
setDashboardLoading(false);
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ const AnalysisResult = () => {
|
|||||||
|
|
||||||
const getTimeOfDay = (lostTime) => {
|
const getTimeOfDay = (lostTime) => {
|
||||||
if (!lostTime) return 'день';
|
if (!lostTime) return 'день';
|
||||||
const hour = parseInt(lostTime.split(':')[0]);
|
// Значение может прийти как "YYYY-MM-DDTHH:MM" (input type="datetime-local")
|
||||||
|
// или как "HH:MM" — берём часы из обоих форматов.
|
||||||
|
const timePart = lostTime.includes('T') ? lostTime.split('T')[1] : lostTime;
|
||||||
|
const hour = parseInt(timePart.split(':')[0]);
|
||||||
if (hour >= 6 && hour < 18) return 'день';
|
if (hour >= 6 && hour < 18) return 'день';
|
||||||
if (hour >= 18 && hour < 22) return 'сумерки';
|
if (hour >= 18 && hour < 22) return 'сумерки';
|
||||||
return 'ночь';
|
return 'ночь';
|
||||||
|
|||||||
Reference in New Issue
Block a user