commit dda6e56ea0a9d547c0a0c749713a266335804993
Author: Vaclav Honzik <vaclavhonzik98@gmail.com>
Date:   Thu May 5 15:39:49 2022 +0200

    Send text query to the api
    
    re #9629

diff --git a/backend/src/main/java/cz/zcu/kiv/backendapi/path/PathController.java b/backend/src/main/java/cz/zcu/kiv/backendapi/path/PathController.java
index 1311524..b64b1f4 100644
--- a/backend/src/main/java/cz/zcu/kiv/backendapi/path/PathController.java
+++ b/backend/src/main/java/cz/zcu/kiv/backendapi/path/PathController.java
@@ -4,6 +4,7 @@ import cz.zcu.kiv.backendapi.catalog.ICatalogItemService;
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -27,7 +28,7 @@ public class PathController {
      * @param pathDto catalog DTO with text to be searched
      * @return path DTO with highlighted text and found catalog items
      */
-    @GetMapping("")
+    @PostMapping("")
     @Operation(summary = "returns path with highlighted text and found catalog items based on given text")
     public PathDto getPath(@RequestBody PathDto pathDto) {
         return catalogItemService.getPath(pathDto.getText());
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index e6103ee..f4fd9d0 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -23,7 +23,7 @@ const App = () => {
             <Fragment>
                 <Notification />
                 <Navigation>
-                    <Box sx={{ mx: 10 }}>
+                    <Box sx={{ mx: 5 }}>
                         <Routes>
                             <Route path="/" element={<Home />} />
                             <Route path="/editHome" element={<EditHome />} />
diff --git a/frontend/src/features/Catalog/catalogSlice.ts b/frontend/src/features/Catalog/catalogSlice.ts
new file mode 100644
index 0000000..62e44a3
--- /dev/null
+++ b/frontend/src/features/Catalog/catalogSlice.ts
@@ -0,0 +1,86 @@
+import { createSlice } from '@reduxjs/toolkit'
+import { CatalogItemDto } from '../../swagger/data-contracts'
+import { fetchItems } from './catalogThunks'
+
+export interface CatalogFilter {
+    name?: string
+    type?: string
+    country?: string
+}
+
+export interface CatalogState {
+    items: CatalogItemDto[] // list of all fetched items
+    filter: CatalogFilter // filter object
+    filterOpen: boolean
+    loading: boolean // whether the catalog is loading
+    error?: string
+    rowsPerPage: number
+    rowsPerPageOptions: any[]
+}
+
+export const ShowAllItemsOption = 'All'
+
+const initialState: CatalogState = {
+    items: [],
+    filter: {},
+    filterOpen: false,
+    loading: true,
+    error: undefined,
+    rowsPerPage: 20,
+    rowsPerPageOptions: [5, 10, 20, 50, 100, 1000, ShowAllItemsOption],
+}
+
+const catalogSlice = createSlice({
+    name: 'catalog',
+    initialState,
+    reducers: {
+        setFilter: (state, action) => ({
+            ...state,
+            filter: { ...action.payload },
+        }),
+        clearFilter: (state, action) => ({
+            ...state,
+            loading: true,
+            filter: {},
+        }),
+        setFilterOpen: (state, action) => ({
+            ...state,
+            filterOpen: action.payload,
+        }),
+        clear: (state) => ({ ...initialState }),
+        setLoading: (state) => ({ ...state, loading: true }),
+        consumeError: (state) => ({ ...state, error: undefined }),
+        setRowsPerPage: (state, action) => ({
+            ...state,
+            rowsPerPage: action.payload,
+        }),
+    },
+    extraReducers: (builder) => {
+        builder.addCase(fetchItems.pending, (state) => ({
+            ...state,
+            loading: true,
+        }))
+        builder.addCase(fetchItems.fulfilled, (state, action) => ({
+            ...state,
+            items: action.payload,
+            loading: false,
+        }))
+        builder.addCase(fetchItems.rejected, (state, action) => ({
+            ...state,
+            loading: false,
+            error: action.error.message as string,
+        }))
+    },
+})
+
+export const {
+    setFilter,
+    clearFilter,
+    setFilterOpen,
+    clear,
+    setLoading,
+    consumeError,
+    setRowsPerPage,
+} = catalogSlice.actions
+const reducer = catalogSlice.reducer
+export default reducer
diff --git a/frontend/src/features/Catalog/catalogSlice.tsx b/frontend/src/features/Catalog/catalogSlice.tsx
deleted file mode 100644
index 62e44a3..0000000
--- a/frontend/src/features/Catalog/catalogSlice.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { createSlice } from '@reduxjs/toolkit'
-import { CatalogItemDto } from '../../swagger/data-contracts'
-import { fetchItems } from './catalogThunks'
-
-export interface CatalogFilter {
-    name?: string
-    type?: string
-    country?: string
-}
-
-export interface CatalogState {
-    items: CatalogItemDto[] // list of all fetched items
-    filter: CatalogFilter // filter object
-    filterOpen: boolean
-    loading: boolean // whether the catalog is loading
-    error?: string
-    rowsPerPage: number
-    rowsPerPageOptions: any[]
-}
-
-export const ShowAllItemsOption = 'All'
-
-const initialState: CatalogState = {
-    items: [],
-    filter: {},
-    filterOpen: false,
-    loading: true,
-    error: undefined,
-    rowsPerPage: 20,
-    rowsPerPageOptions: [5, 10, 20, 50, 100, 1000, ShowAllItemsOption],
-}
-
-const catalogSlice = createSlice({
-    name: 'catalog',
-    initialState,
-    reducers: {
-        setFilter: (state, action) => ({
-            ...state,
-            filter: { ...action.payload },
-        }),
-        clearFilter: (state, action) => ({
-            ...state,
-            loading: true,
-            filter: {},
-        }),
-        setFilterOpen: (state, action) => ({
-            ...state,
-            filterOpen: action.payload,
-        }),
-        clear: (state) => ({ ...initialState }),
-        setLoading: (state) => ({ ...state, loading: true }),
-        consumeError: (state) => ({ ...state, error: undefined }),
-        setRowsPerPage: (state, action) => ({
-            ...state,
-            rowsPerPage: action.payload,
-        }),
-    },
-    extraReducers: (builder) => {
-        builder.addCase(fetchItems.pending, (state) => ({
-            ...state,
-            loading: true,
-        }))
-        builder.addCase(fetchItems.fulfilled, (state, action) => ({
-            ...state,
-            items: action.payload,
-            loading: false,
-        }))
-        builder.addCase(fetchItems.rejected, (state, action) => ({
-            ...state,
-            loading: false,
-            error: action.error.message as string,
-        }))
-    },
-})
-
-export const {
-    setFilter,
-    clearFilter,
-    setFilterOpen,
-    clear,
-    setLoading,
-    consumeError,
-    setRowsPerPage,
-} = catalogSlice.actions
-const reducer = catalogSlice.reducer
-export default reducer
diff --git a/frontend/src/features/Navigation/NavigationMenu.tsx b/frontend/src/features/Navigation/NavigationMenu.tsx
index b8007f1..d066ac3 100644
--- a/frontend/src/features/Navigation/NavigationMenu.tsx
+++ b/frontend/src/features/Navigation/NavigationMenu.tsx
@@ -53,6 +53,7 @@ const NavigationMenu: FunctionComponent<NavigationMenuProps> = ({
     useEffect(() => {
         setMenuItems(getNavigationItems(userRoles))
     }, [userRoles])
+    
 
     return (
         <Drawer
diff --git a/frontend/src/features/TrackingTool/PlaintextUpload.tsx b/frontend/src/features/TrackingTool/PlaintextUpload.tsx
index 98788d7..00d4fb4 100644
--- a/frontend/src/features/TrackingTool/PlaintextUpload.tsx
+++ b/frontend/src/features/TrackingTool/PlaintextUpload.tsx
@@ -1,39 +1,98 @@
 import {
     Button,
+    Dialog,
     DialogContent,
     DialogTitle,
     Stack,
     TextField,
 } from '@mui/material'
 import { useFormik } from 'formik'
-import { Fragment } from 'react'
-import ButtonOpenableDialog from '../Reusables/ButtonOpenableDialog'
+import { Fragment, FunctionComponent, useState } from 'react'
 import SendIcon from '@mui/icons-material/Send'
 import ClearIcon from '@mui/icons-material/Clear'
+import { PathDto } from '../../swagger/data-contracts'
+import axiosInstance from '../../api/api'
+import { useDispatch } from 'react-redux'
+import { showNotification } from '../Notification/notificationSlice'
+
+export interface PlaintextUploadProps {
+    setPaths: React.Dispatch<React.SetStateAction<PathDto | undefined>>
+}
+
+const PlaintextUpload: FunctionComponent<PlaintextUploadProps> = ({
+    setPaths,
+}) => {
+    const [submitButtonDisabled, setSubmitButtonDisabled] = useState(false)
+    const [open, setOpen] = useState(false) // controls whether the dialog is open
+
+    const dispatch = useDispatch()
 
-const PlaintextUpload = () => {
     const formik = useFormik({
         initialValues: {
             text: '',
         },
-        onSubmit: () => {
+        onSubmit: async () => {
+            let closeDialog = false
+            setSubmitButtonDisabled(true)
+            try {
+                const { data, status } = await axiosInstance.post('path', {
+                    text: formik.values.text,
+                })
+
+                if (status !== 200) {
+                    dispatch(
+                        showNotification({
+                            message:
+                                'Error while fetching map, please try again later.',
+                            severity: 'error',
+                        })
+                    )
+                } else {
+                    dispatch(
+                        showNotification({
+                            message: 'Map fetched successfully.',
+                            severity: 'success',
+                        })
+                    )
+                    setPaths(data)
+                    closeDialog = true
+                    console.log(data)
+                }
+            } catch (err: any) {
+                dispatch(showNotification)
+                closeDialog = true
+            }
+            setSubmitButtonDisabled(false)
+
+            if (closeDialog) {
+                onCloseDialog()
+            }
         },
     })
 
+    const onCloseDialog = () => {
+        formik.resetForm()
+        setOpen(false)
+    }
+
     const resetForm = () => {
         formik.resetForm()
     }
 
     return (
         <Fragment>
-            <ButtonOpenableDialog
-                buttonText="Plaintext"
-                buttonColor="primary"
-                buttonVariant="contained"
+            <Button variant="contained" onClick={() => setOpen(true)}>
+                Text
+            </Button>
+            <Dialog
+                open={open}
+                fullWidth={true}
+                onClose={onCloseDialog}
+                maxWidth="lg"
             >
                 <DialogTitle>Plaintext Input</DialogTitle>
                 <DialogContent>
-                    <form onSubmit={formik.handleChange}>
+                    <form onSubmit={formik.handleSubmit}>
                         <TextField
                             sx={{ my: 2 }}
                             fullWidth
@@ -58,13 +117,18 @@ const PlaintextUpload = () => {
                             >
                                 Clear
                             </Button>
-                            <Button type="submit" variant="contained" startIcon={<SendIcon />}>
+                            <Button
+                                type="submit"
+                                variant="contained"
+                                disabled={submitButtonDisabled}
+                                startIcon={<SendIcon />}
+                            >
                                 Submit
                             </Button>
                         </Stack>
                     </form>
                 </DialogContent>
-            </ButtonOpenableDialog>
+            </Dialog>
         </Fragment>
     )
 }
diff --git a/frontend/src/features/TrackingTool/TrackingTool.tsx b/frontend/src/features/TrackingTool/TrackingTool.tsx
index 35461b8..fcc11bd 100644
--- a/frontend/src/features/TrackingTool/TrackingTool.tsx
+++ b/frontend/src/features/TrackingTool/TrackingTool.tsx
@@ -1,5 +1,14 @@
-import { Button, Divider, Grid, Stack, Typography } from '@mui/material'
-import { Fragment } from 'react'
+import {
+    Button,
+    Card,
+    CardContent,
+    Divider,
+    Grid,
+    Paper,
+    Stack,
+    Typography,
+} from '@mui/material'
+import { Fragment, useState } from 'react'
 import AddIcon from '@mui/icons-material/Add'
 import { MapContainer, Marker, Polyline, Popup, TileLayer } from 'react-leaflet'
 import mapConfig from '../../config/mapConfig'
@@ -8,9 +17,14 @@ 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'
 
 // Page with tracking tool
 const TrackingTool = () => {
+    // Path response from the API
+    const [pathDto, setPathDto] = useState<PathDto | undefined>(undefined)
+
     const createDummyPathCoords = () => {
         // Sample dummy path to display
         const dummyPath = []
@@ -39,20 +53,6 @@ const TrackingTool = () => {
 
         for (let i = 0; i < coords.length - 1; i += 1) {
             polylines.push(
-                // <Polyline
-                //     key={i}
-                //     positions={[
-                //         [dummyPath[i].latitude, dummyPath[i].longitude],
-                //         [dummyPath[i + 1].latitude, dummyPath[i + 1].longitude],
-                //     ]}
-                //     color="red"
-                //     weight={5}
-                //     opacity={1}
-
-                //     smoothFactor={1}
-                // >
-                //     <Popup>Caesar 🥗 War Path (Allegedly)</Popup>
-                //     </Polyline>
                 <TextPath
                     positions={[
                         [coords[i].latitude, coords[i].longitude],
@@ -81,8 +81,42 @@ const TrackingTool = () => {
                 Tracking Tool
             </Typography>
 
+            {pathDto && (
+                <Fragment>
+                    <Card variant="outlined">
+                        <CardContent>
+                            <Stack direction="column">
+                                <Typography
+                                    variant="h5"
+                                    sx={{ mb: 1 }}
+                                    fontWeight="600"
+                                >
+                                    Sought text
+                                </Typography>
+                                <Typography variant="body2">
+                                    {formatHtmlStringToReactDom(
+                                        pathDto.text ?? ''
+                                    )}
+                                </Typography>
+                            </Stack>
+                        </CardContent>
+                    </Card>
+                </Fragment>
+            )}
             <Grid container>
-                <Grid item xs={12} md={4}>
+                <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>
+                    )}
+
                     <Stack
                         direction="row"
                         alignItems="flex-start"
@@ -94,12 +128,13 @@ const TrackingTool = () => {
                             sx={{ mb: 2 }}
                             fontWeight="500"
                         >
-                            Upload:
+                            {pathDto ? 'Update path' : 'Show Path'}
                         </Typography>
-                        <PlaintextUpload />
+                        <PlaintextUpload setPaths={setPathDto} />
                         <FileUpload />
                     </Stack>
                 </Grid>
+
                 <Grid
                     item
                     xs={12}
@@ -123,9 +158,7 @@ const TrackingTool = () => {
                             url={mapConfig.url}
                         />
                         {coords.map(({ latitude, longitude }, idx) => (
-                            <Marker
-                                position={[latitude, longitude]}
-                            />
+                            <Marker position={[latitude, longitude]} />
                         ))}
                         {polylines}
                     </MapContainer>
diff --git a/frontend/src/swagger/CatalogItems.ts b/frontend/src/swagger/CatalogItems.ts
index 1b0cb07..9ad9376 100644
--- a/frontend/src/swagger/CatalogItems.ts
+++ b/frontend/src/swagger/CatalogItems.ts
@@ -65,7 +65,10 @@ export class CatalogItems<SecurityDataType = unknown> extends HttpClient<Securit
    * @summary returns catalog items based on filter
    * @request GET:/catalog-items
    */
-  getCatalog = (query?: { name?: string; country?: string; type?: string }, params: RequestParams = {}) =>
+  getCatalog = (
+    query?: { name?: string; country?: string; type?: string; writtenForm?: string },
+    params: RequestParams = {},
+  ) =>
     this.request<CatalogItemDto[], any>({
       path: `/catalog-items`,
       method: "GET",
