Revize 8c57f958
Přidáno uživatelem Václav Honzík před téměř 3 roky(ů)
frontend/src/features/Auth/userSlice.ts | ||
---|---|---|
28 | 28 |
|
29 | 29 |
export const userSlice = createSlice({ |
30 | 30 |
name: 'user', // name to generate action types |
31 |
|
|
32 | 31 |
initialState, // default state |
33 |
|
|
34 | 32 |
// Reducers that update the state |
35 | 33 |
reducers: { |
36 | 34 |
logout: () => initialState, // Reset to the inital state |
... | ... | |
57 | 55 |
return { ...state, lastErr: action.error.message } |
58 | 56 |
} |
59 | 57 |
}) |
60 |
builder.addCase(logIn.pending, (state, action) => {
|
|
58 |
builder.addCase(logIn.pending, (state) => { |
|
61 | 59 |
return { ...state, isLoggingIn: true } |
62 | 60 |
}) |
63 | 61 |
}, |
... | ... | |
65 | 63 |
|
66 | 64 |
const userReducer = persistReducer(persistConfig, userSlice.reducer) |
67 | 65 |
|
68 |
export const { logout, refreshTokens, setErr, setUserState, resetLoggingIn } = userSlice.actions |
|
66 |
export const { logout, refreshTokens, setErr, setUserState, resetLoggingIn } = |
|
67 |
userSlice.actions |
|
69 | 68 |
|
70 | 69 |
export default userReducer |
frontend/src/features/TrackingTool/FileUpload.tsx | ||
---|---|---|
22 | 22 |
file: yup.mixed().required('File is required'), |
23 | 23 |
}) |
24 | 24 |
|
25 |
const [submitButtonEnabled, setSubmitButtonEnabled] = useState(true) |
|
26 |
|
|
27 | 25 |
const formik = useFormik({ |
28 | 26 |
initialValues: { |
29 | 27 |
file: undefined, |
30 | 28 |
}, |
31 | 29 |
validationSchema, |
32 | 30 |
onSubmit: async (values) => { |
33 |
// TODO actually send the file somewhere |
|
34 |
// TODO implement me |
|
35 |
|
|
36 |
const formData = new FormData() |
|
37 |
// @ts-ignore for now |
|
38 |
formData.append('file', values.file as File) |
|
39 |
|
|
40 |
const { data } = await axiosInstance.post('/path', formData, { |
|
41 |
headers: { |
|
42 |
'Content-Type': 'multipart/form-data', |
|
43 |
}, |
|
44 |
}) |
|
31 |
|
|
45 | 32 |
}, |
46 | 33 |
}) |
47 | 34 |
|
frontend/src/features/TrackingTool/MapPath.tsx | ||
---|---|---|
1 |
import { Fragment, FunctionComponent, useState } from 'react' |
|
1 |
import { Fragment, FunctionComponent, useEffect, useState } from 'react'
|
|
2 | 2 |
import { CatalogItemDto } from '../../swagger/data-contracts' |
3 | 3 |
import { PathVariant } from './buildPathVariants' |
4 | 4 |
import TextPath from 'react-leaflet-textpath' |
... | ... | |
6 | 6 |
import { Checkbox, FormControlLabel, Stack, Typography } from '@mui/material' |
7 | 7 |
import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils' |
8 | 8 |
import { DialogCatalogItemDetail as CatalogItemDetailDialog } from '../Catalog/CatalogItemDetail' |
9 |
import { useDispatch, useSelector } from 'react-redux' |
|
10 |
import { RootState } from '../redux/store' |
|
9 | 11 |
|
10 | 12 |
// CatalogItemDto wrapper to keep track whether the item is active or not |
11 | 13 |
class DisplayableMapPoint { |
... | ... | |
17 | 19 |
|
18 | 20 |
export interface MapPathProps { |
19 | 21 |
pathVariant: PathVariant // aka CatalogItemDto[] |
20 |
active: boolean // whether this component is active |
|
21 | 22 |
idx: number // index of path in the list |
22 |
primaryIdx: number // reference to index of path which has primary color |
|
23 |
setPrimary: (idx: number) => void // callback to set the primary path |
|
24 | 23 |
} |
25 | 24 |
|
26 | 25 |
// Blue |
... | ... | |
29 | 28 |
// Grey |
30 | 29 |
export const secondaryPathColor = '#878e9c' |
31 | 30 |
|
32 |
// Map path component |
|
33 |
const MapPath: FunctionComponent<MapPathProps> = ({ |
|
34 |
pathVariant, |
|
35 |
active, |
|
36 |
primaryIdx, |
|
37 |
idx, |
|
38 |
setPrimary |
|
39 |
}) => { |
|
31 |
// Map path component |
|
32 |
const MapPath: FunctionComponent<MapPathProps> = ({ idx, pathVariant }) => { |
|
40 | 33 |
// List of all map points that belong to the path |
41 | 34 |
const [mapPoints, setMapPoints] = useState<DisplayableMapPoint[]>( |
42 | 35 |
pathVariant |
43 | 36 |
.filter((item) => item.latitude && item.longitude) |
44 | 37 |
.map((item) => new DisplayableMapPoint(item)) |
45 | 38 |
) |
46 |
|
|
39 |
|
|
40 |
// Set of all active paths |
|
41 |
const activePaths = useSelector( |
|
42 |
(state: RootState) => state.trackingTool.activePaths |
|
43 |
) |
|
44 |
// Index of the primary path |
|
45 |
const primaryPathIdx = useSelector( |
|
46 |
(state: RootState) => state.trackingTool.primaryPathIdx |
|
47 |
) |
|
48 |
|
|
49 |
// Whether the path is active or not |
|
50 |
const [active, setActive] = useState(false) |
|
51 |
useEffect(() => { |
|
52 |
setActive(activePaths.has(idx)) |
|
53 |
}, [activePaths, idx]) |
|
47 | 54 |
|
48 | 55 |
const getActiveMapPoints = () => mapPoints.filter((item) => item.active) |
49 | 56 |
|
57 |
const dispatch = useDispatch() |
|
58 |
|
|
50 | 59 |
// Builds all edges of the path |
51 | 60 |
const buildEdges = () => { |
52 | 61 |
const activeMapPoints = getActiveMapPoints() |
... | ... | |
73 | 82 |
attributes={{ |
74 | 83 |
'font-size': 25, |
75 | 84 |
// Set to primaryPathColor if primary index in the tracking tool is equal to this index |
76 |
fill: primaryIdx === idx ? primaryPathColor : secondaryPathColor, |
|
85 |
fill: |
|
86 |
primaryPathIdx === idx |
|
87 |
? primaryPathColor |
|
88 |
: secondaryPathColor, |
|
77 | 89 |
}} |
78 | 90 |
onClick={() => { |
79 |
setPrimary(idx) |
|
80 |
console.log('hehehe') |
|
81 |
} |
|
82 |
} |
|
91 |
dispatch(setPrimaryIdx(idx)) |
|
92 |
}} |
|
83 | 93 |
repeat |
84 | 94 |
center |
85 | 95 |
weight={9} |
... | ... | |
111 | 121 |
fontWeight="bold" |
112 | 122 |
fontSize={16} |
113 | 123 |
> |
114 |
{formatHtmlStringToReactDom(mapPoint.catalogItem.name as string)} |
|
124 |
{formatHtmlStringToReactDom( |
|
125 |
mapPoint.catalogItem.name as string |
|
126 |
)} |
|
115 | 127 |
</Typography> |
116 | 128 |
<FormControlLabel |
117 | 129 |
control={ |
... | ... | |
126 | 138 |
labelPlacement="end" |
127 | 139 |
label="Active" |
128 | 140 |
/> |
129 |
<CatalogItemDetailDialog itemId={mapPoint.catalogItem.id ?? ''} /> |
|
141 |
<CatalogItemDetailDialog |
|
142 |
itemId={mapPoint.catalogItem.id ?? ''} |
|
143 |
/> |
|
130 | 144 |
</Stack> |
131 | 145 |
</Fragment> |
132 | 146 |
</Popup> |
... | ... | |
136 | 150 |
|
137 | 151 |
return ( |
138 | 152 |
<Fragment> |
139 |
{active && <Fragment> |
|
140 |
{buildVertices()} |
|
141 |
{buildEdges()} |
|
142 |
</Fragment>} |
|
153 |
{active && ( |
|
154 |
<Fragment> |
|
155 |
{buildVertices()} |
|
156 |
{buildEdges()} |
|
157 |
</Fragment> |
|
158 |
)} |
|
143 | 159 |
</Fragment> |
144 | 160 |
) |
145 | 161 |
} |
146 | 162 |
|
147 | 163 |
export default MapPath |
164 |
function setPrimaryIdx(idx: number): any { |
|
165 |
throw new Error('Function not implemented.') |
|
166 |
} |
frontend/src/features/TrackingTool/PlaintextUpload.tsx | ||
---|---|---|
7 | 7 |
TextField, |
8 | 8 |
} from '@mui/material' |
9 | 9 |
import { useFormik } from 'formik' |
10 |
import { Fragment, FunctionComponent, useState } from 'react' |
|
10 |
import { Fragment, FunctionComponent, useEffect, useState } from 'react'
|
|
11 | 11 |
import SendIcon from '@mui/icons-material/Send' |
12 | 12 |
import ClearIcon from '@mui/icons-material/Clear' |
13 | 13 |
import { PathDto } from '../../swagger/data-contracts' |
14 | 14 |
import axiosInstance from '../../api/api' |
15 |
import { useDispatch } from 'react-redux' |
|
15 |
import { useDispatch, useSelector } from 'react-redux'
|
|
16 | 16 |
import { showNotification } from '../Notification/notificationSlice' |
17 |
import { RootState } from '../redux/store' |
|
18 |
import { sendTextForProcessing } from './trackingToolThunks' |
|
19 |
import * as yup from 'yup' |
|
20 |
import { resetDialogApiCallSuccess } from './trackingToolSlice' |
|
17 | 21 |
|
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) |
|
22 |
const PlaintextUpload = () => { |
|
23 |
const loading = useSelector( |
|
24 |
(state: RootState) => state.trackingTool.isLoading |
|
25 |
) |
|
26 | 26 |
const [open, setOpen] = useState(false) // controls whether the dialog is open |
27 | 27 |
|
28 |
// This controls whether to keep dialog open after sending the request to the API |
|
29 |
const dialogApiCallSuccess = useSelector( |
|
30 |
(state: RootState) => state.trackingTool.dialogApiCallSuccess |
|
31 |
) |
|
32 |
|
|
28 | 33 |
const dispatch = useDispatch() |
29 | 34 |
|
35 |
const validationSchema = yup.object().shape({ |
|
36 |
text: yup.mixed().required('Text is required'), |
|
37 |
}) |
|
38 |
|
|
30 | 39 |
const formik = useFormik({ |
31 | 40 |
initialValues: { |
32 | 41 |
text: '', |
33 | 42 |
}, |
43 |
validationSchema, |
|
34 | 44 |
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 |
} |
|
60 |
} catch (err: any) { |
|
61 |
dispatch(showNotification) |
|
62 |
closeDialog = true |
|
63 |
} |
|
64 |
setSubmitButtonDisabled(false) |
|
65 |
|
|
66 |
if (closeDialog) { |
|
67 |
onCloseDialog() |
|
68 |
} |
|
45 |
// Dispatch the thunk |
|
46 |
dispatch(sendTextForProcessing(formik.values.text)) |
|
69 | 47 |
}, |
70 | 48 |
}) |
71 | 49 |
|
... | ... | |
78 | 56 |
formik.resetForm() |
79 | 57 |
} |
80 | 58 |
|
59 |
useEffect(() => { |
|
60 |
if (!dialogApiCallSuccess) { |
|
61 |
return |
|
62 |
} |
|
63 |
dispatch(resetDialogApiCallSuccess()) |
|
64 |
setOpen(false) |
|
65 |
}, [dialogApiCallSuccess, dispatch]) |
|
66 |
|
|
81 | 67 |
return ( |
82 | 68 |
<Fragment> |
83 | 69 |
<Button variant="contained" onClick={() => setOpen(true)}> |
... | ... | |
119 | 105 |
<Button |
120 | 106 |
type="submit" |
121 | 107 |
variant="contained" |
122 |
disabled={submitButtonDisabled} |
|
123 | 108 |
startIcon={<SendIcon />} |
109 |
disabled={loading} |
|
124 | 110 |
> |
125 | 111 |
Submit |
126 | 112 |
</Button> |
frontend/src/features/TrackingTool/ProcessedTextCard.tsx | ||
---|---|---|
1 |
import { PathDto } from "../../swagger/data-contracts" |
|
2 |
|
|
3 |
export interface ProcessedtextCardProps { |
|
4 |
pathDto?: PathDto |
|
5 |
|
|
6 |
} |
|
7 |
|
|
8 |
const ProcessedTextCard = () => { |
|
9 |
|
|
10 |
} |
|
11 |
|
|
12 |
export default ProcessedTextCard |
frontend/src/features/TrackingTool/TrackingTool.tsx | ||
---|---|---|
1 |
import { Card, CardContent, Grid, Stack, Typography } from '@mui/material' |
|
1 |
import { |
|
2 |
Button, |
|
3 |
Card, |
|
4 |
CardContent, |
|
5 |
Grid, |
|
6 |
Stack, |
|
7 |
Typography, |
|
8 |
} from '@mui/material' |
|
2 | 9 |
import { Fragment, useEffect, useState } from 'react' |
3 |
import { MapContainer, TileLayer } from 'react-leaflet' |
|
10 |
import { MapContainer, TileLayer, useMap } from 'react-leaflet'
|
|
4 | 11 |
import mapConfig from '../../config/mapConfig' |
5 | 12 |
import TextPath from 'react-leaflet-textpath' |
6 | 13 |
import PlaintextUpload from './PlaintextUpload' |
... | ... | |
9 | 16 |
import DeleteIcon from '@mui/icons-material/Delete' |
10 | 17 |
import { PathDto } from '../../swagger/data-contracts' |
11 | 18 |
import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils' |
12 |
import buildPathVariants, { PathVariant } from './buildPathVariants' |
|
13 | 19 |
import MapPath from './MapPath' |
20 |
import EditIcon from '@mui/icons-material/Edit' |
|
21 |
import { useSelector } from 'react-redux' |
|
22 |
import { RootState } from '../redux/store' |
|
14 | 23 |
|
15 | 24 |
// Page with tracking tool |
16 | 25 |
const TrackingTool = () => { |
17 | 26 |
// Path response from the API |
18 |
const [pathDto, setPathDto] = useState<PathDto | undefined>(undefined) |
|
27 |
const pathDto = useSelector((state: RootState) => state.trackingTool.pathDto) |
|
28 |
const pathVariants = useSelector((state: RootState) => state.trackingTool.pathVariants) |
|
29 |
const mapCenter = useSelector((state: RootState) => state.trackingTool.mapCenter) |
|
19 | 30 |
|
20 |
// Whether the path variants are building |
|
21 |
const [buildingPathVariants, setBuildingPathVariants] = |
|
22 |
useState<boolean>(false) |
|
31 |
// const map = useMap() |
|
23 | 32 |
|
24 |
// List of all computed path variants |
|
25 |
const [pathVariants, setPathVariants] = useState<PathVariant[] | undefined>( |
|
26 |
undefined |
|
27 |
) |
|
28 |
|
|
29 |
// latitude longitude pair of where the map is to be centered |
|
30 |
const [mapCenter, setMapCenter] = useState<number[]>([ |
|
31 |
mapConfig.defaultCoordinates[0], |
|
32 |
mapConfig.defaultCoordinates[1], |
|
33 |
]) |
|
34 |
|
|
35 |
const [primaryPathIdx, setActivePathIdx] = useState(0) |
|
36 |
|
|
37 |
// TODO make some sort of selection list to select active paths? |
|
38 |
const [activePaths, setActivePaths] = useState<Set<number>>(new Set()) |
|
33 |
// // Set the map center |
|
34 |
// useEffect(() => { |
|
35 |
// map.flyTo(mapCenter, mapConfig.defaultZoom) |
|
36 |
// }, [map, mapCenter]) |
|
39 | 37 |
|
40 |
// Returns tuple of average latitude and longitude |
|
41 |
const calculateMapCenter = (pathVariant: PathVariant) => [ |
|
42 |
pathVariant |
|
43 |
.map((item) => item.latitude ?? 0) |
|
44 |
.reduce((a, b) => a + b, 0) / pathVariant.length, |
|
45 |
pathVariant |
|
46 |
.map((item) => item.longitude ?? 0) |
|
47 |
.reduce((a, b) => a + b, 0) / pathVariant.length, |
|
48 |
] |
|
49 |
|
|
50 |
useEffect(() => { |
|
51 |
if (!pathVariants || pathVariants.length === 0) { |
|
52 |
return |
|
53 |
} |
|
54 |
// TODO calculate only for path that has some non-null coordinates |
|
55 |
setMapCenter(calculateMapCenter(pathVariants[0])) |
|
56 |
}, [pathVariants]) |
|
57 |
|
|
58 |
useEffect(() => { |
|
59 |
if (!pathDto) { |
|
60 |
return |
|
61 |
} |
|
62 |
|
|
63 |
setBuildingPathVariants(true) |
|
64 |
buildPathVariants(pathDto).then((pathVariants) => { |
|
65 |
setActivePaths(new Set(pathVariants.map((_, idx) => idx))) |
|
66 |
setPathVariants(pathVariants) |
|
67 |
setBuildingPathVariants(false) |
|
68 |
}) |
|
69 |
}, [pathDto]) |
|
70 | 38 |
|
71 | 39 |
return ( |
72 | 40 |
<Fragment> |
... | ... | |
92 | 60 |
)} |
93 | 61 |
</Typography> |
94 | 62 |
</Stack> |
63 |
<Stack justifyItems="flex-end" alignSelf="flex-end" alignItems="flex-end"> |
|
64 |
<Button size="small" variant="outlined" startIcon={<EditIcon />}>Edit</Button> |
|
65 |
</Stack> |
|
95 | 66 |
</CardContent> |
96 | 67 |
</Card> |
97 | 68 |
</Fragment> |
... | ... | |
123 | 94 |
> |
124 | 95 |
{pathDto ? 'Update path' : 'Show Path'} |
125 | 96 |
</Typography> |
126 |
<PlaintextUpload setPaths={setPathDto} />
|
|
97 |
<PlaintextUpload /> |
|
127 | 98 |
<FileUpload /> |
128 | 99 |
</Stack> |
129 | 100 |
</Grid> |
... | ... | |
153 | 124 |
key={idx} |
154 | 125 |
pathVariant={pathVariant} |
155 | 126 |
idx={idx} |
156 |
setPrimary={setActivePathIdx} |
|
157 |
primaryIdx={primaryPathIdx} |
|
158 |
active={activePaths.has(idx)} |
|
159 | 127 |
/> |
160 | 128 |
))} |
161 | 129 |
</MapContainer> |
frontend/src/features/TrackingTool/buildPathVariants.ts | ||
---|---|---|
14 | 14 |
[[]] |
15 | 15 |
) |
16 | 16 |
|
17 |
export const buildPathVariants = async (pathDto: PathDto): Promise<PathVariant[]> => {
|
|
17 |
export const buildPathVariants = (pathDto: PathDto): PathVariant[] => {
|
|
18 | 18 |
if (!pathDto.foundCatalogItems) { |
19 | 19 |
return [] |
20 | 20 |
} |
frontend/src/features/TrackingTool/trackingToolSlice.ts | ||
---|---|---|
1 |
import { createSlice } from '@reduxjs/toolkit' |
|
2 |
import { LatLngTuple } from 'leaflet' |
|
3 |
import mapConfig from '../../config/mapConfig' |
|
4 |
import { PathDto } from '../../swagger/data-contracts' |
|
5 |
import buildPathVariants, { PathVariant } from './buildPathVariants' |
|
6 |
import { sendTextForProcessing } from './trackingToolThunks' |
|
7 |
|
|
8 |
export interface TrackingToolState { |
|
9 |
isLoading: boolean // whether the data is being loaded |
|
10 |
pathDto?: PathDto // the data |
|
11 |
pathVariants?: PathVariant[] // undefined signals that no path variants were yet fetched from the API |
|
12 |
lastErr?: string // consumable for errors during thunks |
|
13 |
mapCenter: LatLngTuple // pair of latitude and longitude |
|
14 |
primaryPathIdx: number // index of the primary path |
|
15 |
activePaths: Set<number> // indices of the active paths |
|
16 |
// trigger to close the dialog when API call is finished |
|
17 |
dialogApiCallSuccess: boolean |
|
18 |
} |
|
19 |
|
|
20 |
const initialState: TrackingToolState = { |
|
21 |
isLoading: false, |
|
22 |
mapCenter: [ |
|
23 |
mapConfig.defaultCoordinates[0], |
|
24 |
mapConfig.defaultCoordinates[1], |
|
25 |
], |
|
26 |
primaryPathIdx: 0, |
|
27 |
activePaths: new Set(), |
|
28 |
dialogApiCallSuccess: true, |
|
29 |
} |
|
30 |
|
|
31 |
// Returns tuple of average latitude and longitude |
|
32 |
const calculateMapCenter = (pathVariant: PathVariant): LatLngTuple => [ |
|
33 |
pathVariant.map((item) => item.latitude ?? 0).reduce((a, b) => a + b, 0) / |
|
34 |
pathVariant.length, |
|
35 |
pathVariant.map((item) => item.longitude ?? 0).reduce((a, b) => a + b, 0) / |
|
36 |
pathVariant.length, |
|
37 |
] |
|
38 |
|
|
39 |
export const trackingToolSlice = createSlice({ |
|
40 |
name: 'trackingTool', |
|
41 |
initialState, |
|
42 |
reducers: { |
|
43 |
consumeErr: (state) => ({ ...state, lastErr: undefined }), |
|
44 |
setPrimaryIdx: (state, action) => ({ |
|
45 |
...state, |
|
46 |
primaryPathIdx: action.payload, |
|
47 |
}), |
|
48 |
resetDialogApiCallSuccess: (state) => ({ |
|
49 |
...state, |
|
50 |
dialogApiCallSuccess: false, |
|
51 |
}), |
|
52 |
}, |
|
53 |
extraReducers: (builder) => { |
|
54 |
builder.addCase(sendTextForProcessing.fulfilled, (state, action) => { |
|
55 |
const dto: PathDto = action.payload |
|
56 |
const pathVariants = buildPathVariants(dto) |
|
57 |
return { |
|
58 |
...state, |
|
59 |
pathVariants, |
|
60 |
// TODO map this correctly |
|
61 |
activePaths: new Set(pathVariants.map((_, idx) => idx)), |
|
62 |
// TODO calculate correctly |
|
63 |
mapCenter: |
|
64 |
pathVariants.length > 0 |
|
65 |
? calculateMapCenter(pathVariants[0]) |
|
66 |
: (state.mapCenter as LatLngTuple), |
|
67 |
isLoading: false, |
|
68 |
dialogApiCallSuccess: true, |
|
69 |
} |
|
70 |
}) |
|
71 |
builder.addCase(sendTextForProcessing.rejected, (state, action) => ({ |
|
72 |
...initialState, |
|
73 |
lastErr: action.error.message, |
|
74 |
isLoading: false, |
|
75 |
dialogApiCallSuccess: false, |
|
76 |
})) |
|
77 |
builder.addCase(sendTextForProcessing.pending, (state) => { |
|
78 |
return { |
|
79 |
...state, |
|
80 |
isLoading: true, |
|
81 |
dialogApiCallSuccess: false, |
|
82 |
} |
|
83 |
}) |
|
84 |
}, |
|
85 |
}) |
|
86 |
|
|
87 |
export const { consumeErr, setPrimaryIdx, resetDialogApiCallSuccess } = trackingToolSlice.actions |
|
88 |
const trackingToolReducer = trackingToolSlice.reducer |
|
89 |
export default trackingToolReducer |
frontend/src/features/TrackingTool/trackingToolThunks.ts | ||
---|---|---|
1 |
import { createAsyncThunk } from '@reduxjs/toolkit' |
|
2 |
import axiosInstance from '../../api/api' |
|
3 |
|
|
4 |
export const sendTextForProcessing = createAsyncThunk( |
|
5 |
'trackingTool/sendTextForProcessing', |
|
6 |
async (text: string) => { |
|
7 |
try { |
|
8 |
const { data, status } = await axiosInstance.post('path', { text }) |
|
9 |
if (status !== 200) { |
|
10 |
return Promise.reject( |
|
11 |
'Error while fetching map, please try again later' |
|
12 |
) |
|
13 |
} |
|
14 |
return data |
|
15 |
} catch (err: any) { |
|
16 |
return Promise.reject('Error, server is currently unavailable') |
|
17 |
} |
|
18 |
} |
|
19 |
) |
frontend/src/features/redux/store.ts | ||
---|---|---|
6 | 6 |
import catalogReducer from '../Catalog/catalogSlice' |
7 | 7 |
import { composeWithDevTools } from 'redux-devtools-extension' |
8 | 8 |
import notificationReducer from '../Notification/notificationSlice' |
9 |
import trackingToolReducer from '../TrackingTool/trackingToolSlice' |
|
10 |
import { enableMapSet } from 'immer' |
|
11 |
|
|
12 |
enableMapSet() |
|
9 | 13 |
|
10 | 14 |
const composeEnhancers = composeWithDevTools({}) |
11 | 15 |
|
... | ... | |
15 | 19 |
user: userReducer, |
16 | 20 |
theme: themeReducer, |
17 | 21 |
catalog: catalogReducer, |
18 |
notification: notificationReducer |
|
22 |
notification: notificationReducer, |
|
23 |
trackingTool: trackingToolReducer |
|
19 | 24 |
}), |
20 | 25 |
process.env.REACT_APP_DEV_ENV === 'true' |
21 | 26 |
? composeEnhancers( // ComposeEnhancers will inject redux-devtools-extension |
Také k dispozici: Unified diff
refactor to use Redux instead of localstate
re #9629