commit de12c6bedc1abc0821e20193716eb0a5eb5f70e3
Author: Vaclav Honzik <vaclavhonzik98@gmail.com>
Date:   Mon May 16 16:04:56 2022 +0200

    draggable
    
    re #9741

diff --git a/frontend/src/features/TrackingTool/MapMarker.tsx b/frontend/src/features/TrackingTool/MapMarker.tsx
index 0801643..98c2499 100644
--- a/frontend/src/features/TrackingTool/MapMarker.tsx
+++ b/frontend/src/features/TrackingTool/MapMarker.tsx
@@ -1,14 +1,20 @@
-import { LatLngTuple, Marker as MarkerPOJO } from 'leaflet'
-import { FunctionComponent, ReactNode, useMemo, useRef, useState } from 'react'
-import { Marker } from 'react-leaflet'
+import { LatLngTuple, Marker as MarkerPOJO } from "leaflet"
+import { FunctionComponent, ReactNode, useMemo, useRef, useState } from "react"
+import { Marker } from "react-leaflet"
 
 export interface MapMarkerProps {
     position: LatLngTuple
     children?: ReactNode
+    color?: "external" | "disabled" | "localCatalog"
+    updatePositionCallbackFn: (position: LatLngTuple) => void // Callback function to notify MapPath to rerender the path
 }
 
 // Custom Map Marker component
-const MapMarker: FunctionComponent<MapMarkerProps> = ({ position, children }) => {
+const MapMarker: FunctionComponent<MapMarkerProps> = ({
+    position,
+    children,
+    updatePositionCallbackFn,
+}) => {
     const [currentPosition, setCurrentPosition] = useState(position)
     const markerRef = useRef<MarkerPOJO | null>(null)
     const eventHandlers = useMemo(
@@ -21,9 +27,10 @@ const MapMarker: FunctionComponent<MapMarkerProps> = ({ position, children }) =>
                 }
                 const latlng = marker.getLatLng()
                 setCurrentPosition([latlng.lat, latlng.lng])
+                updatePositionCallbackFn([latlng.lat, latlng.lng])
             },
         }),
-        []
+        [updatePositionCallbackFn]
     )
 
     return (
diff --git a/frontend/src/features/TrackingTool/MapPath.tsx b/frontend/src/features/TrackingTool/MapPath.tsx
index 8c592fc..ea97ec0 100644
--- a/frontend/src/features/TrackingTool/MapPath.tsx
+++ b/frontend/src/features/TrackingTool/MapPath.tsx
@@ -1,184 +1,170 @@
-import { Fragment, FunctionComponent, useEffect, useState } from 'react'
-import { CatalogItemDto } from '../../swagger/data-contracts'
-import { PathVariant } from './buildPathVariants'
-import TextPath from 'react-leaflet-textpath'
-import { Marker, Popup } from 'react-leaflet'
-import {
-    Checkbox,
-    FormControlLabel,
-    Paper,
-    Stack,
-    Typography,
-} from '@mui/material'
-import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils'
-import { DialogCatalogItemDetail as CatalogItemDetailDialog } from '../Catalog/CatalogItemDetail'
-import { useDispatch, useSelector } from 'react-redux'
-import { RootState } from '../redux/store'
-import MapMarker from './MapMarker'
-
-// CatalogItemDto wrapper to keep track whether the item is active or not
-class DisplayableMapPoint {
-    constructor(
-        public readonly catalogItem: CatalogItemDto,
-        public active: boolean = true
-    ) {}
-}
+import { Fragment, FunctionComponent, useEffect, useState } from "react"
+import { useDispatch, useSelector } from "react-redux"
+import { RootState } from "../redux/store"
+import { PathVariant, MapPoint } from "./buildPathVariants"
+import TextPath from "react-leaflet-textpath"
+import { setPrimaryIdx, updateMapMarkerPosition } from "./trackingToolSlice"
+import MapMarker from "./MapMarker"
+import { LatLngTuple } from "leaflet"
+import { Popup } from "react-leaflet"
+import { Checkbox, FormControlLabel, Stack, Typography } from "@mui/material"
+import { formatHtmlStringToReactDom } from "../../utils/formatting/HtmlUtils"
+import { DialogCatalogItemDetail as CatalogItemDetailDialog } from "../Catalog/CatalogItemDetail"
 
 export interface MapPathProps {
-    pathVariant: PathVariant // aka CatalogItemDto[]
-    idx: number // index of path in the list
+    idx: number // index of the path in the list
 }
 
+type EdgeElement = any
+
 // Blue
-export const primaryPathColor = '#346eeb'
+export const primaryPathColor = "#346eeb"
 
 // Grey
-export const secondaryPathColor = '#878e9c'
+export const secondaryPathColor = "#878e9c"
 
-// Map path component
-const MapPath: FunctionComponent<MapPathProps> = ({ idx, pathVariant }) => {
-    // List of all map points that belong to the path
-    const pathVariants = useSelector(
-        (state: RootState) => state.trackingTool.pathVariants
-    )
-    const [mapPoints, setMapPoints] = useState<DisplayableMapPoint[]>([])
+const MapPath: FunctionComponent<MapPathProps> = ({ idx }) => {
     const dispatch = useDispatch()
 
-    // Set of all active paths
-    const activePaths = useSelector(
-        (state: RootState) => state.trackingTool.activePaths
+    // Get list of all paths from the store
+    // And extract path from them
+    const paths = useSelector(
+        (state: RootState) => state.trackingTool.pathVariants
     )
-    // Index of the primary path
+    const [path, setPath] = useState<PathVariant>([])
+    useEffect(() => {
+        // Either set the path if it exists or set it to an empty array
+        setPath(paths && paths.length > idx ? paths[idx] : [])
+    }, [paths])
+
+    // Primary path index to set the correct color
     const primaryPathIdx = useSelector(
         (state: RootState) => state.trackingTool.primaryPathIdx
     )
 
-    // Whether the path is active or not
-    const [active, setActive] = useState(false)
+    // List of all active map points
+    const [displayableMapPoints, setDisplayableMapPoints] = useState<
+        MapPoint[]
+    >([])
     useEffect(() => {
-        setActive(activePaths.has(idx))
-    }, [activePaths, idx])
+        // Set all displayable vertices
+        setDisplayableMapPoints(path.filter((vertex) => vertex.displayable))
+    }, [path])
 
-    const getActiveMapPoints = () => mapPoints.filter((item) => item.active)
-
-    // Refresh the list of map points if it has changed
+    // List of all edges in the path
+    const [edges, setEdges] = useState<EdgeElement[]>([])
     useEffect(() => {
-        if (!pathVariants || pathVariants.length <= idx) {
-            return
-        }
-        setMapPoints(
-            [...pathVariants[idx]]
-                .filter((item) => item.latitude && item.longitude)
-                .map((item) => new DisplayableMapPoint(item))
+        // Get all active map points
+        const activeMapPoints = displayableMapPoints.filter(
+            (item) => item.active
         )
-    }, [pathVariants, idx])
-
-    // Builds all edges of the path
-    const buildEdges = () => {
-        const activeMapPoints = getActiveMapPoints()
         if (activeMapPoints.length < 2) {
-            return null
+            return
         }
 
-        // Create path edges
-        const edges: any[] = []
+        // Build edges
+        const edges = []
         for (let i = 0; i < activeMapPoints.length - 1; i += 1) {
+            const [start, end] = [
+                activeMapPoints[i].catalogItem,
+                activeMapPoints[i + 1].catalogItem,
+            ]
             edges.push(
                 <TextPath
+                    // Somehow this refuses to work so let it rerender everything ...
+                    // key={`${start.latitude},${start.longitude}-${end.latitude},${end.longitude}`}
+                    // key={`${start.id}-${end.id}`}
                     positions={[
-                        [
-                            activeMapPoints[i].catalogItem.latitude,
-                            activeMapPoints[i].catalogItem.longitude,
-                        ],
-                        [
-                            activeMapPoints[i + 1].catalogItem.latitude,
-                            activeMapPoints[i + 1].catalogItem.longitude,
-                        ],
+                        [start.latitude, start.longitude],
+                        [end.latitude, end.longitude],
                     ]}
                     text="►"
                     // text=" > > > > "
                     attributes={{
-                        'font-size': 19,
+                        "font-size": 19,
                         // Set to primaryPathColor if primary index in the tracking tool is equal to this index
                         fill:
                             primaryPathIdx === idx
                                 ? primaryPathColor
                                 : secondaryPathColor,
                     }}
-                    onClick={() => {
-                        dispatch(setPrimaryIdx(idx))
-                    }}
+                    onClick={() => dispatch(setPrimaryIdx(idx))}
                     repeat
                     center
                     weight={0}
                 />
             )
         }
+        setEdges(edges)
+    }, [dispatch, displayableMapPoints, idx, primaryPathIdx])
 
-        // Return the path
-        return edges
-    }
-
-    /**
-     * Creates a list of all vertices in the path
-     */
-    const buildVertices = () => {
-        return mapPoints.map((mapPoint, idx) => (
-            <MapMarker
-                key={idx}
-                position={[
-                    mapPoint.catalogItem.latitude as number,
-                    mapPoint.catalogItem.longitude as number,
-                ]}
-            >
-                <Popup>
-                    <Fragment>
-                        <Stack direction="column" sx={{ m: 0 }}>
-                            <Typography
-                                variant="h6"
-                                fontWeight="bold"
-                                fontSize={16}
-                            >
-                                {formatHtmlStringToReactDom(
-                                    mapPoint.catalogItem.name as string
-                                )}
-                            </Typography>
-                            <FormControlLabel
-                                control={
-                                    <Checkbox
-                                        checked={mapPoint.active}
-                                        onChange={() => {
-                                            mapPoint.active = !mapPoint.active
-                                            setMapPoints([...mapPoints])
-                                        }}
-                                    />
-                                }
-                                labelPlacement="end"
-                                label="Active"
-                            />
-                            <CatalogItemDetailDialog
-                                itemId={mapPoint.catalogItem.id ?? ''}
-                            />
-                        </Stack>
-                    </Fragment>
-                </Popup>
-            </MapMarker>
-        ))
-    }
+    // List of vertices to display
+    const [vertices, setVertices] = useState<JSX.Element[]>([])
+    useEffect(() => {
+        // Iterate over all displayable map points and map them to MapMarker
+        setVertices(
+            displayableMapPoints.map((item) => (
+                <MapMarker
+                    key={`${item.catalogItem.latitude}${item.catalogItem.latitude}`}
+                    position={[
+                        item.catalogItem.latitude as number,
+                        item.catalogItem.longitude as number,
+                    ]}
+                    updatePositionCallbackFn={(position: LatLngTuple) => {
+                        // Update the position of the map point
+                        dispatch(
+                            updateMapMarkerPosition({
+                                idx,
+                                mapPointIdx: item.idx,
+                                position,
+                            })
+                        )
+                    }}
+                >
+                    <Popup>
+                        <Fragment>
+                            <Stack direction="column" sx={{ m: 0 }}>
+                                <Typography
+                                    variant="h6"
+                                    fontWeight="bold"
+                                    fontSize={16}
+                                >
+                                    {formatHtmlStringToReactDom(
+                                        item.catalogItem.name as string
+                                    )}
+                                </Typography>
+                                <FormControlLabel
+                                    control={
+                                        <Checkbox
+                                            checked={item.active}
+                                            onChange={() => {
+                                                item.active = !item.active
+                                                setDisplayableMapPoints([
+                                                    ...displayableMapPoints,
+                                                ])
+                                            }}
+                                        />
+                                    }
+                                    labelPlacement="end"
+                                    label="Active"
+                                />
+                                <CatalogItemDetailDialog
+                                    itemId={item.catalogItem.id ?? ""}
+                                />
+                            </Stack>
+                        </Fragment>
+                    </Popup>
+                </MapMarker>
+            ))
+        )
+    }, [dispatch, displayableMapPoints, idx])
 
     return (
         <Fragment>
-            {active && (
-                <Fragment>
-                    {buildVertices()}
-                    {buildEdges()}
-                </Fragment>
-            )}
+            {vertices}
+            {edges}
         </Fragment>
     )
 }
 
 export default MapPath
-function setPrimaryIdx(idx: number): any {
-    throw new Error('Function not implemented.')
-}
diff --git a/frontend/src/features/TrackingTool/TrackingTool.tsx b/frontend/src/features/TrackingTool/TrackingTool.tsx
index e91f5bd..f096c8c 100644
--- a/frontend/src/features/TrackingTool/TrackingTool.tsx
+++ b/frontend/src/features/TrackingTool/TrackingTool.tsx
@@ -1,168 +1,149 @@
 import {
-    Button,
-    Card,
-    CardContent,
-    Grid,
-    Stack,
-    Typography,
-} from '@mui/material'
-import { Fragment, useEffect, useState } from 'react'
-import { MapContainer, TileLayer, useMap } from 'react-leaflet'
-import mapConfig from '../../config/mapConfig'
-import TextPath from 'react-leaflet-textpath'
-import PlaintextUpload from './PlaintextUpload'
-import FileUpload from './FileUpload'
-import L from 'leaflet'
-import DeleteIcon from '@mui/icons-material/Delete'
-import { PathDto } from '../../swagger/data-contracts'
-import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils'
-import MapPath from './MapPath'
-import EditIcon from '@mui/icons-material/Edit'
-import { useDispatch, useSelector } from 'react-redux'
-import { RootState } from '../redux/store'
-import { clear, consumeErr as consumeError } from './trackingToolSlice'
-import { showNotification } from '../Notification/notificationSlice'
-import ClearIcon from '@mui/icons-material/Clear'
+  Button,
+  Card,
+  CardContent,
+  Grid,
+  Stack,
+  Typography,
+} from "@mui/material"
+import { Fragment, useEffect, useState } from "react"
+import { MapContainer, TileLayer, useMap } from "react-leaflet"
+import mapConfig from "../../config/mapConfig"
+import TextPath from "react-leaflet-textpath"
+import PlaintextUpload from "./PlaintextUpload"
+import FileUpload from "./FileUpload"
+import L from "leaflet"
+import DeleteIcon from "@mui/icons-material/Delete"
+import { PathDto } from "../../swagger/data-contracts"
+import { formatHtmlStringToReactDom } from "../../utils/formatting/HtmlUtils"
+import MapPath from "./MapPath"
+import EditIcon from "@mui/icons-material/Edit"
+import { useDispatch, useSelector } from "react-redux"
+import { RootState } from "../redux/store"
+import { clear, consumeErr as consumeError } from "./trackingToolSlice"
+import { showNotification } from "../Notification/notificationSlice"
+import ClearIcon from "@mui/icons-material/Clear"
 
 // Page with tracking tool
 const TrackingTool = () => {
-    // Path response from the API
-    const pathDto = useSelector(
-        (state: RootState) => state.trackingTool.pathDto
-    )
-    const pathVariants = useSelector(
-        (state: RootState) => state.trackingTool.pathVariants
-    )
-    const mapCenter = useSelector(
-        (state: RootState) => state.trackingTool.mapCenter
-    )
-
-    // const map = useMap()
+  // Path response from the API
+  const pathDto = useSelector((state: RootState) => state.trackingTool.pathDto)
+  const pathVariants = useSelector(
+    (state: RootState) => state.trackingTool.pathVariants
+  )
+  const mapCenter = useSelector(
+    (state: RootState) => state.trackingTool.mapCenter
+  )
 
-    // // Set the map center
-    // useEffect(() => {
-    //     map.flyTo(mapCenter, mapConfig.defaultZoom)
-    // }, [map, mapCenter])
+  // Consume any error
+  const err = useSelector((state: RootState) => state.trackingTool.lastError)
+  const dispatch = useDispatch()
 
-    // Consume any error
-    const err = useSelector((state: RootState) => state.trackingTool.lastError)
-    const dispatch = useDispatch()
+  useEffect(() => {
+    if (!err) {
+      return
+    }
+    const error = `${err}`
+    dispatch(consumeError())
+    dispatch(
+      showNotification({
+        message: error,
+        severity: "error",
+      })
+    )
+  }, [err, dispatch])
 
-    useEffect(() => {
-        if (!err) {
-            return
-        }
-        const error = `${err}`
-        dispatch(consumeError())
-        dispatch(
-            showNotification({
-                message: error,
-                severity: 'error',
-            })
-        )
-    }, [err, dispatch])
+  return (
+    <Fragment>
+      <Typography variant="h3" sx={{ mb: 2 }} fontWeight="bold">
+        Tracking Tool
+      </Typography>
 
-    return (
-        <Fragment>
-            <Typography variant="h3" sx={{ mb: 2 }} fontWeight="bold">
-                Tracking Tool
+      <Grid container>
+        <Grid item xs={12}>
+          {pathDto && pathDto?.foundCatalogItems?.length === 0 && (
+            <Typography
+              variant="body1"
+              sx={{ mb: 2 }}
+              fontWeight="500"
+              align="center"
+              color="error.main"
+            >
+              Looks like no path / catalog items match this query.
             </Typography>
+          )}
+          {!pathDto && (
+            <Stack
+              direction="row"
+              alignItems="flex-start"
+              spacing={2}
+              sx={{ mt: 1 }}
+            >
+              <Typography variant="h5" sx={{ mb: 2 }} fontWeight="500">
+                Upload:
+              </Typography>
+              <PlaintextUpload />
+              <FileUpload />
+            </Stack>
+          )}
 
-            <Grid container>
-                <Grid item xs={12}>
-                    {pathDto && pathDto?.foundCatalogItems?.length === 0 && (
-                        <Typography
-                            variant="body1"
-                            sx={{ mb: 2 }}
-                            fontWeight="500"
-                            align="center"
-                            color="error.main"
-                        >
-                            Looks like no path / catalog items match this query.
-                        </Typography>
-                    )}
-                    {!pathDto && (
-                        <Stack
-                            direction="row"
-                            alignItems="flex-start"
-                            spacing={2}
-                            sx={{ mt: 1 }}
-                        >
-                            <Typography
-                                variant="h5"
-                                sx={{ mb: 2 }}
-                                fontWeight="500"
-                            >
-                                Upload:
-                            </Typography>
-                            <PlaintextUpload />
-                            <FileUpload />
-                        </Stack>
-                    )}
-
-                    {pathDto && (
-                        <Stack alignItems="flex-end">
-                            <Button
-                                startIcon={<ClearIcon />}
-                                sx={{ mb: 1 }}
-                                variant="contained"
-                                onClick={() => dispatch(clear())}
-                            >
-                                Clear Map
-                            </Button>
-                        </Stack>
-                    )}
-                </Grid>
+          {pathDto && (
+            <Stack alignItems="flex-end">
+              <Button
+                startIcon={<ClearIcon />}
+                sx={{ mb: 1 }}
+                variant="contained"
+                onClick={() => dispatch(clear())}
+              >
+                Clear Map
+              </Button>
+            </Stack>
+          )}
+        </Grid>
 
-                <Grid
-                    item
-                    xs={12}
-                    md={12}
-                    style={{
-                        minHeight: '60vh',
-                        maxHeight: '100vh',
-                        width: '100%',
-                    }}
-                >
-                    <MapContainer
-                        center={[mapCenter[0], mapCenter[1]]}
-                        zoom={mapConfig.defaultZoom}
-                        style={{ height: '100%', minHeight: '100%' }}
-                    >
-                        <TileLayer
-                            attribution={mapConfig.attribution}
-                            url={mapConfig.url}
-                        />
-                        {pathVariants?.map((pathVariant, idx) => (
-                            <MapPath pathVariant={pathVariant} idx={idx} />
-                        ))}
-                    </MapContainer>
-                    {pathDto && (
-                        <Fragment>
-                            <Card variant="outlined" sx={{ mt: 2 }}>
-                                <CardContent>
-                                    <Stack direction="column">
-                                        <Typography
-                                            variant="h5"
-                                            sx={{ mb: 1 }}
-                                            fontWeight="600"
-                                        >
-                                            Processed Text
-                                        </Typography>
-                                        <Typography variant="body2">
-                                            {formatHtmlStringToReactDom(
-                                                pathDto.text ?? ''
-                                            )}
-                                        </Typography>
-                                    </Stack>
-                                </CardContent>
-                            </Card>
-                        </Fragment>
-                    )}
-                </Grid>
-            </Grid>
-        </Fragment>
-    )
+        <Grid
+          item
+          xs={12}
+          md={12}
+          style={{
+            minHeight: "60vh",
+            maxHeight: "100vh",
+            width: "100%",
+          }}
+        >
+          <MapContainer
+            center={[mapCenter[0], mapCenter[1]]}
+            zoom={mapConfig.defaultZoom}
+            style={{ height: "100%", minHeight: "100%" }}
+          >
+            <TileLayer
+              attribution={mapConfig.attribution}
+              url={mapConfig.url}
+            />
+            {pathVariants?.map((pathVariant, idx) => (
+              <MapPath idx={idx} />
+            ))}
+          </MapContainer>
+          {pathDto && (
+            <Fragment>
+              <Card variant="outlined" sx={{ mt: 2 }}>
+                <CardContent>
+                  <Stack direction="column">
+                    <Typography variant="h5" sx={{ mb: 1 }} fontWeight="600">
+                      Processed Text
+                    </Typography>
+                    <Typography variant="body2">
+                      {formatHtmlStringToReactDom(pathDto.text ?? "")}
+                    </Typography>
+                  </Stack>
+                </CardContent>
+              </Card>
+            </Fragment>
+          )}
+        </Grid>
+      </Grid>
+    </Fragment>
+  )
 }
 
 export default TrackingTool
diff --git a/frontend/src/features/TrackingTool/buildPathVariants.ts b/frontend/src/features/TrackingTool/buildPathVariants.ts
index 939c485..5f74e85 100644
--- a/frontend/src/features/TrackingTool/buildPathVariants.ts
+++ b/frontend/src/features/TrackingTool/buildPathVariants.ts
@@ -3,8 +3,28 @@
 import { CatalogItemDto, PathDto } from '../../swagger/data-contracts'
 
 // For more comprehensive code alias CatalogItemDto[] as path variant
-export type PathVariant = CatalogItemDto[]
+export type PathVariant = MapPoint[]
 
+export class MapPoint {
+    constructor(
+        public idx: number,
+        public active: boolean,
+        public catalogItem: CatalogItemDto
+    ) { }
+
+    /**
+     * @returns true if the map point is displayable - i.e. it has a valid lat/lng
+     */
+    get displayable() {
+        return !!this.catalogItem.latitude && !!this.catalogItem.longitude
+    }
+}
+
+/**
+ * Cartesian product of two arrays
+ * @param sets
+ * @returns
+ */
 const cartesianProduct = (sets: CatalogItemDto[][]): CatalogItemDto[][] =>
     sets.reduce<CatalogItemDto[][]>(
         (results, ids) =>
@@ -14,17 +34,30 @@ const cartesianProduct = (sets: CatalogItemDto[][]): CatalogItemDto[][] =>
         [[]]
     )
 
+/**
+ * Builds a list of all possible path variants from pathDto
+ * @param pathDto
+ * @returns
+ */
 export const buildPathVariants = (pathDto: PathDto): PathVariant[] => {
     if (!pathDto.foundCatalogItems) {
         return []
     }
 
-    const catalogItems = pathDto.foundCatalogItems
-    if (catalogItems.length === 1) {
-        return catalogItems
-    }
-
-    return cartesianProduct(catalogItems)
+    return (
+        pathDto.foundCatalogItems.length === 1
+            ? pathDto.foundCatalogItems
+            : cartesianProduct(pathDto.foundCatalogItems)
+    ).map((variant, _) =>
+        variant.map(
+            (catalogItem, idx) =>
+                new MapPoint(
+                    idx,
+                    !!catalogItem.latitude && !!catalogItem.longitude,
+                    catalogItem
+                )
+        )
+    )
 }
 
 export default buildPathVariants
diff --git a/frontend/src/features/TrackingTool/trackingToolSlice.ts b/frontend/src/features/TrackingTool/trackingToolSlice.ts
index 1e4c1bc..0060b66 100644
--- a/frontend/src/features/TrackingTool/trackingToolSlice.ts
+++ b/frontend/src/features/TrackingTool/trackingToolSlice.ts
@@ -1,11 +1,11 @@
-import { createSlice } from '@reduxjs/toolkit'
-import { LatLngTuple } from 'leaflet'
-import { persistReducer } from 'redux-persist'
-import mapConfig from '../../config/mapConfig'
-import { PathDto } from '../../swagger/data-contracts'
-import buildPathVariants, { PathVariant } from './buildPathVariants'
-import { sendTextForProcessing } from './trackingToolThunks'
-import storage from 'redux-persist/lib/storage'
+import { createSlice } from "@reduxjs/toolkit"
+import { LatLngTuple } from "leaflet"
+import { persistReducer } from "redux-persist"
+import mapConfig from "../../config/mapConfig"
+import { PathDto } from "../../swagger/data-contracts"
+import buildPathVariants, { PathVariant } from "./buildPathVariants"
+import { sendTextForProcessing } from "./trackingToolThunks"
+import storage from "redux-persist/lib/storage"
 
 export interface TrackingToolState {
     isLoading: boolean // whether the data is being loaded
@@ -14,7 +14,6 @@ export interface TrackingToolState {
     lastError?: string // consumable for errors during thunks
     mapCenter: LatLngTuple // pair of latitude and longitude
     primaryPathIdx: number // index of the primary path
-    activePaths: Set<number> // indices of the active paths
     // trigger to close the dialog when API call is finished
     dialogApiCallSuccess: boolean
     pathsPerPage: number // max number of paths to show on the map at once
@@ -25,12 +24,8 @@ const defaultPathsPerPage = 5
 
 const initialState: TrackingToolState = {
     isLoading: false,
-    mapCenter: [
-        mapConfig.defaultCoordinates[0],
-        mapConfig.defaultCoordinates[1],
-    ],
+    mapCenter: [mapConfig.defaultCoordinates[0], mapConfig.defaultCoordinates[1]],
     primaryPathIdx: 0,
-    activePaths: new Set(),
     dialogApiCallSuccess: true,
     pathsPerPage: defaultPathsPerPage,
     currentPage: 0,
@@ -38,22 +33,27 @@ const initialState: TrackingToolState = {
 
 // Returns tuple of average latitude and longitude
 const calculateMapCenter = (pathVariant: PathVariant): LatLngTuple => [
-    pathVariant.map((item) => item.latitude ?? 0).reduce((a, b) => a + b, 0) /
-        pathVariant.length,
-    pathVariant.map((item) => item.longitude ?? 0).reduce((a, b) => a + b, 0) /
-        pathVariant.length,
+    pathVariant
+        .map((item) => item.catalogItem.latitude ?? 0)
+        .reduce((a, b) => a + b, 0) / pathVariant.length,
+    pathVariant
+        .map((item) => item.catalogItem.longitude ?? 0)
+        .reduce((a, b) => a + b, 0) / pathVariant.length,
 ]
 
 const persistConfig = {
-    key: 'auth',
+    key: "auth",
     storage, // localStorage for browsers
 }
 
 export const trackingToolSlice = createSlice({
-    name: 'trackingTool',
+    name: "trackingTool",
     initialState,
     reducers: {
-        consumeErr: (state: TrackingToolState) => ({ ...state, lastErr: undefined }),
+        consumeErr: (state: TrackingToolState) => ({
+            ...state,
+            lastErr: undefined,
+        }),
         setPrimaryIdx: (state: TrackingToolState, action: any) => ({
             ...state,
             primaryPathIdx: action.payload,
@@ -62,11 +62,39 @@ export const trackingToolSlice = createSlice({
             ...state,
             dialogApiCallSuccess: false,
         }),
-        setPage: (state: TrackingToolState, action: any) => ({
+        setPage: (state: TrackingToolState, action: { payload: number }) => ({
             ...state,
             currentPage: action.payload,
         }),
-        clear: () => ({...initialState})
+        updateMapMarkerPosition: (state: TrackingToolState, action: any) => {
+            const { idx, mapPointIdx, position } = action.payload
+            if (!state.pathVariants || state.pathVariants.length <= idx) {
+                return state
+            }
+
+            return {
+                ...state,
+                pathVariants: state.pathVariants.map((pathVariant, i) => {
+                    if (i !== idx) {
+                        return [...pathVariant]
+                    }
+
+                    const mapPoint = pathVariant[mapPointIdx]
+                    mapPoint.catalogItem = {
+                        ...mapPoint.catalogItem,
+                        latitude: position[0],
+                        longitude: position[1]
+                    }
+
+                    return [
+                        ...pathVariant.slice(0, mapPointIdx),
+                        mapPoint,
+                        ...pathVariant.slice(mapPointIdx + 1),
+                    ]
+                })
+            }
+        },
+        clear: () => ({ ...initialState }),
     },
     extraReducers: (builder) => {
         builder.addCase(sendTextForProcessing.fulfilled, (state, action) => {
@@ -76,8 +104,6 @@ export const trackingToolSlice = createSlice({
                 ...state,
                 pathVariants,
                 pathDto,
-                // TODO map this correctly
-                activePaths: new Set(pathVariants.map((_, idx) => idx)),
                 // TODO calculate correctly
                 mapCenter:
                     pathVariants.length > 0
@@ -105,6 +131,7 @@ export const trackingToolSlice = createSlice({
     },
 })
 
-export const { consumeErr, setPrimaryIdx, resetDialogApiCallSuccess, clear } = trackingToolSlice.actions
+export const { consumeErr, setPrimaryIdx, resetDialogApiCallSuccess, clear, updateMapMarkerPosition } =
+    trackingToolSlice.actions
 const trackingToolReducer = trackingToolSlice.reducer
 export default trackingToolReducer
