route edit fixed with drag and drop tool
This commit is contained in:
parent
9e34a71e14
commit
463c593a0e
@ -27,6 +27,16 @@ import { axiosInstance } from "../providers/data";
|
|||||||
import { TOKEN_KEY } from "../authProvider";
|
import { TOKEN_KEY } from "../authProvider";
|
||||||
import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
|
import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
|
||||||
|
|
||||||
|
function insertAtPosition<T>(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<T> = {
|
type Field<T> = {
|
||||||
label: string;
|
label: string;
|
||||||
data: keyof T;
|
data: keyof T;
|
||||||
@ -49,6 +59,7 @@ type LinkedItemsProps<T> = {
|
|||||||
type: "show" | "edit";
|
type: "show" | "edit";
|
||||||
extraField?: ExtraFieldConfig;
|
extraField?: ExtraFieldConfig;
|
||||||
dragAllowed?: boolean;
|
dragAllowed?: boolean;
|
||||||
|
onSave?: (items: T[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const reorder = (list: any[], startIndex: number, endIndex: number) => {
|
const reorder = (list: any[], startIndex: number, endIndex: number) => {
|
||||||
@ -66,7 +77,9 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
title,
|
title,
|
||||||
dragAllowed = false,
|
dragAllowed = false,
|
||||||
type,
|
type,
|
||||||
|
onSave,
|
||||||
}: LinkedItemsProps<T>) => {
|
}: LinkedItemsProps<T>) => {
|
||||||
|
const [position, setPosition] = useState<number>(1);
|
||||||
const [items, setItems] = useState<T[]>([]);
|
const [items, setItems] = useState<T[]>([]);
|
||||||
const [linkedItems, setLinkedItems] = useState<T[]>([]);
|
const [linkedItems, setLinkedItems] = useState<T[]>([]);
|
||||||
const [selectedItemId, setSelectedItemId] = useState<number | null>(null);
|
const [selectedItemId, setSelectedItemId] = useState<number | null>(null);
|
||||||
@ -89,16 +102,11 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
axiosInstance.post(
|
axiosInstance.post(
|
||||||
`${import.meta.env.VITE_KRBL_API}/route/${parentId}/station`,
|
`${import.meta.env.VITE_KRBL_API}/route/${parentId}/station`,
|
||||||
{
|
{
|
||||||
after_station: 3,
|
stations: reorderedItems.map((item) => ({
|
||||||
offset_x: 90,
|
id: item.id,
|
||||||
offset_y: 15,
|
})),
|
||||||
station_id: linkedItems[result.destination.index].id + 1,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you need to save the new order to the backend, you would add that here
|
|
||||||
// For example:
|
|
||||||
// saveNewOrder(reorderedItems);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -158,9 +166,19 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
[`${childResource}_id`]: selectedItemId,
|
[`${childResource}_id`]: selectedItemId,
|
||||||
media_order: mediaOrder,
|
media_order: mediaOrder,
|
||||||
}
|
}
|
||||||
: {
|
: childResource === "station"
|
||||||
[`${childResource}_id`]: selectedItemId,
|
? {
|
||||||
};
|
stations: insertAtPosition(
|
||||||
|
linkedItems.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
})),
|
||||||
|
position,
|
||||||
|
{
|
||||||
|
id: selectedItemId,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: { [`${childResource}_id`]: selectedItemId };
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.post(
|
.post(
|
||||||
@ -232,6 +250,7 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
{type === "edit" && dragAllowed && (
|
{type === "edit" && dragAllowed && (
|
||||||
<TableCell width="40px"></TableCell>
|
<TableCell width="40px"></TableCell>
|
||||||
)}
|
)}
|
||||||
|
<TableCell key="id">№</TableCell>
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
<TableCell key={String(field.data)}>
|
<TableCell key={String(field.data)}>
|
||||||
{field.label}
|
{field.label}
|
||||||
@ -259,6 +278,7 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
<TableRow
|
<TableRow
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
{...provided.draggableProps}
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
hover
|
hover
|
||||||
>
|
>
|
||||||
{type === "edit" && dragAllowed && (
|
{type === "edit" && dragAllowed && (
|
||||||
@ -268,6 +288,9 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
|
<TableCell key={String(item.id)}>
|
||||||
|
{index + 1}
|
||||||
|
</TableCell>
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
<TableCell key={String(field.data)}>
|
<TableCell key={String(field.data)}>
|
||||||
{field.render
|
{field.render
|
||||||
@ -389,6 +412,21 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
|||||||
>
|
>
|
||||||
Добавить
|
Добавить
|
||||||
</Button>
|
</Button>
|
||||||
|
{childResource == "station" && (
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
label="Позиция добавляемой остановки к маршруту"
|
||||||
|
value={position}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newValue = Number(e.target.value);
|
||||||
|
setPosition(
|
||||||
|
newValue > linkedItems.length + 1
|
||||||
|
? linkedItems.length + 1
|
||||||
|
: newValue
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
></TextField>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
stationFields,
|
stationFields,
|
||||||
vehicleFields,
|
vehicleFields,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export const RouteEdit = () => {
|
export const RouteEdit = () => {
|
||||||
const {
|
const {
|
||||||
@ -24,10 +25,23 @@ export const RouteEdit = () => {
|
|||||||
register,
|
register,
|
||||||
control,
|
control,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
|
refineCore: { queryResult },
|
||||||
|
setValue,
|
||||||
|
watch,
|
||||||
} = useForm({});
|
} = useForm({});
|
||||||
|
|
||||||
const { id: routeId } = useParams<{ id: string }>();
|
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({
|
const { autocompleteProps: carrierAutocompleteProps } = useAutocomplete({
|
||||||
resource: "carrier",
|
resource: "carrier",
|
||||||
onSearch: (value) => [
|
onSearch: (value) => [
|
||||||
@ -134,7 +148,6 @@ export const RouteEdit = () => {
|
|||||||
{...register("path", {
|
{...register("path", {
|
||||||
required: "Это поле является обязательным",
|
required: "Это поле является обязательным",
|
||||||
setValueAs: (value: string) => {
|
setValueAs: (value: string) => {
|
||||||
// Преобразование строки в массив координат
|
|
||||||
try {
|
try {
|
||||||
const lines = value.trim().split("\n");
|
const lines = value.trim().split("\n");
|
||||||
return lines.map((line) => {
|
return lines.map((line) => {
|
||||||
|
@ -13,11 +13,10 @@ import { LocalizedStringDefaults } from "@mt/common-types";
|
|||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
|
|
||||||
export const RoutePreviewContainer = () => {
|
export const RoutePreviewContainer = () => {
|
||||||
const { id } = useParams();
|
const { routeId } = useParams();
|
||||||
const routeId = id as string;
|
|
||||||
const { routeData, mappedData, isLoading, isError } =
|
const { routeData, mappedData, isLoading, isError } =
|
||||||
useGetRouteData(routeId);
|
useGetRouteData(routeId);
|
||||||
// const updateRouteView = useUpdateRouteData(routeId, routeData);
|
const updateRouteView = useUpdateRouteData(routeId, routeData);
|
||||||
const [routeInfo, setRouteInfo] = useState<RouteInfoData>();
|
const [routeInfo, setRouteInfo] = useState<RouteInfoData>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -28,9 +27,9 @@ export const RoutePreviewContainer = () => {
|
|||||||
getUpdatedStations,
|
getUpdatedStations,
|
||||||
} = useMapWidgetContext();
|
} = useMapWidgetContext();
|
||||||
|
|
||||||
// const handleSubmit = (mapSettings: MapSettings) => {
|
const handleSubmit = (mapSettings: MapSettings) => {
|
||||||
// updateRouteView(mapSettings, getUpdatedStations());
|
updateRouteView(mapSettings, getUpdatedStations());
|
||||||
// };
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mappedData) {
|
if (!mappedData) {
|
||||||
@ -42,7 +41,7 @@ export const RoutePreviewContainer = () => {
|
|||||||
setIsEditMode(true);
|
setIsEditMode(true);
|
||||||
|
|
||||||
setRouteInfo({
|
setRouteInfo({
|
||||||
routeNumber: routeData.number,
|
routeNumber: routeData?.number ?? "",
|
||||||
firstStationName:
|
firstStationName:
|
||||||
mappedData.stationsOnMap.at(0)?.shortName ?? LocalizedStringDefaults,
|
mappedData.stationsOnMap.at(0)?.shortName ?? LocalizedStringDefaults,
|
||||||
lastStationName:
|
lastStationName:
|
||||||
@ -74,7 +73,7 @@ export const RoutePreviewContainer = () => {
|
|||||||
|
|
||||||
if (mappedData) {
|
if (mappedData) {
|
||||||
return (
|
return (
|
||||||
<RoutePreviewDashboard routeInfo={routeInfo!}>
|
<RoutePreviewDashboard routeInfo={routeInfo}>
|
||||||
<MapWidget />
|
<MapWidget />
|
||||||
{/* <SettingsPanel onSubmit={handleSubmit} /> */}
|
{/* <SettingsPanel onSubmit={handleSubmit} /> */}
|
||||||
</RoutePreviewDashboard>
|
</RoutePreviewDashboard>
|
||||||
|
@ -21,17 +21,24 @@ export const RoutePreviewDashboard = ({ children, routeInfo }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={styles.root}>
|
||||||
{/* <Drawer isOpen={openNav} onToggle={setOpenNav} onLocaleChange={setLocale} />
|
<Drawer
|
||||||
|
isOpen={openNav}
|
||||||
|
onToggle={setOpenNav}
|
||||||
|
onLocaleChange={setLocale}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className={cn(styles.container, { [styles.pushed]: openNav })}>
|
<div className={cn(styles.container, { [styles.pushed]: openNav })}>
|
||||||
<div className={styles.leftTopWrapper}>
|
<div className={styles.leftTopWrapper}>
|
||||||
<RouteInfoWidget className={styles.routeNumber} routeInfo={routeInfo} />
|
<RouteInfoWidget
|
||||||
|
className={styles.routeNumber}
|
||||||
|
routeInfo={routeInfo}
|
||||||
|
/>
|
||||||
<WeatherWidget className={styles.weatherWidget} />
|
<WeatherWidget className={styles.weatherWidget} />
|
||||||
</div> */}
|
</div>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{/* <div className={styles.transferPlaceholder}>
|
<div className={styles.transferPlaceholder}>
|
||||||
<Icons.InfoBtn
|
<Icons.InfoBtn
|
||||||
className={styles.toggleTransferBtn}
|
className={styles.toggleTransferBtn}
|
||||||
onClick={() => setOpenTransfer(!openTransfers)}
|
onClick={() => setOpenTransfer(!openTransfers)}
|
||||||
@ -41,7 +48,7 @@ export const RoutePreviewDashboard = ({ children, routeInfo }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.rightSidebar} /> */}
|
<div className={styles.rightSidebar} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,62 +1,58 @@
|
|||||||
import { MapData } from "@mt/components";
|
import { MapData } from "@mt/components";
|
||||||
|
|
||||||
import { Route, Station } from "@mt/common-types";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { mapRouteFromApi } from "../mappers/mapRouteFromApi";
|
import { mapRouteFromApi } from "../mappers/mapRouteFromApi";
|
||||||
import { useOne, useMany } from "@refinedev/core";
|
import { useOn, Station, useOne, useMany } from "@refinedev/core";
|
||||||
import { axiosInstance } from "../../../../providers/data";
|
import { Route } from "@mt/common-types";
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetRouteData = (routeId: string) => {
|
export const useGetRouteData = (routeId: string) => {
|
||||||
const [mappedData, setMappedData] = useState<MapData>(null);
|
const [mappedData, setMappedData] = useState<MapData | null>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null); // Handle error state
|
||||||
|
|
||||||
|
// Fetch route data
|
||||||
const {
|
const {
|
||||||
data: routeData,
|
data: routeData,
|
||||||
isSuccess,
|
isSuccess,
|
||||||
isError,
|
isError,
|
||||||
isLoading,
|
isLoading,
|
||||||
} = useOne<Route>({
|
} = useOne<Route>("routes", { id: routeId });
|
||||||
resource: "route",
|
|
||||||
id: routeId,
|
const { data: stations } = useMany<Station>("stations", {
|
||||||
|
ids: routeData?.stations.flatMap(({ stationId, transferStations }) => {
|
||||||
|
const transferIds = transferStations
|
||||||
|
.filter(({ isShowOnMap }) => isShowOnMap)
|
||||||
|
.map(({ stationId: id }) => id);
|
||||||
|
return [stationId, ...transferIds];
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
// Handle loading state and errors
|
||||||
const [stations, setStations] = useState<any[]>([]);
|
|
||||||
const [sights, setSights] = useState<any[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchStations(routeId, setStations, setSights);
|
if (isLoading || stationsLoading) {
|
||||||
}, [routeData]);
|
setError(null); // Reset error during loading
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// useEffect(() => {
|
if (isError || stationsError) {
|
||||||
// if (!routeData) {
|
setError("Failed to fetch route or station data.");
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // const data = mapRouteFromApi(routeData, []);
|
if (!routeData || !stations) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// setMappedData(data);
|
try {
|
||||||
// }, [routeData]);
|
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
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user