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 }, // 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) }>({}); // 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]); // 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", 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 */} ( 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: { code: string; name: string; id: string; }) => { // Define how to display the label for each option return item ? item.name : ""; }} 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) => ( )} /> )} /> {/* City Name TextField for the current language */} {/* Arms Autocomplete field */} ( 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: { id: string; media_name: string }) => { // Define how to display the label for each option return item ? item.media_name : ""; }} 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: { 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) => ( )} /> )} /> ); };