commit 4a2ac9a870a3ddc77d66e03c00bd2bb2135418d8
Author: Vaclav Honzik <vaclavhonzik98@gmail.com>
Date:   Fri Apr 8 17:33:24 2022 +0200

    re #9369 item + catalog refactor

diff --git a/frontend/src/features/Catalog/CatalogItemDetail.tsx b/frontend/src/features/Catalog/CatalogItemDetail.tsx
index e4caaad..f732995 100644
--- a/frontend/src/features/Catalog/CatalogItemDetail.tsx
+++ b/frontend/src/features/Catalog/CatalogItemDetail.tsx
@@ -7,39 +7,46 @@ import {
     Stack,
     Typography,
 } from '@mui/material'
-import { Fragment, useEffect } from 'react'
-import { useDispatch, useSelector } from 'react-redux'
+import { Fragment, useEffect, useState } from 'react'
 import { useParams } from 'react-router-dom'
-import { RootState } from '../redux/store'
-import ConsumeLastError from '../Reusables/ConsumeLastError'
+import axiosInstance from '../../api/api'
+import { CatalogItemDto } from '../../swagger/data-contracts'
+import ShowErrorIfPresent from '../Reusables/ShowErrorIfPresent'
 import ContentLoading from '../Reusables/ContentLoading'
-import { consumeCatalogItemLastErr, setItemLoading } from './catalogSlice'
-import { getCatalogItem } from './catalogThunks'
+
+const apiError =
+    'Error while fetching data from the server, please try again later.'
 
 const CatalogItemDetail = () => {
     // itemId from query params
     const { itemId } = useParams()
 
-    // Loaded item
-    const item = useSelector((state: RootState) => state.catalog.catalogItem)
-
-    // Whether the item is loading
-    const isItemLoading = useSelector(
-        (state: RootState) => state.catalog.isItemLoading
-    )
-
-    // Redux dispatch
-    const dispatch = useDispatch()
-
-    const lastErr = useSelector(
-        (state: RootState) => state.catalog.catalogItemLastErr
-    )
+    const [item, setItem] = useState<CatalogItemDto | undefined>(undefined)
+    const [isItemLoading, setIsItemLoading] = useState(true)
+    const [err, setErr] = useState<string | undefined>(undefined)
 
     // Fetch the item from the api after mounting the component
     useEffect(() => {
-        dispatch(setItemLoading(true))
-        dispatch(getCatalogItem(itemId as string))
-    }, [dispatch, itemId])
+        // Function to fetch the item from the api
+        const fetchItem = async () => {
+            try {
+                const { data, status } = await axiosInstance.get(
+                    `/catalog-items/${itemId}`
+                )
+                if (status !== 200) {
+                    setErr(apiError)
+                    return
+                }
+
+                setItem(data)
+                setIsItemLoading(false)
+            } catch (err: any) {
+                setErr(apiError)
+            }
+        }
+
+        fetchItem()
+    }, [])
 
     // Maps catalogItem property to corresponding table row
     const mapToRow = (rowName: string, items: string[]) => (
@@ -96,12 +103,9 @@ const CatalogItemDetail = () => {
     return (
         // TODO remove min height
         <Paper style={{ minHeight: '100vh', borderRadius: 0 }} elevation={2}>
-            <ConsumeLastError
-                consumeFn={consumeCatalogItemLastErr}
-                err={lastErr}
-            />
+            <ShowErrorIfPresent err={err} />
 
-            {isItemLoading ? <ContentLoading /> : null}
+            {isItemLoading && !err ? <ContentLoading /> : null}
             {!isItemLoading && item ? (
                 <Grid container justifyContent="space-around">
                     <Grid item xs={6} sx={{ px: 2 }}>
diff --git a/frontend/src/features/Catalog/CatalogTable.tsx b/frontend/src/features/Catalog/CatalogTable.tsx
index 49e23a0..d1318bf 100644
--- a/frontend/src/features/Catalog/CatalogTable.tsx
+++ b/frontend/src/features/Catalog/CatalogTable.tsx
@@ -9,14 +9,14 @@ import {
     TableRow,
 } from '@mui/material'
 import { Fragment, useEffect, useState } from 'react'
-import { useDispatch, useSelector } from 'react-redux'
 import { Link as RouterLink } from 'react-router-dom'
 import { CatalogItemDto } from '../../swagger/data-contracts'
-import { RootState } from '../redux/store'
-import ConsumeLastError from '../Reusables/ConsumeLastError'
+import ShowErrorIfPresent from '../Reusables/ShowErrorIfPresent'
 import ContentLoading from '../Reusables/ContentLoading'
-import { consumeCatalogLastErr } from './catalogSlice'
-import { getCatalogItems } from './catalogThunks'
+import axiosInstance from '../../api/api'
+
+const apiError =
+    'Error while fetching data from the server, please try again later.'
 
 // Catalog table component
 const CatalogTable = () => {
@@ -28,17 +28,9 @@ const CatalogTable = () => {
         rowsPerPage[0]
     )
 
-    // Fetched items from the store
-    const items = useSelector((state: RootState) => state.catalog.catalogItems)
-    const areItemsLoading = useSelector(
-        (state: RootState) => state.catalog.isListLoading
-    )
-    const fetchError = useSelector(
-        (state: RootState) => state.catalog.catalogLastErr
-    )
-
-    // Redux dispatch
-    const dispatch = useDispatch()
+    const [items, setItems] = useState<CatalogItemDto[]>([])
+    const [areItemsLoading, setAreItemsLoading] = useState(true)
+    const [err, setErr] = useState<string | undefined>(undefined)
 
     // When changing rows per page set the selected number and reset to the first page
     const onRowsPerPageChange = (
@@ -50,8 +42,26 @@ const CatalogTable = () => {
 
     // Use effect hook to fetch rows from the server
     useEffect(() => {
-        dispatch(getCatalogItems())
-    }, [dispatch])
+        // Function to fetch items from the API
+        const fetchItems = async () => {
+            try {
+                const { data, status } = await axiosInstance.get(
+                    '/catalog-items'
+                )
+                if (status !== 200) {
+                    setErr(apiError)
+                    return
+                }
+
+                setItems(data)
+                setAreItemsLoading(false)
+            } catch (err: any) {
+                setErr(apiError)
+            }
+        }
+
+        fetchItems()
+    }, [])
 
     // Name of columns in the header
     const columns = [
@@ -73,7 +83,12 @@ const CatalogTable = () => {
         <Fragment>
             {/* {mapValueOrDefault(item.name)} */}
             <TableCell align="center">
-                <Link component={RouterLink} to={`/catalog/${item.id as string}`}>{item.name}</Link>
+                <Link
+                    component={RouterLink}
+                    to={`/catalog/${item.id as string}`}
+                >
+                    {item.name}
+                </Link>
             </TableCell>
             {mapValueOrDefault(item.alternativeNames?.join(', '))}
             {mapValueOrDefault(item.writtenForms?.join(', '))}
@@ -92,12 +107,9 @@ const CatalogTable = () => {
 
     return (
         <Fragment>
-            <ConsumeLastError
-                err={fetchError}
-                consumeFn={consumeCatalogLastErr}
-            />
-            {areItemsLoading && !fetchError ? <ContentLoading /> : null}
-            {!areItemsLoading && !fetchError ? (
+            <ShowErrorIfPresent err={err} />
+            {areItemsLoading && !err ? <ContentLoading /> : null}
+            {!areItemsLoading && !err ? (
                 <Fragment>
                     <TableContainer>
                         <Table
diff --git a/frontend/src/features/Catalog/catalogSlice.ts b/frontend/src/features/Catalog/catalogSlice.ts
deleted file mode 100644
index a1b0a73..0000000
--- a/frontend/src/features/Catalog/catalogSlice.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Action, createSlice } from '@reduxjs/toolkit'
-import { CatalogItemDto } from '../../swagger/data-contracts'
-import { getCatalogItem, getCatalogItems } from './catalogThunks'
-
-export interface CatalogState {
-    catalogItems: CatalogItemDto[] // Items shown in the table
-    isListLoading: boolean
-    catalogLastErr?: string // Error while fetching all catalog items
-    catalogItem?: CatalogItemDto // Item shown in detail
-    isItemLoading: boolean
-    catalogItemLastErr?: string // Error while fetching catalog item
-}
-
-const initialState: CatalogState = {
-    catalogItems: [],
-    isListLoading: true,
-    isItemLoading: true,
-}
-
-export const catalogSlice = createSlice({
-    name: 'catalog',
-    initialState,
-    reducers: {
-        setItemListLoading: (state, action) => ({
-            ...state,
-            isListLoading: action.payload,
-        }),
-        consumeCatalogLastErr: (state) => ({
-            ...state,
-            catalogLastErr: undefined,
-        }),
-        setItemLoading: (state, action) => ({
-            ...state,
-            isItemLoading: action.payload,
-        }),
-        consumeCatalogItemLastErr: (state) => ({
-            ...state,
-            catalogItemLastErr: undefined,
-        }),
-    },
-
-    extraReducers: (builder) => {
-        builder.addCase(getCatalogItems.fulfilled, (state, action) => ({
-            ...state,
-            catalogItems: action.payload,
-            isListLoading: false,
-        }))
-        builder.addCase(getCatalogItems.rejected, (state, action) => ({
-            ...state,
-            lastErr: action.payload as string,
-        }))
-        builder.addCase(getCatalogItem.fulfilled, (state, action) => ({
-            ...state,
-            catalogItem: action.payload,
-            isItemLoading: false,
-        }))
-        builder.addCase(getCatalogItem.rejected, (state, action) => ({
-            ...state,
-            lastErr: action.payload as string,
-        }))
-    },
-})
-
-export const {
-    setItemListLoading,
-    setItemLoading,
-    consumeCatalogItemLastErr,
-    consumeCatalogLastErr,
-} = catalogSlice.actions
-
-export const catalogReducer = catalogSlice.reducer
diff --git a/frontend/src/features/Catalog/catalogThunks.ts b/frontend/src/features/Catalog/catalogThunks.ts
deleted file mode 100644
index 66e0a61..0000000
--- a/frontend/src/features/Catalog/catalogThunks.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { createAsyncThunk } from '@reduxjs/toolkit'
-import axiosInstance from '../../api/api'
-
-const apiError =
-    'Error while fetching data from the server, please try again later.'
-
-export const getCatalogItems = createAsyncThunk(
-    'catalog/getCatalogItems',
-    async () => {
-        try {
-            const { data, status } = await axiosInstance.get('/catalog-items')
-            if (status !== 200) {
-                return Promise.reject(apiError)
-            }
-            return data
-        } catch (err: any) {
-            return Promise.reject(apiError)
-        }
-    }
-)
-
-export const getCatalogItem = createAsyncThunk(
-    'catalog/getCatalogItem',
-    async (id: string) => {
-        try {
-            const { data, status } = await axiosInstance.get(
-                `/catalog-items/${id}`
-            )
-            if (status !== 200) {
-                return Promise.reject(apiError)
-            }
-            return data
-        } catch (err: any) {
-            return Promise.reject(apiError)
-        }
-    }
-)
diff --git a/frontend/src/features/Home/Home.tsx b/frontend/src/features/Home/Home.tsx
index 2bf3999..a4cf3c5 100644
--- a/frontend/src/features/Home/Home.tsx
+++ b/frontend/src/features/Home/Home.tsx
@@ -1,7 +1,6 @@
 import { Button } from '@mui/material'
 import { useDispatch, useSelector } from 'react-redux'
 import { logout } from '../Auth/userSlice'
-import CatalogItemDetail from '../Catalog/CatalogItemDetail'
 import { RootState } from '../redux/store'
 
 const Home = () => {
@@ -24,7 +23,6 @@ const Home = () => {
                     Logout
                 </Button>
             ) : null}
-            <CatalogItemDetail />
         </>
     )
 }
diff --git a/frontend/src/features/Reusables/ConsumeLastError.tsx b/frontend/src/features/Reusables/ConsumeLastError.tsx
deleted file mode 100644
index 7861e9b..0000000
--- a/frontend/src/features/Reusables/ConsumeLastError.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Typography } from '@mui/material'
-import { Fragment, FunctionComponent, useEffect, useState } from 'react'
-import { useDispatch } from 'react-redux'
-
-export interface ConsumeLastErrorProps {
-    // Observable error from the store
-    err?: string
-    // Consume function that clears the error in the store
-    consumeFn: () => void
-}
-
-// Utility component that consumes the last error from the store and shows it
-const ConsumeLastError: FunctionComponent<ConsumeLastErrorProps> = ({
-    err: error,
-    consumeFn,
-}) => {
-    const dispatch = useDispatch()
-    const [err, setErr] = useState('')
-
-    useEffect(() => {
-        if (error) {
-            setErr(`${error}`)
-            dispatch(consumeFn())
-        }
-    }, [consumeFn, dispatch, error])
-
-    return <Fragment>
-        {err ? <Typography variant="h6" fontWeight="400">{err}</Typography> : null}
-    </Fragment>
-}
-
-export default ConsumeLastError
diff --git a/frontend/src/features/Reusables/ShowErrorIfPresent.tsx b/frontend/src/features/Reusables/ShowErrorIfPresent.tsx
new file mode 100644
index 0000000..84b730b
--- /dev/null
+++ b/frontend/src/features/Reusables/ShowErrorIfPresent.tsx
@@ -0,0 +1,19 @@
+import { Typography } from '@mui/material'
+import { Fragment, FunctionComponent } from 'react'
+
+export interface ShowErrorProps {
+    err?: string
+}
+
+// Utility component for showing error messages
+const ShowErrorIfPresent: FunctionComponent<ShowErrorProps> = ({ err }) => (
+    <Fragment>
+        {err ? (
+            <Typography align="center" variant="h6" fontWeight="400">
+                {err}
+            </Typography>
+        ) : null}
+    </Fragment>
+)
+
+export default ShowErrorIfPresent
diff --git a/frontend/src/features/redux/store.ts b/frontend/src/features/redux/store.ts
index f825bf1..945ae90 100644
--- a/frontend/src/features/redux/store.ts
+++ b/frontend/src/features/redux/store.ts
@@ -3,13 +3,12 @@ import { applyMiddleware, combineReducers, createStore } from 'redux'
 import { persistStore } from 'redux-persist'
 import thunk from 'redux-thunk'
 import userReducer from '../Auth/userSlice'
-import { catalogReducer } from '../Catalog/catalogSlice'
 import themeReducer from '../Theme/themeReducer'
 
 
 // Store holds shared state in the application
 const store = createStore(
-    combineReducers({ user: userReducer, theme: themeReducer, catalog: catalogReducer }),
+    combineReducers({ user: userReducer, theme: themeReducer }),
     applyMiddleware(thunk) // Thunk middleware so we can async fetch data from the api
 )
 
