Projekt

Obecné

Profil

« Předchozí | Další » 

Revize dda6e56e

Přidáno uživatelem Václav Honzík před asi 2 roky(ů)

Send text query to the api

re #9629

Zobrazit rozdíly:

backend/src/main/java/cz/zcu/kiv/backendapi/path/PathController.java
4 4
import io.swagger.v3.oas.annotations.Operation;
5 5
import lombok.RequiredArgsConstructor;
6 6
import org.springframework.web.bind.annotation.GetMapping;
7
import org.springframework.web.bind.annotation.PostMapping;
7 8
import org.springframework.web.bind.annotation.RequestBody;
8 9
import org.springframework.web.bind.annotation.RequestMapping;
9 10
import org.springframework.web.bind.annotation.RestController;
......
27 28
     * @param pathDto catalog DTO with text to be searched
28 29
     * @return path DTO with highlighted text and found catalog items
29 30
     */
30
    @GetMapping("")
31
    @PostMapping("")
31 32
    @Operation(summary = "returns path with highlighted text and found catalog items based on given text")
32 33
    public PathDto getPath(@RequestBody PathDto pathDto) {
33 34
        return catalogItemService.getPath(pathDto.getText());
frontend/src/App.tsx
23 23
            <Fragment>
24 24
                <Notification />
25 25
                <Navigation>
26
                    <Box sx={{ mx: 10 }}>
26
                    <Box sx={{ mx: 5 }}>
27 27
                        <Routes>
28 28
                            <Route path="/" element={<Home />} />
29 29
                            <Route path="/editHome" element={<EditHome />} />
frontend/src/features/Catalog/catalogSlice.ts
1
import { createSlice } from '@reduxjs/toolkit'
2
import { CatalogItemDto } from '../../swagger/data-contracts'
3
import { fetchItems } from './catalogThunks'
4

  
5
export interface CatalogFilter {
6
    name?: string
7
    type?: string
8
    country?: string
9
}
10

  
11
export interface CatalogState {
12
    items: CatalogItemDto[] // list of all fetched items
13
    filter: CatalogFilter // filter object
14
    filterOpen: boolean
15
    loading: boolean // whether the catalog is loading
16
    error?: string
17
    rowsPerPage: number
18
    rowsPerPageOptions: any[]
19
}
20

  
21
export const ShowAllItemsOption = 'All'
22

  
23
const initialState: CatalogState = {
24
    items: [],
25
    filter: {},
26
    filterOpen: false,
27
    loading: true,
28
    error: undefined,
29
    rowsPerPage: 20,
30
    rowsPerPageOptions: [5, 10, 20, 50, 100, 1000, ShowAllItemsOption],
31
}
32

  
33
const catalogSlice = createSlice({
34
    name: 'catalog',
35
    initialState,
36
    reducers: {
37
        setFilter: (state, action) => ({
38
            ...state,
39
            filter: { ...action.payload },
40
        }),
41
        clearFilter: (state, action) => ({
42
            ...state,
43
            loading: true,
44
            filter: {},
45
        }),
46
        setFilterOpen: (state, action) => ({
47
            ...state,
48
            filterOpen: action.payload,
49
        }),
50
        clear: (state) => ({ ...initialState }),
51
        setLoading: (state) => ({ ...state, loading: true }),
52
        consumeError: (state) => ({ ...state, error: undefined }),
53
        setRowsPerPage: (state, action) => ({
54
            ...state,
55
            rowsPerPage: action.payload,
56
        }),
57
    },
58
    extraReducers: (builder) => {
59
        builder.addCase(fetchItems.pending, (state) => ({
60
            ...state,
61
            loading: true,
62
        }))
63
        builder.addCase(fetchItems.fulfilled, (state, action) => ({
64
            ...state,
65
            items: action.payload,
66
            loading: false,
67
        }))
68
        builder.addCase(fetchItems.rejected, (state, action) => ({
69
            ...state,
70
            loading: false,
71
            error: action.error.message as string,
72
        }))
73
    },
74
})
75

  
76
export const {
77
    setFilter,
78
    clearFilter,
79
    setFilterOpen,
80
    clear,
81
    setLoading,
82
    consumeError,
83
    setRowsPerPage,
84
} = catalogSlice.actions
85
const reducer = catalogSlice.reducer
86
export default reducer
frontend/src/features/Catalog/catalogSlice.tsx
1
import { createSlice } from '@reduxjs/toolkit'
2
import { CatalogItemDto } from '../../swagger/data-contracts'
3
import { fetchItems } from './catalogThunks'
4

  
5
export interface CatalogFilter {
6
    name?: string
7
    type?: string
8
    country?: string
9
}
10

  
11
export interface CatalogState {
12
    items: CatalogItemDto[] // list of all fetched items
13
    filter: CatalogFilter // filter object
14
    filterOpen: boolean
15
    loading: boolean // whether the catalog is loading
16
    error?: string
17
    rowsPerPage: number
18
    rowsPerPageOptions: any[]
19
}
20

  
21
export const ShowAllItemsOption = 'All'
22

  
23
const initialState: CatalogState = {
24
    items: [],
25
    filter: {},
26
    filterOpen: false,
27
    loading: true,
28
    error: undefined,
29
    rowsPerPage: 20,
30
    rowsPerPageOptions: [5, 10, 20, 50, 100, 1000, ShowAllItemsOption],
31
}
32

  
33
const catalogSlice = createSlice({
34
    name: 'catalog',
35
    initialState,
36
    reducers: {
37
        setFilter: (state, action) => ({
38
            ...state,
39
            filter: { ...action.payload },
40
        }),
41
        clearFilter: (state, action) => ({
42
            ...state,
43
            loading: true,
44
            filter: {},
45
        }),
46
        setFilterOpen: (state, action) => ({
47
            ...state,
48
            filterOpen: action.payload,
49
        }),
50
        clear: (state) => ({ ...initialState }),
51
        setLoading: (state) => ({ ...state, loading: true }),
52
        consumeError: (state) => ({ ...state, error: undefined }),
53
        setRowsPerPage: (state, action) => ({
54
            ...state,
55
            rowsPerPage: action.payload,
56
        }),
57
    },
58
    extraReducers: (builder) => {
59
        builder.addCase(fetchItems.pending, (state) => ({
60
            ...state,
61
            loading: true,
62
        }))
63
        builder.addCase(fetchItems.fulfilled, (state, action) => ({
64
            ...state,
65
            items: action.payload,
66
            loading: false,
67
        }))
68
        builder.addCase(fetchItems.rejected, (state, action) => ({
69
            ...state,
70
            loading: false,
71
            error: action.error.message as string,
72
        }))
73
    },
74
})
75

  
76
export const {
77
    setFilter,
78
    clearFilter,
79
    setFilterOpen,
80
    clear,
81
    setLoading,
82
    consumeError,
83
    setRowsPerPage,
84
} = catalogSlice.actions
85
const reducer = catalogSlice.reducer
86
export default reducer
frontend/src/features/Navigation/NavigationMenu.tsx
53 53
    useEffect(() => {
54 54
        setMenuItems(getNavigationItems(userRoles))
55 55
    }, [userRoles])
56
    
56 57

  
57 58
    return (
58 59
        <Drawer
frontend/src/features/TrackingTool/PlaintextUpload.tsx
1 1
import {
2 2
    Button,
3
    Dialog,
3 4
    DialogContent,
4 5
    DialogTitle,
5 6
    Stack,
6 7
    TextField,
7 8
} from '@mui/material'
8 9
import { useFormik } from 'formik'
9
import { Fragment } from 'react'
10
import ButtonOpenableDialog from '../Reusables/ButtonOpenableDialog'
10
import { Fragment, FunctionComponent, useState } from 'react'
11 11
import SendIcon from '@mui/icons-material/Send'
12 12
import ClearIcon from '@mui/icons-material/Clear'
13
import { PathDto } from '../../swagger/data-contracts'
14
import axiosInstance from '../../api/api'
15
import { useDispatch } from 'react-redux'
16
import { showNotification } from '../Notification/notificationSlice'
17

  
18
export interface PlaintextUploadProps {
19
    setPaths: React.Dispatch<React.SetStateAction<PathDto | undefined>>
20
}
21

  
22
const PlaintextUpload: FunctionComponent<PlaintextUploadProps> = ({
23
    setPaths,
24
}) => {
25
    const [submitButtonDisabled, setSubmitButtonDisabled] = useState(false)
26
    const [open, setOpen] = useState(false) // controls whether the dialog is open
27

  
28
    const dispatch = useDispatch()
13 29

  
14
const PlaintextUpload = () => {
15 30
    const formik = useFormik({
16 31
        initialValues: {
17 32
            text: '',
18 33
        },
19
        onSubmit: () => {
34
        onSubmit: async () => {
35
            let closeDialog = false
36
            setSubmitButtonDisabled(true)
37
            try {
38
                const { data, status } = await axiosInstance.post('path', {
39
                    text: formik.values.text,
40
                })
41

  
42
                if (status !== 200) {
43
                    dispatch(
44
                        showNotification({
45
                            message:
46
                                'Error while fetching map, please try again later.',
47
                            severity: 'error',
48
                        })
49
                    )
50
                } else {
51
                    dispatch(
52
                        showNotification({
53
                            message: 'Map fetched successfully.',
54
                            severity: 'success',
55
                        })
56
                    )
57
                    setPaths(data)
58
                    closeDialog = true
59
                    console.log(data)
60
                }
61
            } catch (err: any) {
62
                dispatch(showNotification)
63
                closeDialog = true
64
            }
65
            setSubmitButtonDisabled(false)
66

  
67
            if (closeDialog) {
68
                onCloseDialog()
69
            }
20 70
        },
21 71
    })
22 72

  
73
    const onCloseDialog = () => {
74
        formik.resetForm()
75
        setOpen(false)
76
    }
77

  
23 78
    const resetForm = () => {
24 79
        formik.resetForm()
25 80
    }
26 81

  
27 82
    return (
28 83
        <Fragment>
29
            <ButtonOpenableDialog
30
                buttonText="Plaintext"
31
                buttonColor="primary"
32
                buttonVariant="contained"
84
            <Button variant="contained" onClick={() => setOpen(true)}>
85
                Text
86
            </Button>
87
            <Dialog
88
                open={open}
89
                fullWidth={true}
90
                onClose={onCloseDialog}
91
                maxWidth="lg"
33 92
            >
34 93
                <DialogTitle>Plaintext Input</DialogTitle>
35 94
                <DialogContent>
36
                    <form onSubmit={formik.handleChange}>
95
                    <form onSubmit={formik.handleSubmit}>
37 96
                        <TextField
38 97
                            sx={{ my: 2 }}
39 98
                            fullWidth
......
58 117
                            >
59 118
                                Clear
60 119
                            </Button>
61
                            <Button type="submit" variant="contained" startIcon={<SendIcon />}>
120
                            <Button
121
                                type="submit"
122
                                variant="contained"
123
                                disabled={submitButtonDisabled}
124
                                startIcon={<SendIcon />}
125
                            >
62 126
                                Submit
63 127
                            </Button>
64 128
                        </Stack>
65 129
                    </form>
66 130
                </DialogContent>
67
            </ButtonOpenableDialog>
131
            </Dialog>
68 132
        </Fragment>
69 133
    )
70 134
}
frontend/src/features/TrackingTool/TrackingTool.tsx
1
import { Button, Divider, Grid, Stack, Typography } from '@mui/material'
2
import { Fragment } from 'react'
1
import {
2
    Button,
3
    Card,
4
    CardContent,
5
    Divider,
6
    Grid,
7
    Paper,
8
    Stack,
9
    Typography,
10
} from '@mui/material'
11
import { Fragment, useState } from 'react'
3 12
import AddIcon from '@mui/icons-material/Add'
4 13
import { MapContainer, Marker, Polyline, Popup, TileLayer } from 'react-leaflet'
5 14
import mapConfig from '../../config/mapConfig'
......
8 17
import FileUpload from './FileUpload'
9 18
import L from 'leaflet'
10 19
import DeleteIcon from '@mui/icons-material/Delete'
20
import { PathDto } from '../../swagger/data-contracts'
21
import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils'
11 22

  
12 23
// Page with tracking tool
13 24
const TrackingTool = () => {
25
    // Path response from the API
26
    const [pathDto, setPathDto] = useState<PathDto | undefined>(undefined)
27

  
14 28
    const createDummyPathCoords = () => {
15 29
        // Sample dummy path to display
16 30
        const dummyPath = []
......
39 53

  
40 54
        for (let i = 0; i < coords.length - 1; i += 1) {
41 55
            polylines.push(
42
                // <Polyline
43
                //     key={i}
44
                //     positions={[
45
                //         [dummyPath[i].latitude, dummyPath[i].longitude],
46
                //         [dummyPath[i + 1].latitude, dummyPath[i + 1].longitude],
47
                //     ]}
48
                //     color="red"
49
                //     weight={5}
50
                //     opacity={1}
51

  
52
                //     smoothFactor={1}
53
                // >
54
                //     <Popup>Caesar 🥗 War Path (Allegedly)</Popup>
55
                //     </Polyline>
56 56
                <TextPath
57 57
                    positions={[
58 58
                        [coords[i].latitude, coords[i].longitude],
......
81 81
                Tracking Tool
82 82
            </Typography>
83 83

  
84
            {pathDto && (
85
                <Fragment>
86
                    <Card variant="outlined">
87
                        <CardContent>
88
                            <Stack direction="column">
89
                                <Typography
90
                                    variant="h5"
91
                                    sx={{ mb: 1 }}
92
                                    fontWeight="600"
93
                                >
94
                                    Sought text
95
                                </Typography>
96
                                <Typography variant="body2">
97
                                    {formatHtmlStringToReactDom(
98
                                        pathDto.text ?? ''
99
                                    )}
100
                                </Typography>
101
                            </Stack>
102
                        </CardContent>
103
                    </Card>
104
                </Fragment>
105
            )}
84 106
            <Grid container>
85
                <Grid item xs={12} md={4}>
107
                <Grid item xs={12}>
108
                    {pathDto && pathDto?.foundCatalogItems?.length === 0 && (
109
                        <Typography
110
                            variant="body1"
111
                            sx={{ mb: 2 }}
112
                            fontWeight="500"
113
                            align="center"
114
                            color="error.main"
115
                        >
116
                            Looks like no path / catalog items match this query.
117
                        </Typography>
118
                    )}
119

  
86 120
                    <Stack
87 121
                        direction="row"
88 122
                        alignItems="flex-start"
......
94 128
                            sx={{ mb: 2 }}
95 129
                            fontWeight="500"
96 130
                        >
97
                            Upload:
131
                            {pathDto ? 'Update path' : 'Show Path'}
98 132
                        </Typography>
99
                        <PlaintextUpload />
133
                        <PlaintextUpload setPaths={setPathDto} />
100 134
                        <FileUpload />
101 135
                    </Stack>
102 136
                </Grid>
137

  
103 138
                <Grid
104 139
                    item
105 140
                    xs={12}
......
123 158
                            url={mapConfig.url}
124 159
                        />
125 160
                        {coords.map(({ latitude, longitude }, idx) => (
126
                            <Marker
127
                                position={[latitude, longitude]}
128
                            />
161
                            <Marker position={[latitude, longitude]} />
129 162
                        ))}
130 163
                        {polylines}
131 164
                    </MapContainer>
frontend/src/swagger/CatalogItems.ts
65 65
   * @summary returns catalog items based on filter
66 66
   * @request GET:/catalog-items
67 67
   */
68
  getCatalog = (query?: { name?: string; country?: string; type?: string }, params: RequestParams = {}) =>
68
  getCatalog = (
69
    query?: { name?: string; country?: string; type?: string; writtenForm?: string },
70
    params: RequestParams = {},
71
  ) =>
69 72
    this.request<CatalogItemDto[], any>({
70 73
      path: `/catalog-items`,
71 74
      method: "GET",

Také k dispozici: Unified diff