Projekt

Obecné

Profil

« Předchozí | Další » 

Revize ed410564

Přidáno uživatelem Michal Schwob před asi 2 roky(ů)

Administation page finished
re #9818

Zobrazit rozdíly:

backend/docker-compose.yml
19 19
  app: # Spring boot application
20 20
    build: .
21 21
    container_name: app-backend  # name of the container
22
    image: schwobik/backend-app:1.7
22
    image: schwobik/backend-app:1.8
23 23
    ports:
24 24
      - "8080:8080"                 # expose port 8080 out of the docker container do the local machine
25 25
    depends_on:
......
31 31
#     Since our Dockerfile for web-server is located in react-app folder, our build context is ./react-app
32 32
    build: ../frontend
33 33
    container_name: frontend
34
    image: schwobik/frontend-app:1.7
34
    image: schwobik/frontend-app:1.8
35 35
    ports:
36 36
      - "80:80"
37 37

  
backend/src/main/java/cz/zcu/kiv/backendapi/config/CorsConfig.java
14 14
    public void addCorsMappings(CorsRegistry registry) {
15 15
        registry.addMapping("/**")
16 16
                .allowedOrigins("*")
17
                .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS");
17
                .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS", "PATCH");
18 18
    }
19 19
}
backend/src/main/java/cz/zcu/kiv/backendapi/security/SecurityConfig.java
21 21
import org.springframework.web.cors.CorsConfigurationSource;
22 22
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
23 23

  
24
import java.util.Arrays;
25
import java.util.Collections;
24 26
import java.util.HashMap;
25 27
import java.util.Map;
26 28

  
......
124 126
    @Bean
125 127
    CorsConfigurationSource corsConfigurationSource() {
126 128
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
127
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
129
        CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues();
130
        corsConfiguration.setAllowedMethods(java.util.List.of(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name(), HttpMethod.PUT.name()));
131
        source.registerCorsConfiguration("/**", corsConfiguration);
128 132
        return source;
129 133
    }
130 134
}
frontend/src/features/Administration/Administration.tsx
19 19
import {
20 20
    clear,
21 21
    consumeError,
22
    resetIsRequestCompleted,
22 23
    setLoading,
23 24
    setSelectedUser
24 25
} from "./userDetailSlice"
......
34 35
    const loading = useSelector((state: RootState) => state.usersDetail.loading)
35 36
    const apiError = useSelector((state: RootState) => state.usersDetail.error)
36 37
    const selectedUser = useSelector((state: RootState) => state.usersDetail.selectedUser)
38
    const isRequestCompleted = useSelector((state: RootState) => state.usersDetail.isRequestCompleted)
37 39
    const [selectedUserId, setSelectedUserId] = useState<number | undefined>(undefined)
38 40

  
39 41
    // Local state to display any error relevant error
......
59 61
    // Use effect to read the error and consume it
60 62
    useEffect(() => {
61 63
        if (users.length > 0) {
62
            setSelectedUserId(0)
63
            dispatch(setSelectedUser(users.at(0)))
64
            console.log("selected id: " + selectedUser)
64
            // if there is user selected, leave that state - handled by useEffect on isRequestCompleted
65
            if (selectedUser !== undefined) {
66
                const selUser = users.find(u => u.email === selectedUser.email)
67
                if (selUser !== undefined) {
68
                    console.log("Selected from before")
69
                    console.log(selUser)
70
                    setSelectedUserId(users.findIndex(u => u.email === selectedUser.email))
71
                    dispatch(setSelectedUser(selUser))
72
                    return
73
                }
74
            } else {
75
                setSelectedUserId(0)
76
                dispatch(setSelectedUser(users.at(0)))
77
            }
65 78
        }
66
    }, [users])
79
    }, [users, dispatch])
80

  
81
    useEffect(() => {
82
        if (isRequestCompleted) {
83
            dispatch(resetIsRequestCompleted())
84
            // dispatch(clear())
85
            dispatch(fetchUsers())
86
            // setSelectedUserId(0)
87
            // dispatch(setSelectedUser(users.at(0)))
88
            //
89
            // if (users.length > 0) {
90
            //     setSelectedUserId(0)
91
            //     dispatch(setSelectedUser(users.at(0)))
92
            //     console.log("selected id: " + selectedUser)
93
            // }
94
        }
95
    }, [isRequestCompleted, dispatch])
67 96

  
68 97

  
69 98
    // Use effect to read the error and consume it
......
87 116
        }
88 117
    }, [dispatch])
89 118

  
90
    const childClosed = () => {
91
        dispatch(clear())
92
        dispatch(fetchUsers())
93
        setSelectedUserId(0)
94
        dispatch(setSelectedUser(users.at(0)))
95
    }
96

  
97 119
    return (
98 120
        <Fragment>
99 121
            <ShowErrorIfPresent err={displayError} />
......
129 151
                    </Grid>
130 152
                    <Grid item md={9} xs={6}>
131 153
                        {selectedUserId !== undefined  ?
132
                            <UserDetail user={selectedUser as UserDto} onClose={childClosed} />
154
                            <UserDetail user={selectedUser as UserDto}/>
133 155
                        : null }
134 156
                    </Grid>
135 157
                </Grid>
frontend/src/features/Administration/UserDetail.tsx
5 5
    Paper,
6 6
    Typography,
7 7
    FormGroup,
8
    FormControlLabel, Container
8
    FormControlLabel
9 9
} from '@mui/material'
10 10
import React, {Fragment, useEffect, useState} from 'react'
11 11
import axiosInstance from "../../api/api"
......
14 14
import ClearIcon from "@mui/icons-material/Clear"
15 15
import CheckIcon from '@mui/icons-material/Check';
16 16
import {useDispatch, useSelector} from "react-redux"
17
import {fetchUsers, savePermissions} from "./userDetailThunks"
17
import {deleteUser, savePermissions} from "./userDetailThunks"
18 18
import {RootState} from "../redux/store"
19 19
import {setSelectedUserPermissions} from "./userDetailSlice"
20 20

  
21 21
export interface UserDetailProps {
22
    user: UserDto,
23
    onClose: () => void
22
    user: UserDto
24 23
}
25 24

  
26 25
const UserDetail = (props: UserDetailProps) => {
27
    const selectedUser = useSelector((state: RootState) => state.usersDetail.selectedUser)
26
    const selectedUser: UserDto | undefined = useSelector((state: RootState) => state.usersDetail.selectedUser)
28 27
    const [canRead, setCanRead] = useState(selectedUser?.permissions?.canRead)
29 28
    const [canWrite, setCanWrite] = useState(selectedUser?.permissions?.canWrite)
30 29
    const [canDelete, setCanDelete] = useState(selectedUser?.permissions?.canDelete)
......
69 68
    }
70 69

  
71 70

  
72
    const deleteUser = async () => {
73
        const { data, status } = await axiosInstance.delete(
74
            `/users/${selectedUser?.email}`
75
        )
76
        if (status !== 200) {
77
            // TODO dodělat zpracování erroru
78
            return
79
        }
80
        props.onClose();
71
    const prepareDeleteUser = async () => {
72
        dispatch(deleteUser())
81 73
    }
82 74

  
83 75
    const prepareSavePermissions = () => {
84 76
        dispatch(setSelectedUserPermissions({canRead: canRead, canWrite: canWrite, canDelete: canDelete}))
85 77
        dispatch(savePermissions())
86
        dispatch(fetchUsers())
87 78
    }
88 79

  
89 80
    return (
......
139 130
                <Button startIcon={<ClearIcon />}
140 131
                        variant="contained"
141 132
                        color="primary"
142
                        onClick={deleteUser}
133
                        onClick={prepareDeleteUser}
143 134
                        sx={{ m: 2 }} >
144 135
                    Delete
145 136
                </Button>
frontend/src/features/Administration/userDetailSlice.tsx
1 1
import { createSlice } from '@reduxjs/toolkit'
2 2
import {PermissionDto, UserDto} from '../../swagger/data-contracts'
3
import {fetchUsers} from './userDetailThunks'
3
import {deleteUser, fetchUsers, savePermissions} from './userDetailThunks'
4 4
import {number} from "yup"
5 5

  
6 6
export interface UsersDetailState {
7 7
    users: UserDto[] // list of all fetched items
8 8
    loading: boolean // whether the users are loading
9
    error?: string,
10
    selectedUser?: UserDto,
9
    error?: string
10
    selectedUser?: UserDto
11 11
    permissions?: PermissionDto
12
    isRequestCompleted: boolean
12 13
}
13 14

  
14 15
const initialState: UsersDetailState = {
......
16 17
    loading: true,
17 18
    error: undefined,
18 19
    selectedUser: undefined,
19
    permissions: undefined
20
    permissions: undefined,
21
    isRequestCompleted: false
20 22
}
21 23

  
22 24
const usersDetailSlice = createSlice({
......
34 36
            ...state,
35 37
            permissions: action.payload
36 38
        }),
39
        resetIsRequestCompleted: (state) => ({
40
            ...state,
41
            isRequestCompleted: false
42
        }),
37 43
    },
38 44
    extraReducers: (builder) => {
39 45
        builder.addCase(fetchUsers.pending, (state) => ({
......
50 56
            loading: false,
51 57
            error: action.error.message as string,
52 58
        }))
59
        builder.addCase(savePermissions.fulfilled, (state, action) => ({
60
            ...state,
61
            isRequestCompleted: true,
62
        }))
63
        builder.addCase(deleteUser.fulfilled, (state, action) => ({
64
            ...state,
65
            isRequestCompleted: true,
66
        }))
53 67
    },
54 68
})
55 69

  
......
59 73
    consumeError,
60 74
    setSelectedUser,
61 75
    setSelectedUserPermissions,
76
    resetIsRequestCompleted
62 77
} = usersDetailSlice.actions
63 78
const usersDetailReducer = usersDetailSlice.reducer
64 79
export default usersDetailReducer
frontend/src/features/Administration/userDetailThunks.tsx
59 59
        }
60 60
    }
61 61
)
62

  
63
export const deleteUser = createAsyncThunk(
64
    'usersDetail/deleteUser',
65
    async (dispatch, { getState }) => {
66
        const { usersDetail } = getState() as { usersDetail: UsersDetailState }
67
        console.log("savePermissions called")
68

  
69
        const selectedUser = usersDetail.selectedUser
70

  
71
        if (selectedUser === undefined) {
72
            return Promise.reject("User is undefined")
73
        }
74
        const { data, status } = await axiosInstance.delete(
75
            `/users/${selectedUser?.email}`
76
        )
77
        if (status !== 200) {
78
            // TODO dodělat zpracování erroru
79
            return Promise.reject("Deleting user failed")
80
        }
81
        return status
82
    }
83
)

Také k dispozici: Unified diff