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 { 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> = {
|
||||
label: string;
|
||||
data: keyof T;
|
||||
@ -49,6 +59,7 @@ type LinkedItemsProps<T> = {
|
||||
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 = <T extends { id: number; [key: string]: any }>({
|
||||
title,
|
||||
dragAllowed = false,
|
||||
type,
|
||||
onSave,
|
||||
}: LinkedItemsProps<T>) => {
|
||||
const [position, setPosition] = useState<number>(1);
|
||||
const [items, setItems] = useState<T[]>([]);
|
||||
const [linkedItems, setLinkedItems] = useState<T[]>([]);
|
||||
const [selectedItemId, setSelectedItemId] = useState<number | null>(null);
|
||||
@ -89,16 +102,11 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
||||
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 = <T extends { id: number; [key: string]: any }>({
|
||||
[`${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 = <T extends { id: number; [key: string]: any }>({
|
||||
{type === "edit" && dragAllowed && (
|
||||
<TableCell width="40px"></TableCell>
|
||||
)}
|
||||
<TableCell key="id">№</TableCell>
|
||||
{fields.map((field) => (
|
||||
<TableCell key={String(field.data)}>
|
||||
{field.label}
|
||||
@ -259,6 +278,7 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
||||
<TableRow
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
hover
|
||||
>
|
||||
{type === "edit" && dragAllowed && (
|
||||
@ -268,6 +288,9 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell key={String(item.id)}>
|
||||
{index + 1}
|
||||
</TableCell>
|
||||
{fields.map((field) => (
|
||||
<TableCell key={String(field.data)}>
|
||||
{field.render
|
||||
@ -389,6 +412,21 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({
|
||||
>
|
||||
Добавить
|
||||
</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>
|
||||
|
@ -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) => {
|
||||
|
@ -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<RouteInfoData>();
|
||||
|
||||
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 (
|
||||
<RoutePreviewDashboard routeInfo={routeInfo!}>
|
||||
<RoutePreviewDashboard routeInfo={routeInfo}>
|
||||
<MapWidget />
|
||||
{/* <SettingsPanel onSubmit={handleSubmit} /> */}
|
||||
</RoutePreviewDashboard>
|
||||
|
@ -21,17 +21,24 @@ export const RoutePreviewDashboard = ({ children, routeInfo }: Props) => {
|
||||
|
||||
return (
|
||||
<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={styles.leftTopWrapper}>
|
||||
<RouteInfoWidget className={styles.routeNumber} routeInfo={routeInfo} />
|
||||
<RouteInfoWidget
|
||||
className={styles.routeNumber}
|
||||
routeInfo={routeInfo}
|
||||
/>
|
||||
<WeatherWidget className={styles.weatherWidget} />
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
{children}
|
||||
|
||||
{/* <div className={styles.transferPlaceholder}>
|
||||
<div className={styles.transferPlaceholder}>
|
||||
<Icons.InfoBtn
|
||||
className={styles.toggleTransferBtn}
|
||||
onClick={() => setOpenTransfer(!openTransfers)}
|
||||
@ -41,7 +48,7 @@ export const RoutePreviewDashboard = ({ children, routeInfo }: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.rightSidebar} /> */}
|
||||
<div className={styles.rightSidebar} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -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<MapData>(null);
|
||||
const [mappedData, setMappedData] = useState<MapData | null>(null);
|
||||
const [error, setError] = useState<string | null>(null); // Handle error state
|
||||
|
||||
// Fetch route data
|
||||
const {
|
||||
data: routeData,
|
||||
isSuccess,
|
||||
isError,
|
||||
isLoading,
|
||||
} = useOne<Route>({
|
||||
resource: "route",
|
||||
id: routeId,
|
||||
} = useOne<Route>("routes", { 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];
|
||||
}),
|
||||
});
|
||||
|
||||
const [stations, setStations] = useState<any[]>([]);
|
||||
const [sights, setSights] = useState<any[]>([]);
|
||||
|
||||
// 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
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user