From 463c593a0e6409e08ae18819d0c019db8f5b61e7 Mon Sep 17 00:00:00 2001 From: itoshi Date: Mon, 21 Apr 2025 15:34:11 +0300 Subject: [PATCH] route edit fixed with drag and drop tool --- src/components/LinkedItems.tsx | 60 ++++++++++--- src/pages/route/edit.tsx | 15 +++- .../components/RoutePreviewContainer.tsx | 15 ++-- .../RoutePreviewDashboard.tsx | 19 +++-- .../route-preview/hooks/useGetRouteData.ts | 84 +++++++++---------- 5 files changed, 123 insertions(+), 70 deletions(-) diff --git a/src/components/LinkedItems.tsx b/src/components/LinkedItems.tsx index f9a2afb..976a4f8 100644 --- a/src/components/LinkedItems.tsx +++ b/src/components/LinkedItems.tsx @@ -27,6 +27,16 @@ import { axiosInstance } from "../providers/data"; import { TOKEN_KEY } from "../authProvider"; import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; +function insertAtPosition(arr: T[], pos: number, value: T): T[] { + const index = pos - 1; + if (index >= arr.length) { + arr.push(value); + } else { + arr.splice(index, 0, value); + } + return arr; +} + type Field = { label: string; data: keyof T; @@ -49,6 +59,7 @@ type LinkedItemsProps = { type: "show" | "edit"; extraField?: ExtraFieldConfig; dragAllowed?: boolean; + onSave?: (items: T[]) => void; }; const reorder = (list: any[], startIndex: number, endIndex: number) => { @@ -66,7 +77,9 @@ export const LinkedItems = ({ title, dragAllowed = false, type, + onSave, }: LinkedItemsProps) => { + const [position, setPosition] = useState(1); const [items, setItems] = useState([]); const [linkedItems, setLinkedItems] = useState([]); const [selectedItemId, setSelectedItemId] = useState(null); @@ -89,16 +102,11 @@ export const LinkedItems = ({ axiosInstance.post( `${import.meta.env.VITE_KRBL_API}/route/${parentId}/station`, { - after_station: 3, - offset_x: 90, - offset_y: 15, - station_id: linkedItems[result.destination.index].id + 1, + stations: reorderedItems.map((item) => ({ + id: item.id, + })), } ); - - // If you need to save the new order to the backend, you would add that here - // For example: - // saveNewOrder(reorderedItems); }; useEffect(() => { @@ -158,9 +166,19 @@ export const LinkedItems = ({ [`${childResource}_id`]: selectedItemId, media_order: mediaOrder, } - : { - [`${childResource}_id`]: selectedItemId, - }; + : childResource === "station" + ? { + stations: insertAtPosition( + linkedItems.map((item) => ({ + id: item.id, + })), + position, + { + id: selectedItemId, + } + ), + } + : { [`${childResource}_id`]: selectedItemId }; axiosInstance .post( @@ -232,6 +250,7 @@ export const LinkedItems = ({ {type === "edit" && dragAllowed && ( )} + {fields.map((field) => ( {field.label} @@ -259,6 +278,7 @@ export const LinkedItems = ({ {type === "edit" && dragAllowed && ( @@ -268,6 +288,9 @@ export const LinkedItems = ({ )} + + {index + 1} + {fields.map((field) => ( {field.render @@ -389,6 +412,21 @@ export const LinkedItems = ({ > Добавить + {childResource == "station" && ( + { + const newValue = Number(e.target.value); + setPosition( + newValue > linkedItems.length + 1 + ? linkedItems.length + 1 + : newValue + ); + }} + > + )} )} diff --git a/src/pages/route/edit.tsx b/src/pages/route/edit.tsx index 1d98e83..db588b1 100644 --- a/src/pages/route/edit.tsx +++ b/src/pages/route/edit.tsx @@ -17,6 +17,7 @@ import { stationFields, vehicleFields, } from "./types"; +import { useEffect } from "react"; export const RouteEdit = () => { const { @@ -24,10 +25,23 @@ export const RouteEdit = () => { register, control, formState: { errors }, + refineCore: { queryResult }, + setValue, + watch, } = useForm({}); const { id: routeId } = useParams<{ id: string }>(); + useEffect(() => { + if (queryResult?.data?.data && Array.isArray(queryResult.data.data.path)) { + const formattedPath = queryResult.data.data.path + .map((coords) => coords.join(" ")) + .join("\n"); + + setValue("path", formattedPath); + } + }, [queryResult?.data?.data, setValue]); + const { autocompleteProps: carrierAutocompleteProps } = useAutocomplete({ resource: "carrier", onSearch: (value) => [ @@ -134,7 +148,6 @@ export const RouteEdit = () => { {...register("path", { required: "Это поле является обязательным", setValueAs: (value: string) => { - // Преобразование строки в массив координат try { const lines = value.trim().split("\n"); return lines.map((line) => { diff --git a/src/preview/components/route-preview/components/RoutePreviewContainer.tsx b/src/preview/components/route-preview/components/RoutePreviewContainer.tsx index c7ea2b5..6bd7085 100644 --- a/src/preview/components/route-preview/components/RoutePreviewContainer.tsx +++ b/src/preview/components/route-preview/components/RoutePreviewContainer.tsx @@ -13,11 +13,10 @@ import { LocalizedStringDefaults } from "@mt/common-types"; import { useParams } from "react-router"; export const RoutePreviewContainer = () => { - const { id } = useParams(); - const routeId = id as string; + const { routeId } = useParams(); const { routeData, mappedData, isLoading, isError } = useGetRouteData(routeId); - // const updateRouteView = useUpdateRouteData(routeId, routeData); + const updateRouteView = useUpdateRouteData(routeId, routeData); const [routeInfo, setRouteInfo] = useState(); const { @@ -28,9 +27,9 @@ export const RoutePreviewContainer = () => { getUpdatedStations, } = useMapWidgetContext(); - // const handleSubmit = (mapSettings: MapSettings) => { - // updateRouteView(mapSettings, getUpdatedStations()); - // }; + const handleSubmit = (mapSettings: MapSettings) => { + updateRouteView(mapSettings, getUpdatedStations()); + }; useEffect(() => { if (!mappedData) { @@ -42,7 +41,7 @@ export const RoutePreviewContainer = () => { setIsEditMode(true); setRouteInfo({ - routeNumber: routeData.number, + routeNumber: routeData?.number ?? "", firstStationName: mappedData.stationsOnMap.at(0)?.shortName ?? LocalizedStringDefaults, lastStationName: @@ -74,7 +73,7 @@ export const RoutePreviewContainer = () => { if (mappedData) { return ( - + {/* */} diff --git a/src/preview/components/route-preview/components/RoutePreviewDashboard/RoutePreviewDashboard.tsx b/src/preview/components/route-preview/components/RoutePreviewDashboard/RoutePreviewDashboard.tsx index 0f13323..7f6c52d 100644 --- a/src/preview/components/route-preview/components/RoutePreviewDashboard/RoutePreviewDashboard.tsx +++ b/src/preview/components/route-preview/components/RoutePreviewDashboard/RoutePreviewDashboard.tsx @@ -21,17 +21,24 @@ export const RoutePreviewDashboard = ({ children, routeInfo }: Props) => { return (
- {/* +
- + -
*/} +
- {children} + {children} - {/*
+
setOpenTransfer(!openTransfers)} @@ -41,7 +48,7 @@ export const RoutePreviewDashboard = ({ children, routeInfo }: Props) => {
-
*/} +
); }; diff --git a/src/preview/components/route-preview/hooks/useGetRouteData.ts b/src/preview/components/route-preview/hooks/useGetRouteData.ts index 6ca56f6..f597070 100644 --- a/src/preview/components/route-preview/hooks/useGetRouteData.ts +++ b/src/preview/components/route-preview/hooks/useGetRouteData.ts @@ -1,62 +1,58 @@ import { MapData } from "@mt/components"; - -import { Route, Station } from "@mt/common-types"; import { useEffect, useState } from "react"; import { mapRouteFromApi } from "../mappers/mapRouteFromApi"; -import { useOne, useMany } from "@refinedev/core"; -import { axiosInstance } from "../../../../providers/data"; - -const fetchStations = async ( - routeId: string, - setStations: (stations: any[]) => void, - setSights: (sights: any[]) => void -) => { - const stations = (await axiosInstance.get(`/route/${routeId}/station`)).data; - const sights = (await axiosInstance.get(`/route/${routeId}/sight`)).data; - setStations(stations); - setSights(sights); - - const stationsPath = stations.map((station: any) => [ - station.latitude, - station.longitude, - ]); - const sightsPath = sights.map((sight: any) => [ - sight.latitude, - sight.longitude, - ]); - - console.log(stationsPath, sightsPath); -}; +import { useOn, Station, useOne, useMany } from "@refinedev/core"; +import { Route } from "@mt/common-types"; export const useGetRouteData = (routeId: string) => { - const [mappedData, setMappedData] = useState(null); + const [mappedData, setMappedData] = useState(null); + const [error, setError] = useState(null); // Handle error state + // Fetch route data const { data: routeData, isSuccess, isError, isLoading, - } = useOne({ - resource: "route", - id: routeId, + } = useOne("routes", { id: routeId }); + + const { data: stations } = useMany("stations", { + ids: routeData?.stations.flatMap(({ stationId, transferStations }) => { + const transferIds = transferStations + .filter(({ isShowOnMap }) => isShowOnMap) + .map(({ stationId: id }) => id); + return [stationId, ...transferIds]; + }), }); - - const [stations, setStations] = useState([]); - const [sights, setSights] = useState([]); - + // Handle loading state and errors useEffect(() => { - fetchStations(routeId, setStations, setSights); - }, [routeData]); + if (isLoading || stationsLoading) { + setError(null); // Reset error during loading + return; + } - // useEffect(() => { - // if (!routeData) { - // return; - // } + if (isError || stationsError) { + setError("Failed to fetch route or station data."); + return; + } - // // const data = mapRouteFromApi(routeData, []); + if (!routeData || !stations) { + return; + } - // setMappedData(data); - // }, [routeData]); + try { + const data = mapRouteFromApi(routeData, stations); + setMappedData(data); // Set the mapped data + } catch (err) { + setError("Error mapping the route data."); + } + }, [routeData, stations, isLoading, stationsLoading, isError, stationsError]); - return { routeData, mappedData, isLoading, isError }; + return { + routeData, + mappedData, + isLoading: isLoading || stationsLoading, // Combine loading states + isError: isError || stationsError, // Combine error states + error, // Provide the error message + }; };