Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 394f2d16

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

catalog slice

re #9545

Zobrazit rozdíly:

frontend/.env
1
REACT_APP_API_BASE_URL=/api
1
REACT_APP_API_BASE_URL=/api
2
REACT_APP_DEV_ENV=true
frontend/src/config/conf.ts
1 1
const conf = {
2
    baseUrl: '/api'
2
    baseUrl:
3
        process.env.REACT_APP_DEV_ENV === 'true'
4
            ? 'http://localhost:8080'
5
            : '/api',
3 6
}
4 7

  
5
export default conf;
8
export default conf
frontend/src/features/Catalog/Catalog.tsx
49 49
                                            id="type"
50 50
                                            label="Type"
51 51
                                        />
52
                                        <TextField
53
                                            size="small"
54
                                            id="coordinates"
55
                                            label="Coordinates"
56
                                        />
57 52
                                    </Stack>
58 53
                                    <Stack direction="row" spacing={2}>
59 54
                                        <TextField
frontend/src/features/Catalog/CatalogTable.tsx
13 13
import { CatalogItemDto } from '../../swagger/data-contracts'
14 14
import ShowErrorIfPresent from '../Reusables/ShowErrorIfPresent'
15 15
import ContentLoading from '../Reusables/ContentLoading'
16
import axiosInstance from '../../api/api'
17

  
18
const apiError =
19
    'Error while fetching data from the server, please try again later.'
16
import { RootState } from '../redux/store'
17
import { useDispatch, useSelector } from 'react-redux'
18
import { consumeError, setLoading } from './catalogSlice'
19
import { fetchItems } from './catalogThunks'
20 20

  
21 21
// Catalog table component
22 22
const CatalogTable = () => {
......
28 28
        rowsPerPage[0]
29 29
    )
30 30

  
31
    const [items, setItems] = useState<CatalogItemDto[]>([])
32
    const [areItemsLoading, setAreItemsLoading] = useState(true)
33
    const [err, setErr] = useState<string | undefined>(undefined)
31
    // Subscribe to the store
32
    const items = useSelector((state: RootState) => state.catalog.items)
33
    const loading = useSelector((state: RootState) => state.catalog.loading)
34
    const apiError = useSelector((state: RootState) => state.catalog.error)
35

  
36
    const [displayError, setDisplayError] = useState<string | undefined>(undefined)
34 37

  
35 38
    // When changing rows per page set the selected number and reset to the first page
36 39
    const onRowsPerPageChange = (
......
40 43
        setPage(0)
41 44
    }
42 45

  
43
    // Use effect hook to fetch rows from the server
46
    const dispatch = useDispatch()
47

  
44 48
    useEffect(() => {
45
        // Function to fetch items from the API
46
        const fetchItems = async () => {
47
            try {
48
                const { data, status } = await axiosInstance.get(
49
                    '/catalog-items'
50
                )
51
                if (status !== 200) {
52
                    setErr(apiError)
53
                    return
54
                }
49
        // Fetch items when the component is mounted
50
        // This will automatically search whenever the filter changes
51
        dispatch(fetchItems())
55 52

  
56
                setItems(data)
57
                setAreItemsLoading(false)
58
            } catch (err: any) {
59
                setErr(apiError)
60
            }
53
        return () => {
54
            // Invalidate the state when unmounting so that the old list is not rerendered when the user returns to the page
55
            dispatch(setLoading())
56
        }
57
    }, [dispatch])
58

  
59
    // Use effect to read the error and consume it
60
    useEffect(() => {
61
        if (apiError) {
62
            setDisplayError(apiError)
63
            dispatch(consumeError())
61 64
        }
65
    }, [apiError, dispatch])
62 66

  
63
        fetchItems()
64
    }, [])
65 67

  
66 68
    // Name of columns in the header
67 69
    const columns = [
......
107 109

  
108 110
    return (
109 111
        <Fragment>
110
            <ShowErrorIfPresent err={err} />
111
            {areItemsLoading && !err ? <ContentLoading /> : null}
112
            {!areItemsLoading && !err ? (
113
                <Fragment>
112
            <ShowErrorIfPresent err={displayError} />
113
            {loading && !displayError ? <ContentLoading /> : null}
114
            {!loading && !displayError ? (
115
                 <Fragment>
114 116
                    <TableContainer>
115 117
                        <Table
116 118
                            stickyHeader
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
    loading: boolean // whether the catalog is loading
15
    error?: string
16
}
17

  
18
const initialState: CatalogState = {
19
    items: [],
20
    filter: {},
21
    loading: true,
22
    error: undefined,
23
}
24

  
25
const catalogSlice = createSlice({
26
    name: 'catalog',
27
    initialState,
28
    reducers: {
29
        setCatalogFilter: (state, action) => ({
30
            ...state,
31
            catalogFilter: action.payload.catalogFilter,
32
        }),
33
        clearFilter: (state, action) => ({
34
            ...state,
35
            loading: true,
36
            catalogFilter: {},
37
        }),
38
        clear: (state) => ({ ...initialState }),
39
        setLoading: (state) => ({ ...state, loading: true }),
40
        consumeError: (state) => ({ ...state, error: undefined }),
41
    },
42
    extraReducers: (builder) => {
43
        builder.addCase(fetchItems.pending, (state) => ({
44
            ...state,
45
            loading: true,
46
        }))
47
        builder.addCase(fetchItems.fulfilled, (state, action) => ({
48
            ...state,
49
            items: action.payload,
50
            loading: false,
51
        }))
52
        builder.addCase(fetchItems.rejected, (state, action) => ({
53
            ...state,
54
            loading: false,
55
            error: action.payload as string,
56
        }))
57
    },
58
})
59

  
60
export const { setCatalogFilter, clearFilter, clear, setLoading, consumeError } = catalogSlice.actions
61
const reducer = catalogSlice.reducer
62
export default reducer
frontend/src/features/Catalog/catalogThunks.tsx
1
import { createAsyncThunk } from '@reduxjs/toolkit'
2
import axiosInstance from '../../api/api'
3
import { CatalogFilter, CatalogState } from './catalogSlice'
4

  
5
const apiError = 'Error, server is currently unavailable.'
6

  
7
// Builds query string from the filter object
8
const buildQuery = (catalogFilter: CatalogFilter): string => {
9
    return Object.entries(catalogFilter).length === 0
10
        ? ''
11
        : `?${Object.entries(catalogFilter)
12
              .map(([key, value]) => `${key}=${value}`)
13
              .join('&')}`
14
}
15

  
16
// Thunk to fetch catalog items from the API
17
export const fetchItems = createAsyncThunk(
18
    'catalog/fetchItems',
19
    async (dispatch, { getState }) => {
20
        try {
21
            // To make typescript happy we fool it like this
22
            const { catalog } = getState() as { catalog: CatalogState }
23

  
24
            
25
            // Send request with the filter
26
            const { data, status } = await axiosInstance.get(
27
                `/catalog-items${buildQuery(catalog.filter)}`
28
            )
29

  
30
            // If the request was successful return the items
31
            if (status === 200) {
32
                return data
33
            }
34

  
35
            return Promise.reject(apiError)
36
        } catch (err: any) {
37
            return Promise.reject(apiError)
38
        }
39
    }
40
)
frontend/src/features/redux/store.ts
4 4
import thunk from 'redux-thunk'
5 5
import userReducer from '../Auth/userSlice'
6 6
import themeReducer from '../Theme/themeReducer'
7
import catalogReducer from '../Catalog/catalogSlice'
7 8

  
8 9

  
9 10
// Store holds shared state in the application
10 11
const store = createStore(
11
    combineReducers({ user: userReducer, theme: themeReducer }),
12
    combineReducers({ user: userReducer, theme: themeReducer, catalog: catalogReducer }),
12 13
    applyMiddleware(thunk) // Thunk middleware so we can async fetch data from the api
13 14
)
14 15

  
frontend/src/swagger/CatalogItems.ts
25 25
    this.request<CatalogItemDto, any>({
26 26
      path: `/catalog-items/${id}`,
27 27
      method: "GET",
28
      format: "json",
29 28
      ...params,
30 29
    });
31 30
  /**
frontend/src/swagger/Path.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
import { PathDto } from "./data-contracts";
13
import { HttpClient, RequestParams } from "./http-client";
14

  
15
export class Path<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
16
  /**
17
   * No description
18
   *
19
   * @tags path-controller
20
   * @name GetPath
21
   * @request GET:/path
22
   */
23
  getPath = (query: { pathDto: PathDto }, params: RequestParams = {}) =>
24
    this.request<PathDto, any>({
25
      path: `/path`,
26
      method: "GET",
27
      query: query,
28
      ...params,
29
    });
30
}
frontend/src/swagger/data-contracts.ts
13 13
  /** @format uuid */
14 14
  id?: string;
15 15
  name?: string;
16

  
17
  /** @format int32 */
18
  certainty?: number;
16
  alternativeNames?: string[];
17
  writtenForms?: string[];
18
  types?: string[];
19
  countries?: string[];
20
  bibliography?: string[];
19 21

  
20 22
  /** @format double */
21 23
  longitude?: number;
22 24

  
23 25
  /** @format double */
24 26
  latitude?: number;
25
  bibliography?: string[];
26
  countries?: string[];
27
  writtenForms?: string[];
28
  alternativeNames?: string[];
29
  types?: string[];
27

  
28
  /** @format int32 */
29
  certainty?: number;
30
  description?: string;
30 31
}
31 32

  
32 33
export interface PasswordDto {
......
51 52
  title?: string;
52 53
  content?: string;
53 54
}
55

  
56
export interface PathDto {
57
  text?: string;
58
  foundCatalogItems?: CatalogItemDto[][];
59
}
frontend/src/swagger/http-client.ts
54 54
}
55 55

  
56 56
export class HttpClient<SecurityDataType = unknown> {
57
  public baseUrl: string = "/api";
57
  public baseUrl: string = "http://localhost:8080";
58 58
  private securityData: SecurityDataType | null = null;
59 59
  private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
60 60
  private abortControllers = new Map<CancelToken, AbortController>();

Také k dispozici: Unified diff