From 9218743faf09fa500a4d404bbee72672428cafb7 Mon Sep 17 00:00:00 2001 From: itoshi Date: Wed, 28 May 2025 19:15:23 +0300 Subject: [PATCH] fix: Update `language` usage with review `coordinates` --- src/pages/carrier/create.tsx | 68 ++++-- src/pages/city/create.tsx | 320 +++++++++++++++++++++++----- src/pages/media/list.tsx | 1 - src/pages/route-preview/Station.tsx | 8 +- src/pages/route-preview/index.tsx | 6 +- src/pages/sight/create.tsx | 71 +++--- src/pages/sight/edit.tsx | 54 ++--- src/pages/sight/list.tsx | 1 + src/pages/station/create.tsx | 42 +--- src/pages/station/edit.tsx | 88 +++++--- src/pages/vehicle/list.tsx | 2 + 11 files changed, 455 insertions(+), 206 deletions(-) diff --git a/src/pages/carrier/create.tsx b/src/pages/carrier/create.tsx index 0fcf422..3144784 100644 --- a/src/pages/carrier/create.tsx +++ b/src/pages/carrier/create.tsx @@ -3,19 +3,63 @@ import { Create, useAutocomplete } from "@refinedev/mui"; import { useForm } from "@refinedev/react-hook-form"; import { Controller } from "react-hook-form"; import { observer } from "mobx-react-lite"; -import { languageStore, META_LANGUAGE } from "../../store/LanguageStore"; +import { META_LANGUAGE } from "../../store/LanguageStore"; +import { LanguageSwitch } from "@/components/LanguageSwitch"; + +import { useEffect, useState } from "react"; +import { EVERY_LANGUAGE, Languages, languageStore } from "@stores"; + export const CarrierCreate = observer(() => { - const { language } = languageStore; + const { language, setLanguageAction } = languageStore; const { saveButtonProps, refineCore: { formLoading }, register, control, + setValue, + watch, formState: { errors }, } = useForm({ - refineCoreProps: META_LANGUAGE(language) + refineCoreProps: META_LANGUAGE(language), }); + const [carrierData, setCarrierData] = useState({ + full_name: EVERY_LANGUAGE(""), + short_name: EVERY_LANGUAGE(""), + slogan: EVERY_LANGUAGE(""), + }); + + useEffect(() => { + setValue("full_name", carrierData.full_name[language]); + setValue("short_name", carrierData.short_name[language]); + setValue("slogan", carrierData.slogan[language]); + }, [carrierData, language, setValue]); + + function updateTranslations(update: boolean = true) { + const newCarrierData = { + ...carrierData, + full_name: { + ...carrierData.full_name, + [language]: watch("full_name") ?? "", + }, + short_name: { + ...carrierData.short_name, + [language]: watch("short_name") ?? "", + }, + slogan: { + ...carrierData.slogan, + [language]: watch("slogan") ?? "", + }, + }; + if (update) setCarrierData(newCarrierData); + return newCarrierData; + } + + const handleLanguageChange = (lang: Languages) => { + updateTranslations(); + setLanguageAction(lang); + }; + const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({ resource: "city", onSearch: (value) => [ @@ -45,6 +89,7 @@ export const CarrierCreate = observer(() => { sx={{ display: "flex", flexDirection: "column" }} autoComplete="off" > + { helperText={(errors as any)?.full_name?.message} margin="normal" fullWidth - slotProps={{inputLabel: {shrink: true}}} + slotProps={{ inputLabel: { shrink: true } }} type="text" label={"Полное имя *"} name="full_name" @@ -109,16 +154,13 @@ export const CarrierCreate = observer(() => { helperText={(errors as any)?.short_name?.message} margin="normal" fullWidth - slotProps={{inputLabel: {shrink: true}}} + slotProps={{ inputLabel: { shrink: true } }} type="text" label={"Короткое имя"} name="short_name" /> - + { helperText={(errors as any)?.main_color?.message} margin="normal" fullWidth - slotProps={{inputLabel: {shrink: true}}} + slotProps={{ inputLabel: { shrink: true } }} type="color" label={"Основной цвет"} name="main_color" @@ -149,7 +191,7 @@ export const CarrierCreate = observer(() => { helperText={(errors as any)?.left_color?.message} margin="normal" fullWidth - slotProps={{inputLabel: {shrink: true}}} + slotProps={{ inputLabel: { shrink: true } }} type="color" label={"Цвет левого виджета"} name="left_color" @@ -172,7 +214,7 @@ export const CarrierCreate = observer(() => { helperText={(errors as any)?.right_color?.message} margin="normal" fullWidth - slotProps={{inputLabel: {shrink: true}}} + slotProps={{ inputLabel: { shrink: true } }} type="color" label={"Цвет правого виджета"} name="right_color" @@ -195,7 +237,7 @@ export const CarrierCreate = observer(() => { helperText={(errors as any)?.slogan?.message} margin="normal" fullWidth - slotProps={{inputLabel: {shrink: true}}} + slotProps={{ inputLabel: { shrink: true } }} type="text" label={"Слоган"} name="slogan" diff --git a/src/pages/city/create.tsx b/src/pages/city/create.tsx index bb1a93c..647c1ea 100644 --- a/src/pages/city/create.tsx +++ b/src/pages/city/create.tsx @@ -1,97 +1,303 @@ -import {Autocomplete, Box, TextField} from '@mui/material' -import {Create, useAutocomplete} from '@refinedev/mui' -import {useForm} from '@refinedev/react-hook-form' -import {Controller} from 'react-hook-form' +import { Autocomplete, Box, TextField } from "@mui/material"; +import { Create, useAutocomplete } from "@refinedev/mui"; +import { useForm } from "@refinedev/react-hook-form"; +import { Controller } from "react-hook-form"; +import { useEffect, useState } from "react"; +import { EVERY_LANGUAGE, Languages, languageStore } from "@stores"; // Assuming these imports are correct and available +import { LanguageSwitch } from "@/components/LanguageSwitch"; // Assuming this import is correct and available +import { axiosInstance, axiosInstanceForGet } from "@/providers"; // Assuming these imports are correct and available +import { useNavigate } from "react-router"; +import { useNotification } from "@refinedev/core"; export const CityCreate = () => { + const { language, setLanguageAction } = languageStore; + const navigate = useNavigate(); + const notification = useNotification(); + // State to hold all language translations for the city name. + // Initialized with an object where each language key maps to an empty string. + const [allLanguageNames, setAllLanguageNames] = useState< + Record + >(EVERY_LANGUAGE("")); + const { saveButtonProps, - refineCore: {formLoading}, - register, - control, - formState: {errors}, - } = useForm({}) + refineCore: { formLoading }, // Indicates if the form is currently loading data + register, // Function to register input fields with react-hook-form + control, // Object to pass to Controller for controlled components + setValue, // Function to programmatically set form values + watch, // Function to watch for changes in form values + handleSubmit, // Function to handle form submission, including validation + formState: { errors }, // Object containing form validation errors + } = useForm<{ + name: string; // This field will hold the name for the *currently active* language + country_code: string; // The code for the selected country + arms: string; // The ID of the selected media for the city's arms (e.g., coat of arms) + }>({}); - const {autocompleteProps: countryAutocompleteProps} = useAutocomplete({ - resource: 'country', - }) + // useEffect hook to synchronize the 'name' TextField with the 'allLanguageNames' state. + // Whenever the active 'language' or the 'allLanguageNames' state changes, + // the 'name' field in the form is updated to display the correct translation. + useEffect(() => { + setValue("name", allLanguageNames[language]); + }, [language, allLanguageNames, setValue]); - const {autocompleteProps: mediaAutocompleteProps} = useAutocomplete({ - resource: 'media', + // Function to update the 'allLanguageNames' state with the current value from the 'name' TextField. + // This is crucial to ensure that changes made by the user are saved before + // switching languages or submitting the form. + const updateCurrentLanguageName = () => { + const currentNameValue = watch("name"); // Get the current value typed into the 'name' TextField + setAllLanguageNames((prev) => ({ + ...prev, + [language]: currentNameValue || "", // Update the specific language entry in the state + })); + }; + + // Handler for language switch. + // It first saves the current input for the active language, then changes the language. + const handleLanguageChange = (lang: Languages) => { + updateCurrentLanguageName(); // Save the current input before switching language + setLanguageAction(lang); // Update the global language state + }; + + // Autocomplete props for Country selection. + // 'resource' specifies the API endpoint, and it's assumed 'code' is the value + // to be stored and 'name' is the label to display. + const { autocompleteProps: countryAutocompleteProps } = useAutocomplete({ + resource: "country", + }); + + // Autocomplete props for Media selection (specifically for city's arms). + // 'resource' specifies the API endpoint, and 'onSearch' provides a custom + // search filter based on 'media_name'. + const { autocompleteProps: mediaAutocompleteProps } = useAutocomplete({ + resource: "media", onSearch: (value) => [ { - field: 'media_name', - operator: 'contains', + field: "media_name", + operator: "contains", value, }, ], - }) + }); + + // This function is called when the form is submitted and passes validation. + // It orchestrates the API calls for creating the city and updating its translations. + const onFinish = async (data: { + name: string; + country_code: string; + arms: string; + }) => { + // Ensure the very latest input for the current language is captured + // into 'allLanguageNames' before making API calls. This handles cases + // where the user might click 'Save' without blurring the 'name' field. + updateCurrentLanguageName(); + + // Create a final set of names including the latest value from the form + // for the current language. + const finalNames = { + ...allLanguageNames, + [language]: data.name, // Ensure the absolute latest value from the form is included + }; + + try { + // Validate that the Russian name is present, as it's used for the initial POST request. + if (!finalNames["ru"]) { + console.error("Russian name is required for initial city creation."); + // In a real application, you might want to display a user-friendly error message here. + return; + } + + console.log("Submitting with names:", finalNames); + + // 1. Create the city entry in Russian. + // This POST request typically returns the ID of the newly created resource. + const ruResponse = await axiosInstanceForGet("ru").post("/city", { + name: finalNames["ru"], // Russian name + country_code: data.country_code, // Country code from the form + arms: data.arms, // Arms ID from the form, included in initial creation + }); + + const id = ruResponse.data.id; // Extract the ID of the newly created city + + // 2. Patch the city with the English name if available. + if (finalNames["en"]) { + await axiosInstanceForGet("en").patch(`/city/${id}`, { + name: finalNames["en"], // English name + country_code: data.country_code, // Country code from the form + arms: data.arms, // Arms ID from the form, included in initial creation + }); + } + + // 3. Patch the city with the Chinese name if available. + if (finalNames["zh"]) { + await axiosInstanceForGet("zh").patch(`/city/${id}`, { + name: finalNames["zh"], // Chinese name + country_code: data.country_code, // Country code from the form + arms: data.arms, // Arms ID from the form, included in initial creation + }); + } + + console.log("City created/updated successfully!"); + if (notification && typeof notification.open === "function") { + notification.open({ + message: "Город успешно создан", + type: "success", + }); + } + window.onbeforeunload = null; + navigate("/city"); + + // After successful creation/update, you might want to: + // - Navigate to a different page (e.g., city list). + // - Display a success notification to the user. + } catch (error) { + console.error("Error creating/updating city:", error); + // Display an error message to the user if the API calls fail. + } + }; return ( - - + + + {/* Language Switch component to change the active language */} + + + {/* Country Autocomplete field */} ( + control={control} // Pass control from useForm + name="country_code" // Field name in the form state + rules={{ required: "Это поле является обязательным" }} // Validation rule + defaultValue={null} // Initial value + render={({ field }) => ( option.code === field.value) || null} - onChange={(_, value) => { - field.onChange(value?.code || '') + {...countryAutocompleteProps} // Spread autocomplete props from useAutocomplete hook + value={ + // Find the selected option based on the field's current value + countryAutocompleteProps.options.find( + (option: { code: string; name: string; id: string }) => + option.code === field.value + ) || null + } + onChange={( + _, + value: { code: string; name: string; id: string } | null + ) => { + // Update the form field's value when an option is selected + field.onChange(value?.code || ""); }} - getOptionLabel={(item) => { - return item ? item.name : '' + getOptionLabel={(item: { + code: string; + name: string; + id: string; + }) => { + // Define how to display the label for each option + return item ? item.name : ""; }} - isOptionEqualToValue={(option, value) => { - return option.id === value?.id + isOptionEqualToValue={( + option: { code: string; name: string; id: string }, + value: { code: string; name: string; id: string } + ) => { + // Define how to compare options for equality + return option.id === value?.id; }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> )} /> + {/* City Name TextField for the current language */} + {/* Arms Autocomplete field */} ( + control={control} // Pass control from useForm + name="arms" // Field name in the form state + defaultValue={null} // Initial value + render={({ field }) => ( option.id === field.value) || null} - onChange={(_, value) => { - field.onChange(value?.id || '') + {...mediaAutocompleteProps} // Spread autocomplete props from useAutocomplete hook + value={ + // Find the selected option based on the field's current value + mediaAutocompleteProps.options.find( + (option: { id: string; media_name: string }) => + option.id === field.value + ) || null + } + onChange={( + _, + value: { id: string; media_name: string } | null + ) => { + // Update the form field's value when an option is selected + field.onChange(value?.id || ""); }} - getOptionLabel={(item) => { - return item ? item.media_name : '' + getOptionLabel={(item: { id: string; media_name: string }) => { + // Define how to display the label for each option + return item ? item.media_name : ""; }} - isOptionEqualToValue={(option, value) => { - return option.id === value?.id + isOptionEqualToValue={( + option: { id: string; media_name: string }, + value: { id: string; media_name: string } + ) => { + // Define how to compare options for equality + return option.id === value?.id; }} - filterOptions={(options, {inputValue}) => { - return options.filter((option) => option.media_name.toLowerCase().includes(inputValue.toLowerCase())) + filterOptions={( + options: { id: string; media_name: string }[], + { inputValue } + ) => { + // Custom filter for options based on input value + return options.filter((option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> )} /> - ) -} + ); +}; diff --git a/src/pages/media/list.tsx b/src/pages/media/list.tsx index d6e626e..b70320a 100644 --- a/src/pages/media/list.tsx +++ b/src/pages/media/list.tsx @@ -100,7 +100,6 @@ export const MediaList = observer(() => { columns={columns} localeText={localeText} getRowId={(row: any) => row.id} - languageEnabled /> ); diff --git a/src/pages/route-preview/Station.tsx b/src/pages/route-preview/Station.tsx index 1128699..f262fe2 100644 --- a/src/pages/route-preview/Station.tsx +++ b/src/pages/route-preview/Station.tsx @@ -114,10 +114,10 @@ export const StationLabel = observer( rotation={-rotation} > { ))} diff --git a/src/pages/sight/create.tsx b/src/pages/sight/create.tsx index f76f068..2be4644 100644 --- a/src/pages/sight/create.tsx +++ b/src/pages/sight/create.tsx @@ -22,6 +22,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; export const SightCreate = observer(() => { const { language, setLanguageAction } = languageStore; + const [coordinates, setCoordinates] = useState(""); const [sightData, setSightData] = useState({ name: EVERY_LANGUAGE(""), address: EVERY_LANGUAGE(""), @@ -79,10 +80,7 @@ export const SightCreate = observer(() => { }); const [namePreview, setNamePreview] = useState(""); - const [coordinatesPreview, setCoordinatesPreview] = useState({ - latitude: "", - longitude: "", - }); + const [coordinatesPreview, setCoordinatesPreview] = useState(""); const [creatingArticleHeading, setCreatingArticleHeading] = useState(""); @@ -102,13 +100,19 @@ export const SightCreate = observer(() => { const [previewArticlePreview, setPreviewArticlePreview] = useState(""); const handleCoordinatesChange = (e: React.ChangeEvent) => { - const [lat, lon] = e.target.value.split(",").map((s) => s.trim()); - setCoordinatesPreview({ - latitude: lat, - longitude: lon, - }); - setValue("latitude", lat); - setValue("longitude", lon); + setCoordinates(e.target.value); + if (e.target.value) { + const [lat, lon] = e.target.value.split(" ").map((s) => s.trim()); + setCoordinatesPreview( + `${lat ? Number(lat) : 0}, ${lon ? Number(lon) : 0}` + ); + setValue("latitude", lat ? Number(lat) : 0); + setValue("longitude", lon ? Number(lon) : 0); + } else { + setCoordinatesPreview(""); + setValue("latitude", ""); + setValue("longitude", ""); + } }; // Автокомплиты @@ -170,13 +174,6 @@ export const SightCreate = observer(() => { setNamePreview(nameContent ?? ""); }, [nameContent]); - useEffect(() => { - setCoordinatesPreview({ - latitude: latitudeContent || "", - longitude: longitudeContent || "", - }); - }, [latitudeContent, longitudeContent]); - useEffect(() => { const selectedCity = cityAutocompleteProps.options.find( (option) => option.id === cityContent @@ -276,7 +273,7 @@ export const SightCreate = observer(() => { /> { type="text" label={"Координаты *"} /> + { { {/* Координаты */} - - - Координаты:{" "} - - - theme.palette.mode === "dark" ? "grey.300" : "grey.800", - }} - > - {`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`} - - - + {coordinatesPreview && ( + + + Координаты:{" "} + + + theme.palette.mode === "dark" ? "grey.300" : "grey.800", + }} + > + {coordinatesPreview} + + + )} {/* Обложка */} {thumbnailPreview && ( diff --git a/src/pages/sight/edit.tsx b/src/pages/sight/edit.tsx index b995470..2a81981 100644 --- a/src/pages/sight/edit.tsx +++ b/src/pages/sight/edit.tsx @@ -70,6 +70,7 @@ export const SightEdit = observer(() => { name: EVERY_LANGUAGE(""), address: EVERY_LANGUAGE(""), }); + const [coordinates, setCoordinates] = useState(""); const { saveButtonProps, @@ -163,37 +164,13 @@ export const SightEdit = observer(() => { setValue("address", sightData.address[language]); }, [language, sightData, setValue]); - useEffect(() => { - const latitude = getValues("latitude"); - const longitude = getValues("longitude"); - if (latitude && longitude) { - setCoordinatesPreview({ - latitude: latitude, - longitude: longitude, - }); - } - }, [getValues]); - - const handleCoordinatesChange = (e: React.ChangeEvent) => { - const [lat, lon] = e.target.value.split(",").map((s) => s.trim()); - setCoordinatesPreview({ - latitude: lat, - longitude: lon, - }); - setValue("latitude", lat); - setValue("longitude", lon); - }; - // Состояния для предпросмотра const [creatingArticleHeading, setCreatingArticleHeading] = useState(""); const [creatingArticleBody, setCreatingArticleBody] = useState(""); - const [coordinatesPreview, setCoordinatesPreview] = useState({ - latitude: "", - longitude: "", - }); + const [coordinatesPreview, setCoordinatesPreview] = useState(""); const [selectedArticleIndex, setSelectedArticleIndex] = useState(-1); const [previewMediaFile, setPreviewMediaFile] = useState(); const [thumbnailPreview, setThumbnailPreview] = useState(null); @@ -243,12 +220,25 @@ export const SightEdit = observer(() => { const watermarkRDContent = watch("watermark_rd"); useEffect(() => { - setCoordinatesPreview({ - latitude: latitudeContent ?? "", - longitude: longitudeContent ?? "", - }); + if (latitudeContent && longitudeContent) { + setCoordinates(`${latitudeContent} ${longitudeContent}`); + } }, [latitudeContent, longitudeContent]); + const handleCoordinatesChange = (e: React.ChangeEvent) => { + setCoordinates(e.target.value); + if (e.target.value) { + const [lat, lon] = e.target.value.split(" ").map((s) => s.trim()); + setCoordinatesPreview(`${lat ?? "0"}, ${lon ?? "0"}`); + setValue("latitude", lat ?? ""); + setValue("longitude", lon ?? ""); + } else { + setCoordinatesPreview(""); + setValue("latitude", ""); + setValue("longitude", ""); + } + }; + useEffect(() => { if (linkedArticles[selectedArticleIndex]?.id) { getMedia(linkedArticles[selectedArticleIndex].id).then((media) => { @@ -505,7 +495,6 @@ export const SightEdit = observer(() => { { { /> { : "grey.800", }} > - {`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`} + {coordinatesPreview} diff --git a/src/pages/sight/list.tsx b/src/pages/sight/list.tsx index c309a41..a41b838 100644 --- a/src/pages/sight/list.tsx +++ b/src/pages/sight/list.tsx @@ -56,6 +56,7 @@ export const SightList = observer(() => { display: "flex", align: "left", headerAlign: "left", + flex: 1, }, { field: "latitude", diff --git a/src/pages/station/create.tsx b/src/pages/station/create.tsx index aa6f266..42bbf67 100644 --- a/src/pages/station/create.tsx +++ b/src/pages/station/create.tsx @@ -41,40 +41,19 @@ export const StationCreate = () => { }, }); - const [coordinatesPreview, setCoordinatesPreview] = useState({ - latitude: "", - longitude: "", - }); + const [coordinates, setCoordinates] = useState(""); const handleCoordinatesChange = (e: React.ChangeEvent) => { - const [lat, lon] = e.target.value.split(",").map((s) => s.trim()); - setCoordinatesPreview({ - latitude: lat, - longitude: lon, - }); + setCoordinates(e.target.value); + const [lat, lon] = e.target.value + .replace(/,/g, "") // Remove all commas from the string + .split(" ") + .map((s) => s.trim()); + console.log(lat, lon); setValue("latitude", lat); setValue("longitude", lon); }; - const latitudeContent = watch("latitude"); - const longitudeContent = watch("longitude"); - useEffect(() => { - setCoordinatesPreview({ - latitude: latitudeContent || "", - longitude: longitudeContent || "", - }); - }, [latitudeContent, longitudeContent]); - - useEffect(() => { - const latitude = getValues("latitude"); - const longitude = getValues("longitude"); - if (latitude && longitude) { - setCoordinatesPreview({ - latitude: latitude, - longitude: longitude, - }); - } - }, [getValues]); const directions = [ { label: "Прямой", @@ -188,7 +167,7 @@ export const StationCreate = () => { )} /> { type="text" label={"Координаты *"} /> + { if (value === "") { return 0; @@ -213,7 +193,7 @@ export const StationCreate = () => { { if (value === "") { return 0; diff --git a/src/pages/station/edit.tsx b/src/pages/station/edit.tsx index 24e09c7..395f01b 100644 --- a/src/pages/station/edit.tsx +++ b/src/pages/station/edit.tsx @@ -40,24 +40,24 @@ export const StationEdit = observer(() => { system_name: "", description: "", address: "", - latitude: "", - longitude: "", + latitude: 0, + longitude: 0, }, en: { name: "", system_name: "", description: "", address: "", - latitude: "", - longitude: "", + latitude: 0, + longitude: 0, }, zh: { name: "", system_name: "", description: "", address: "", - latitude: "", - longitude: "", + latitude: 0, + longitude: 0, }, }); @@ -69,8 +69,8 @@ export const StationEdit = observer(() => { system_name: watch("system_name") ?? "", description: watch("description") ?? "", address: watch("address") ?? "", - latitude: watch("latitude") ?? "", - longitude: watch("longitude") ?? "", + latitude: Number(watch("latitude")) || 0, + longitude: Number(watch("longitude")) || 0, }, })); }; @@ -120,7 +120,7 @@ export const StationEdit = observer(() => { if (stationData[language as keyof typeof stationData]?.name) { setValue("name", stationData[language as keyof typeof stationData]?.name); } - if (stationData[language as keyof typeof stationData]?.address) { + if (stationData[language as keyof typeof stationData]?.system_name) { setValue( "system_name", stationData[language as keyof typeof stationData]?.system_name || "" @@ -132,16 +132,20 @@ export const StationEdit = observer(() => { stationData[language as keyof typeof stationData]?.description || "" ); } - if (stationData[language as keyof typeof stationData]?.latitude) { + if ( + stationData[language as keyof typeof stationData]?.latitude !== undefined + ) { setValue( "latitude", - stationData[language as keyof typeof stationData]?.latitude || "" + stationData[language as keyof typeof stationData]?.latitude || 0 ); } - if (stationData[language as keyof typeof stationData]?.longitude) { + if ( + stationData[language as keyof typeof stationData]?.longitude !== undefined + ) { setValue( "longitude", - stationData[language as keyof typeof stationData]?.longitude || "" + stationData[language as keyof typeof stationData]?.longitude || 0 ); } }, [language, stationData, setValue]); @@ -150,28 +154,36 @@ export const StationEdit = observer(() => { setLanguageAction("ru"); }, []); - const { id: stationId } = useParams<{ id: string }>(); + const [coordinates, setCoordinates] = useState(""); - const handleCoordinatesChange = (e: React.ChangeEvent) => { - const [lat, lon] = e.target.value.split(",").map((s) => s.trim()); - setCoordinatesPreview({ - latitude: lat, - longitude: lon, - }); - setValue("latitude", lat); - setValue("longitude", lon); - }; + const { id: stationId } = useParams<{ id: string }>(); const latitudeContent = watch("latitude"); const longitudeContent = watch("longitude"); useEffect(() => { - setCoordinatesPreview({ - latitude: latitudeContent || "", - longitude: longitudeContent || "", - }); + if (latitudeContent && longitudeContent) { + setCoordinates(`${latitudeContent} ${longitudeContent}`); + } }, [latitudeContent, longitudeContent]); + const handleCoordinatesChange = (e: React.ChangeEvent) => { + setCoordinates(e.target.value); + if (e.target.value) { + const [lat, lon] = e.target.value + .replace(/,/g, "") // Remove all commas from the string + .split(" ") + .map((s) => s.trim()); + setCoordinates(`${lat ?? 0} ${lon ?? 0}`); + setValue("latitude", lat ?? 0); + setValue("longitude", lon ?? 0); + } else { + setCoordinates(""); + setValue("latitude", ""); + setValue("longitude", ""); + } + }; + const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({ resource: "city", onSearch: (value) => [ @@ -282,7 +294,7 @@ export const StationEdit = observer(() => { /> { { + if (value === "") { + return 0; + } + return Number(value); + }, })} /> { + if (value === "") { + return 0; + } + return Number(value); + }, + })} /> { type: "string", minWidth: 150, display: "flex", + flex: 1, align: "left", headerAlign: "left", renderCell: (params) => { @@ -116,6 +117,7 @@ export const VehicleList = observer(() => { headerName: "Город", type: "string", minWidth: 150, + flex: 1, display: "flex", align: "left", headerAlign: "left",