From a4acaf2473e3ae4fd2db6d43ae100dfc0ff062fa Mon Sep 17 00:00:00 2001 From: Ebubeker Date: Sun, 10 Dec 2023 22:05:52 +0100 Subject: [PATCH] fix: using use form to make the code a bit cleaner --- .../components/map/panels/toolbox/Toolbox.tsx | 3 +- .../map/panels/toolbox/tools/InputLayer.tsx | 58 +++-- .../map/panels/toolbox/tools/SaveResult.tsx | 37 +-- .../isochrone/Isochrone.tsx | 237 +++++++----------- .../isochrone/IsochroneSettings.tsx | 183 ++++++++------ .../isochrone/StartingPoint.tsx | 99 +++++--- .../toolbox/tools/join/FieldsToMatch.tsx | 71 +++--- .../map/panels/toolbox/tools/join/Join.tsx | 169 ++++++------- .../panels/toolbox/tools/join/Statistics.tsx | 57 +++-- apps/web/lib/validations/isochrone.ts | 99 ++++---- 10 files changed, 514 insertions(+), 499 deletions(-) diff --git a/apps/web/components/map/panels/toolbox/Toolbox.tsx b/apps/web/components/map/panels/toolbox/Toolbox.tsx index da263120..457aab1d 100644 --- a/apps/web/components/map/panels/toolbox/Toolbox.tsx +++ b/apps/web/components/map/panels/toolbox/Toolbox.tsx @@ -48,8 +48,9 @@ const ToolboxPanel = () => { setDefaultRoute={setDefaultRoute} /> } + disablePadding /> ); }; -export default ToolboxPanel; +export default ToolboxPanel; \ No newline at end of file diff --git a/apps/web/components/map/panels/toolbox/tools/InputLayer.tsx b/apps/web/components/map/panels/toolbox/tools/InputLayer.tsx index 0f6d5159..4b43ca20 100644 --- a/apps/web/components/map/panels/toolbox/tools/InputLayer.tsx +++ b/apps/web/components/map/panels/toolbox/tools/InputLayer.tsx @@ -14,16 +14,27 @@ import { useProjectLayers } from "@/lib/api/projects"; import { useParams } from "next/navigation"; import type { SelectChangeEvent } from "@mui/material"; +import type { UseFormGetValues, UseFormRegister } from "react-hook-form"; +import type { PostJoin, PostAggregate } from "@/lib/validations/tools"; interface PickLayerProps { + register: UseFormRegister | UseFormRegister; + getValues: UseFormGetValues | UseFormGetValues; multiple?: boolean; - inputValues: string | string[]; - setInputValues: (value: string | string[]) => void; - layerTypes: string[]; + // inputValues: string | string[]; + // setInputValues: (value: string | string[]) => void; + // layerTypes: string[]; } const InputLayer = (props: PickLayerProps) => { - const { multiple = false, inputValues, setInputValues, layerTypes } = props; + const { + register, + // getValues, + multiple = false, + // inputValues, + // setInputValues, + // layerTypes + } = props; const theme = useTheme(); const { t } = useTranslation("maps"); @@ -34,16 +45,17 @@ const InputLayer = (props: PickLayerProps) => { typeof projectId === "string" ? projectId : "", ); - const handleSingleChange = (event: SelectChangeEvent) => { - setInputValues(event.target.value as string); + const handleSingleChange = (_: SelectChangeEvent) => { + // setInputValues(event.target.value as string); }; - const handleMultipleChange = (event: SelectChangeEvent, inputNr: number) => { - const multipleValues = - typeof inputValues !== "string" ? [...inputValues] : ["", ""]; - multipleValues[inputNr] = event.target.value as string; + const handleMultipleChange = (_: SelectChangeEvent, inputNr: number) => { + console.log(inputNr); + // const multipleValues = + // typeof inputValues !== "string" ? [...inputValues] : ["", ""]; + // multipleValues[inputNr] = event.target.value as string; - setInputValues(multipleValues); + // setInputValues(multipleValues); }; return ( @@ -68,7 +80,8 @@ const InputLayer = (props: PickLayerProps) => { - {projectLayers ? projectLayers.map((layer) => - layerTypes.includes(layer.feature_layer_geometry_type) ? ( - - {layer.name} - - ) : null, - ) : null} + {projectLayers + ? projectLayers.map((layer) => + layerTypes.includes(layer.feature_layer_geometry_type) ? ( + + {layer.name} + + ) : null, + ) + : null} @@ -158,4 +174,4 @@ const InputLayer = (props: PickLayerProps) => { ); }; -export default InputLayer; +export default InputLayer; \ No newline at end of file diff --git a/apps/web/components/map/panels/toolbox/tools/SaveResult.tsx b/apps/web/components/map/panels/toolbox/tools/SaveResult.tsx index f925d90b..f5f83c49 100644 --- a/apps/web/components/map/panels/toolbox/tools/SaveResult.tsx +++ b/apps/web/components/map/panels/toolbox/tools/SaveResult.tsx @@ -19,24 +19,33 @@ import { v4 } from "uuid"; import { useTranslation } from "@/i18n/client"; import { useGetUniqueLayerName } from "@/hooks/map/ToolsHooks"; -import type { SelectChangeEvent } from "@mui/material"; +// import type { SelectChangeEvent } from "@mui/material"; +import type { UseFormRegister } from "react-hook-form"; +import type { PostAggregate, PostJoin } from "@/lib/validations/tools"; +import type { PostIsochrone } from "@/lib/validations/isochrone"; interface SaveResultProps { - outputName: string | undefined; - setOutputName: (value: string) => void; - folderSaveId: string | undefined; - setFolderSaveID: (value: string) => void; + register: UseFormRegister | UseFormRegister | UseFormRegister; + watch: PostJoin | PostAggregate | PostIsochrone; + // outputName: string | undefined; + // setOutputName: (value: string) => void; + // folderSaveId: string | undefined; + // setFolderSaveID: (value: string) => void; } const SaveResult = (props: SaveResultProps) => { - const { outputName, setOutputName, folderSaveId, setFolderSaveID } = props; + const { + register, + watch, + } = props; + const theme = useTheme(); const { t } = useTranslation("maps"); const { folders } = useFolders(); - const { uniqueName } = useGetUniqueLayerName(outputName ? outputName : ""); + const { uniqueName } = useGetUniqueLayerName(watch.result_target.layer_name ? watch.result_target.layer_name : ""); return ( @@ -93,9 +102,10 @@ const SaveResult = (props: SaveResultProps) => { value={uniqueName ? uniqueName : ""} label="Name" size="small" - onChange={(event: React.ChangeEvent) => - setOutputName(event.target.value as string) - } + {...register("result_target.layer_name")} + // onChange={(event: React.ChangeEvent) => + // setOutputName(event.target.value as string) + // } /> @@ -108,10 +118,7 @@ const SaveResult = (props: SaveResultProps) => { ) => { - setRouting(event.target.value as RoutingTypes); + if (event.target.value === "pt") { + setValue("routing_type", { + mode: [ + "bus", + "tram", + "rail", + "subway", + "ferry", + "cable_car", + "gondola", + "funicular", + ], + egress_mode: "walk", + access_mode: "walk", + }); + } else { + setValue("routing_type", event.target.value); + } dispatch(removeMarker()); }} > @@ -299,12 +302,15 @@ const IsochroneSettings = (props: PickLayerProps) => { {/*--------------------------PT Options--------------------------*/} - {routing === ("pt" as RoutingTypes) ? ( + {typeof watch.routing_type !== "string" ? ( + watch.routing_type.mode.includes(mode.value as PTModeTypes), + )} getOptionLabel={(option) => option.name} renderOption={(props, option, { selected }) => (
  • @@ -313,9 +319,7 @@ const IsochroneSettings = (props: PickLayerProps) => {
  • )} fullWidth - defaultValue={ptModes.filter( - (mode) => getPtModes?.includes(mode.value as PTModeTypes), - )} + {...register("routing_type.mode")} size="small" renderInput={(params) => ( { placeholder={t("panels.isochrone.routing.pt_type")} /> )} - onChange={(_, value) => { - setPtModes(value.map((val) => val.value) as PTModeTypes[]); - }} /> - ) : null} + ) : ( + <> + )} {/*--------------------------------------------------------------*/} { setTab(newValue as "distance" | "time"); + setValue( + "travel_cost", + newValue === "distance" + ? { + max_distance: 50, + distance_step: 50, + } + : { + max_traveltime: 10, + traveltime_step: 50, + speed: 10, + }, + ); }} variant="fullWidth" > - + { - {routing !== ("pt" as RoutingTypes) ? speedFunctionality() : null} + {typeof watch.routing_type === "string" ? speedFunctionality() : null} {travelTimeFunctionality()} {stepFunctionality()} @@ -368,4 +397,4 @@ const IsochroneSettings = (props: PickLayerProps) => { ); }; -export default IsochroneSettings; +export default IsochroneSettings; \ No newline at end of file diff --git a/apps/web/components/map/panels/toolbox/tools/accessibility_indicators/isochrone/StartingPoint.tsx b/apps/web/components/map/panels/toolbox/tools/accessibility_indicators/isochrone/StartingPoint.tsx index c5ae09da..0be4c9f5 100644 --- a/apps/web/components/map/panels/toolbox/tools/accessibility_indicators/isochrone/StartingPoint.tsx +++ b/apps/web/components/map/panels/toolbox/tools/accessibility_indicators/isochrone/StartingPoint.tsx @@ -25,33 +25,28 @@ import { useDispatch } from "react-redux"; import { addMarker, removeMarker } from "@/lib/store/map/slice"; import type { StartingPointType } from "@/types/map/isochrone"; -import type { SelectChangeEvent } from "@mui/material"; import type { Result } from "@/types/map/controllers"; import type { FeatureCollection } from "geojson"; -import type { RoutingTypes } from "@/types/map/isochrone"; +import type { UseFormRegister, UseFormSetValue } from "react-hook-form"; +import type { PostIsochrone } from "@/lib/validations/isochrone"; +import type { SelectChangeEvent } from "@mui/material"; interface PickLayerProps { - routing: RoutingTypes | undefined; + register: UseFormRegister; + // getValues: UseFormGetValues; + setValue: UseFormSetValue; + watch: PostIsochrone; startingType: StartingPointType | undefined; setStartingType: (value: StartingPointType) => void; - startingPoint: string[] | string; - setStartingPoint: (value: string[] | string) => void; } -const isochroneMarkerIcons = { - walking: ICON_NAME.RUN, - padelec: ICON_NAME.PEDELEC, - bicycle: ICON_NAME.BICYCLE, - pt: ICON_NAME.BUS, - car_peak: ICON_NAME.CAR, -}; - const StartingPoint = (props: PickLayerProps) => { const { - routing, + register, + // getValues, + setValue: setFormValue, startingType, - setStartingPoint, - startingPoint, + watch, setStartingType, } = props; // const { projectLayers } = useProjectLayers(); @@ -94,17 +89,21 @@ const StartingPoint = (props: PickLayerProps) => { useEffect(() => { const handleMapClick = (event) => { - if (getCoordinates && typeof startingPoint !== "string") { - startingPoint?.push(`${event.lngLat.lat},${event.lngLat.lng}`); - setStartingPoint(startingPoint); + if (getCoordinates) { dispatch( addMarker({ - id: `isochrone-${(startingPoint ? startingPoint?.length : 0) + 1}`, + id: `isochrone-${watch.starting_points.latitude.length}`, lat: event.lngLat.lat, long: event.lngLat.lng, - iconName: isochroneMarkerIcons[routing ? routing : "walking"], + iconName: ICON_NAME.LOCATION, }), ); + watch.starting_points.latitude.push(event.lngLat.lat); + watch.starting_points.longitude.push(event.lngLat.lat); + setFormValue("starting_points", { + latitude: watch.starting_points.latitude, + longitude: watch.starting_points.longitude, + }); } }; @@ -114,7 +113,7 @@ const StartingPoint = (props: PickLayerProps) => { map.off("click", handleMapClick); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [getCoordinates, routing]); + }, [getCoordinates, watch.routing_type]); useEffect(() => { let active = true; @@ -125,6 +124,9 @@ const StartingPoint = (props: PickLayerProps) => { const resultCoordinates = testForCoordinates(inputValue); if (resultCoordinates[0]) { const [_, latitude, longitude] = resultCoordinates; + + setFormValue("starting_points", {latitude: [latitude], longitude: [longitude]}) + setOptions([ { feature: { @@ -214,11 +216,16 @@ const StartingPoint = (props: PickLayerProps) => { { - setStartingPoint(event.target.value as string); - }} + {...register("starting_points.layer_id")} > {projectLayers ? projectLayers.map((layer) => @@ -302,13 +306,32 @@ const StartingPoint = (props: PickLayerProps) => { > + `${el},${watch.starting_points.longitude[index]}`, + ) + .join(";") : "" } size="small" onChange={(event: React.ChangeEvent) => { - setStartingPoint(event.target.value.split(";") as string[]); + const inputCoords = event.target.value; + const [latitudes, longitudes] = inputCoords + .split(";") + .map((pair) => pair.split(",")) + .reduce( + ([latAcc, lonAcc], [latitude, longitude]) => [ + [...latAcc, parseFloat(latitude)], + [...lonAcc, parseFloat(longitude)], + ], + [[], []], + ); + setFormValue("starting_points", { + latitude: latitudes, + longitude: longitudes, + }); }} sx={{ margin: `${theme.spacing(1)} 0`, @@ -345,13 +368,6 @@ const StartingPoint = (props: PickLayerProps) => { }} onChange={(_event: unknown, newValue: Result | null) => { setOptions(newValue ? [newValue, ...options] : options); - setStartingPoint( - newValue?.feature.center - ? newValue?.feature.center - .reverse() - .map((coord) => coord.toString()) - : [], - ); setValue(newValue); }} onInputChange={(_event, newInputValue) => { @@ -361,7 +377,8 @@ const StartingPoint = (props: PickLayerProps) => { )} /> @@ -371,4 +388,4 @@ const StartingPoint = (props: PickLayerProps) => { ); }; -export default StartingPoint; +export default StartingPoint; \ No newline at end of file diff --git a/apps/web/components/map/panels/toolbox/tools/join/FieldsToMatch.tsx b/apps/web/components/map/panels/toolbox/tools/join/FieldsToMatch.tsx index a64b2e74..676801c9 100644 --- a/apps/web/components/map/panels/toolbox/tools/join/FieldsToMatch.tsx +++ b/apps/web/components/map/panels/toolbox/tools/join/FieldsToMatch.tsx @@ -13,34 +13,43 @@ import { Icon, ICON_NAME } from "@p4b/ui/components/Icon"; import { useTranslation } from "@/i18n/client"; import { useGetLayerKeys } from "@/hooks/map/ToolsHooks"; -import type { SelectChangeEvent } from "@mui/material"; +// import type { SelectChangeEvent } from "@mui/material"; +import type { UseFormGetValues, UseFormRegister } from "react-hook-form"; +import type { PostJoin } from "@/lib/validations/tools"; interface FieldsToMatchProps { - setFirstField: (value: string) => void; - firstField: string | undefined; - setSecondField: (value: string) => void; - secondField: string | undefined; - firstLayerId: string; - secondLayerId: string; + register: UseFormRegister; + getValues: UseFormGetValues; + // setFirstField: (value: string) => void; + // firstField: string | undefined; + // setSecondField: (value: string) => void; + // secondField: string | undefined; + // firstLayerId: string; + // secondLayerId: string; } const FieldsToMatch = (props: FieldsToMatchProps) => { const { - firstLayerId, - secondLayerId, - setFirstField, - setSecondField, - firstField, - secondField, + register, + getValues, + // firstLayerId, + // secondLayerId, + // setFirstField, + // setSecondField, + // firstField, + // secondField, } = props; const { t } = useTranslation("maps"); const theme = useTheme(); - - const firstLayerKeys = useGetLayerKeys(`user_data.${firstLayerId.split("-").join("")}`); - const secondLayerKeys = useGetLayerKeys(`user_data.${secondLayerId.split("-").join("")}`); - + const firstLayerKeys = useGetLayerKeys( + `user_data.${getValues("target_layer_id").split("-").join("")}`, + ); + const secondLayerKeys = useGetLayerKeys( + `user_data.${getValues("join_layer_id").split("-").join("")}`, + ); + return ( @@ -68,14 +77,15 @@ const FieldsToMatch = (props: FieldsToMatchProps) => { {t("panels.tools.select_field")} { - setSecondField(event.target.value as string); - }} + {...register("join_field")} + // value={secondField} + // onChange={(event: SelectChangeEvent) => { + // setSecondField(event.target.value as string); + // }} > - {secondLayerId.length + {getValues("join_layer_id").length ? secondLayerKeys.keys.map((key) => ( {key.name} @@ -131,4 +142,4 @@ const FieldsToMatch = (props: FieldsToMatchProps) => { ); }; -export default FieldsToMatch; +export default FieldsToMatch; \ No newline at end of file diff --git a/apps/web/components/map/panels/toolbox/tools/join/Join.tsx b/apps/web/components/map/panels/toolbox/tools/join/Join.tsx index eea39030..0e2bab18 100644 --- a/apps/web/components/map/panels/toolbox/tools/join/Join.tsx +++ b/apps/web/components/map/panels/toolbox/tools/join/Join.tsx @@ -1,84 +1,76 @@ -import React, { useState } from "react"; +import React from "react"; import InputLayer from "@/components/map/panels/toolbox/tools/InputLayer"; import FieldsToMatch from "@/components/map/panels/toolbox/tools/join/FieldsToMatch"; import Statistics from "@/components/map/panels/toolbox/tools/join/Statistics"; import { Divider, useTheme, Box, Button } from "@mui/material"; -import { SendJoinFeatureRequest } from "@/lib/api/tools"; +// import { SendJoinFeatureRequest } from "@/lib/api/tools"; import { useTranslation } from "@/i18n/client"; import SaveResult from "@/components/map/panels/toolbox/tools/SaveResult"; +import { useForm } from "react-hook-form"; import type { PostJoin } from "@/lib/validations/tools"; -type ColumStatisticsOperation = - | "count" - | "sum" - | "mean" - | "median" - | "min" - | "max"; +// type ColumStatisticsOperation = +// | "count" +// | "sum" +// | "mean" +// | "median" +// | "min" +// | "max"; -interface JoinProps { - projectId: string; -} - -const Join = (props: JoinProps) => { - const { projectId } = props; - - const [inputValues, setInputValues] = useState(["", ""]); - const [firstField, setFirstField] = useState(undefined); - const [secondField, setSecondField] = useState(undefined); - const [method, setMethod] = useState( - undefined, - ); - const [statisticField, setStatisticField] = useState( - undefined, - ); - const [outputName, setOutputName] = useState("join"); - const [folderSaveID, setFolderSaveID] = useState( - undefined, - ); +const Join = () => { + // const [inputValues, setInputValues] = useState(["", ""]); const theme = useTheme(); const { t } = useTranslation("maps"); + const { + // handleSubmit, + register, + // reset, + // watch, + getValues, + // formState: { errors }, + // control, + } = useForm(); + const handleReset = () => { - setInputValues(["", ""]); - setFirstField(undefined); - setSecondField(undefined); - setMethod(undefined); - setStatisticField(undefined); + // setInputValues(["", ""]); + // setFirstField(undefined); + // setSecondField(undefined); + // setMethod(undefined); + // setStatisticField(undefined); }; const handleRun = () => { - if ( - inputValues[0].length && - inputValues[1].length && - firstField && - secondField && - method && - statisticField - ) { - const requestBody: PostJoin = { - target_layer_id: inputValues[0], - target_field: firstField, - join_layer_id: inputValues[1], - join_field: secondField, - column_statistics: { - operation: method, - field: statisticField, - }, - result_target: { - layer_name: outputName ? outputName : `${statisticField}_${method}`, - folder_id: "159cc0f9-81e9-497d-8823-d9d37507ed54", - project_id: projectId - }, - }; - - console.log(requestBody); - SendJoinFeatureRequest(requestBody); - } else { - console.log("Error: Not all fields are filled"); - } + // if ( + // inputValues[0].length && + // inputValues[1].length && + // firstField && + // secondField && + // method && + // statisticField + // ) { + // const requestBody: PostJoin = { + // target_layer_id: inputValues[0], + // target_field: firstField, + // join_layer_id: inputValues[1], + // join_field: secondField, + // column_statistics: { + // operation: method, + // field: statisticField, + // }, + // result_target: { + // layer_name: outputName ? outputName : `${statisticField}_${method}`, + // folder_id: "159cc0f9-81e9-497d-8823-d9d37507ed54", + // project_id: projectId, + // }, + // }; + // console.log(requestBody); + // SendJoinFeatureRequest(requestBody); + // } else { + // console.log("Error: Not all fields are filled"); + // } }; return ( @@ -90,10 +82,12 @@ const Join = (props: JoinProps) => { > { }} /> - {secondField && method ? ( + {getValues("join_layer_id") && + getValues("column_statistics.operation") ? ( ) : null} @@ -144,4 +145,4 @@ const Join = (props: JoinProps) => { ); }; -export default Join; +export default Join; \ No newline at end of file diff --git a/apps/web/components/map/panels/toolbox/tools/join/Statistics.tsx b/apps/web/components/map/panels/toolbox/tools/join/Statistics.tsx index 828a889e..7425ccf9 100644 --- a/apps/web/components/map/panels/toolbox/tools/join/Statistics.tsx +++ b/apps/web/components/map/panels/toolbox/tools/join/Statistics.tsx @@ -12,30 +12,35 @@ import { import { useTranslation } from "@/i18n/client"; import { useGetLayerKeys } from "@/hooks/map/ToolsHooks"; -import type { SelectChangeEvent } from "@mui/material"; -import type { ColumStatisticsOperation } from "@/types/map/toolbox"; +import type { UseFormGetValues, UseFormRegister } from "react-hook-form"; +import type { PostJoin } from "@/lib/validations/tools"; interface StatisticsProps { - secondLayerId: string; - secondField: string | undefined; - setMethod: (value: ColumStatisticsOperation | undefined) => void; - method: ColumStatisticsOperation | undefined; - setStatisticField: (value: string) => void; - statisticField: string | undefined; + register: UseFormRegister; + getValues: UseFormGetValues; + // secondLayerId: string; + // secondField: string | undefined; + // setMethod: (value: ColumStatisticsOperation | undefined) => void; + // method: ColumStatisticsOperation | undefined; + // setStatisticField: (value: string) => void; + // statisticField: string | undefined; } const Statistics = (props: StatisticsProps) => { const { - secondLayerId, - secondField, - setMethod, - method, - setStatisticField, - statisticField + register, + getValues, + // secondLayerId, + // secondField, + // setMethod, + // method, + // setStatisticField, + // statisticField } = props; const theme = useTheme(); const { t } = useTranslation("maps"); + const method = getValues("column_statistics.operation"); const methods = [ { @@ -64,7 +69,7 @@ const Statistics = (props: StatisticsProps) => { }, ]; - const saveFieldKeys = useGetLayerKeys(`user_data.${secondLayerId.split("-").join("")}`); + const saveFieldKeys = useGetLayerKeys(`user_data.${getValues("join_layer_id").split("-").join("")}`); function checkType() { return saveFieldKeys.keys.map((key) => @@ -97,12 +102,13 @@ const Statistics = (props: StatisticsProps) => { {t("panels.tools.select_method")} { - setStatisticField(event.target.value as string); - }} + // value={statisticField} + // onChange={(event: SelectChangeEvent) => { + // setStatisticField(event.target.value as string); + // }} + {...register("column_statistics.field")} > - {secondLayerId.length ? checkType() : null} + {register("join_layer_id").length ? checkType() : null} diff --git a/apps/web/lib/validations/isochrone.ts b/apps/web/lib/validations/isochrone.ts index 9dc7bfde..b634ffc9 100644 --- a/apps/web/lib/validations/isochrone.ts +++ b/apps/web/lib/validations/isochrone.ts @@ -1,69 +1,54 @@ import * as z from "zod"; -const StartingPoint = z.object({ - latitude: z.array(z.number()), - longitude: z.array(z.number()) -}).or(z.object({ - layer_id: z.string() -})) - -export const IsochroneBaseSchema = z.object({ - starting_points: StartingPoint, - routing_type: z.string().or(z.object({ - mode: z.array(z.string()), - egress_mode: z.string(), - access_mode: z.string() - })), - travel_cost: z.object({ - max_traveltime: z.number(), - traveltime_step: z.number(), - speed: z.number().optional(), - }).or(z.object({ - max_distance: z.number(), - distance_step: z.number() - })), - time_window: z.object({ - weekday: z.string(), - from_time: z.number(), - to_time: z.number(), - }).optional(), - result_target: z.object({ - layer_name: z.string(), - folder_id: z.string(), - project_id: z.string().optional() +const StartingPoint = z + .object({ + latitude: z.array(z.number()), + longitude: z.array(z.number()), }) + .or( + z.object({ + layer_id: z.string(), + }), + ); + +const TraveltimeCost = z.object({ + max_traveltime: z.number().min(1).max(45), + traveltime_step: z.number().multipleOf(50), + speed: z.number().min(1).max(25).optional(), }); -export const IsochronePTSchema = z.object({ - starting_points: z.object({ - latitude: z.array(z.number()), - longitude: z.array(z.number()) - }).or(z.object({ - layer_id: z.string() - })), - routing_type: z.object({ - mode: z.array(z.string()), - egress_mode: z.string(), - access_mode: z.string() - }), - travel_cost: z.object({ - max_traveltime: z.number(), - traveltime_step: z.number(), +const DistanceCost = z + .object({ + max_distance: z.number().max(20000).multipleOf(50), + distance_step: z.number(), speed: z.number().optional(), - }).or(z.object({ - max_distance: z.number(), - distance_step: z.number() - })), - time_window: z.object({ - weekday: z.string(), - from_time: z.number(), - to_time: z.number(), - }).optional(), + }) + .refine((schema) => { + return schema.distance_step <= schema.max_distance; + }); + +export const IsochroneBaseSchema = z.object({ + starting_points: StartingPoint, + routing_type: z.string().or( + z.object({ + mode: z.array(z.string()), + egress_mode: z.string(), + access_mode: z.string(), + }), + ), + travel_cost: TraveltimeCost.or(DistanceCost), + time_window: z + .object({ + weekday: z.string(), + from_time: z.number(), + to_time: z.number(), + }) + .optional(), result_target: z.object({ layer_name: z.string(), folder_id: z.string(), - project_id: z.string().optional() - }) + project_id: z.string().optional(), + }), }); export type StartingPointType = z.infer;