mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Make initial version of userpage; remove deprecated user routes, services and api calls (replaced by person queries)
This commit is contained in:
@@ -26,7 +26,16 @@ router.post('/login', async (req, res) => {
|
||||
expiresIn: LOGIN_VALID_MS,
|
||||
})
|
||||
|
||||
return res.status(200).send({ token, username: result.user_name, role: getRole(result.now_user_group ?? '') })
|
||||
const personResult = await nowDb.com_people.findFirst({ where: { user_id: result.user_id } })
|
||||
|
||||
if (!personResult) throw new Error('User found but not person; this should not happen.')
|
||||
|
||||
return res.status(200).send({
|
||||
token,
|
||||
username: result.user_name,
|
||||
role: getRole(result.now_user_group ?? ''),
|
||||
initials: personResult.initials,
|
||||
})
|
||||
})
|
||||
|
||||
router.get('/all', async (_req, res) => {
|
||||
|
||||
@@ -70,19 +70,3 @@ export const createTestUsers = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getAllUsers = async () => {
|
||||
const result = await nowDb.com_users.findMany({})
|
||||
return result
|
||||
}
|
||||
|
||||
export const getUserDetails = async (id: number) => {
|
||||
// TODO: Check if user has access
|
||||
|
||||
const result = await nowDb.com_users.findUnique({
|
||||
where: {
|
||||
user_id: id,
|
||||
},
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
timeBoundPage,
|
||||
timeUnitPage,
|
||||
} from './components/pages'
|
||||
import { UserPage } from './components/UserPage'
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
@@ -34,8 +35,9 @@ const App = () => {
|
||||
<Route element={regionPage} path="/region/:id?" />
|
||||
<Route element={personPage} path="/person/:id?" />
|
||||
<Route element={projectPage} path="/project/:id?" />
|
||||
<Route element={<FrontPage />} path="/" />
|
||||
<Route element={<Login />} path="/login" />
|
||||
<Route element={<UserPage />} path="/userpage" />
|
||||
<Route element={<FrontPage />} path="/" />
|
||||
<Route element={<div>Page not found.</div>} path="*" />
|
||||
</Routes>
|
||||
</Grid>
|
||||
|
||||
@@ -34,7 +34,7 @@ export const Login = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.token) {
|
||||
dispatch(setUser({ username: data.username, token: data.token, role: data.role || Role.ReadOnly }))
|
||||
dispatch(setUser(data))
|
||||
// Reset api cache, so that we won't show guest data to logged user
|
||||
dispatch(api.util.resetApiState())
|
||||
navigate('/')
|
||||
|
||||
@@ -42,6 +42,8 @@ export const NavBar = () => {
|
||||
},
|
||||
]
|
||||
|
||||
if (user.username) pages.push({ title: 'User', url: '/userpage' })
|
||||
|
||||
const logout = () => {
|
||||
dispatch(clearUser())
|
||||
// Reset api, so that we won't show cached private data to guest user
|
||||
@@ -52,9 +54,7 @@ export const NavBar = () => {
|
||||
|
||||
const logoutButton = (
|
||||
<MenuItem onClick={logout} style={{ display: 'inline-block' }}>
|
||||
<Typography component="h4" textAlign="center">
|
||||
Logout
|
||||
</Typography>
|
||||
<Typography textAlign="center">Logout</Typography>
|
||||
</MenuItem>
|
||||
)
|
||||
|
||||
@@ -70,9 +70,7 @@ export const NavBar = () => {
|
||||
onClick={handleClick}
|
||||
sx={{ color: 'white', paddingTop: '6px', paddingBottom: '6px', paddingLeft: '16px', paddingRight: '16px' }}
|
||||
>
|
||||
<Typography component="h4" textAlign="center">
|
||||
{link.title}
|
||||
</Typography>
|
||||
<Typography textAlign="center">{link.title}</Typography>
|
||||
</Box>
|
||||
<Menu
|
||||
id={`${link.url}-menu`}
|
||||
@@ -102,9 +100,7 @@ export const NavBar = () => {
|
||||
}
|
||||
return (
|
||||
<MenuItem key={`${link.url}-menuitem`} component={Link} to={link.url} style={{ display: 'inline-block' }}>
|
||||
<Typography component="h4" textAlign="center">
|
||||
{link.title}
|
||||
</Typography>
|
||||
<Typography textAlign="center">{link.title}</Typography>
|
||||
</MenuItem>
|
||||
)
|
||||
}
|
||||
@@ -118,9 +114,7 @@ export const NavBar = () => {
|
||||
<AppBar position="static">
|
||||
<Stack spacing={10} direction="row" marginLeft={2} justifyContent="space-between">
|
||||
<MenuItem component={Link} to="/">
|
||||
<Typography component="h1" fontSize={'2.2em'}>
|
||||
Now Database {getModeText()}
|
||||
</Typography>
|
||||
<Typography fontSize={'2.2em'}>Now Database {getModeText()}</Typography>
|
||||
</MenuItem>
|
||||
<MenuList style={{ alignContent: 'center' }}>
|
||||
{pages
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useUser } from '@/hooks/user'
|
||||
import { useGetPersonDetailsQuery } from '@/redux/personReducer'
|
||||
import { Box, CircularProgress, Divider, Stack, Typography } from '@mui/material'
|
||||
import { skipToken } from '@reduxjs/toolkit/query'
|
||||
import { useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export const UserPage = () => {
|
||||
const user = useUser()
|
||||
const navigate = useNavigate()
|
||||
const { data: userDetails, isError } = useGetPersonDetailsQuery(user?.initials ?? skipToken)
|
||||
|
||||
useEffect(() => {
|
||||
if (!user.username) navigate('/login')
|
||||
}, [user, navigate])
|
||||
|
||||
if (!userDetails) return <CircularProgress />
|
||||
if (isError || !userDetails.user) return <Box>Error fetching user</Box>
|
||||
return (
|
||||
<Box sx={{ maxWidth: '30em', marginLeft: 'auto', marginRight: 'auto' }}>
|
||||
<Typography variant="h4">User</Typography>
|
||||
<Divider sx={{ marginBottom: '1em' }} />
|
||||
<Stack gap={2}>
|
||||
<Box>
|
||||
<b>Username:</b> {user.username}
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<b>Initials:</b> {user.initials}
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<b>Last login:</b> {userDetails.user.last_login?.toString() || 'No data'}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { api } from '@/redux/api'
|
||||
import { PersonDetailsType } from '@/backendTypes'
|
||||
import { Role } from '@/types'
|
||||
|
||||
export type FrontendUser = {
|
||||
token: string
|
||||
username: string
|
||||
role: Role
|
||||
initials: string
|
||||
}
|
||||
|
||||
type LoginData = { username: string; password: string }
|
||||
@@ -23,16 +23,6 @@ const userApi = api.injectEndpoints({
|
||||
method: 'POST',
|
||||
}),
|
||||
}),
|
||||
getAllUsers: builder.query<PersonDetailsType[], void>({
|
||||
query: () => ({
|
||||
url: `/user/all`,
|
||||
}),
|
||||
}),
|
||||
getUserDetails: builder.query<PersonDetailsType, string>({
|
||||
query: id => ({
|
||||
url: `/user/${id}`,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -40,30 +30,29 @@ export interface UserState {
|
||||
token: string | null
|
||||
username: string | null
|
||||
role: Role
|
||||
initials: string | null
|
||||
}
|
||||
|
||||
const initialState: UserState = {
|
||||
token: null,
|
||||
username: null,
|
||||
role: Role.ReadOnly,
|
||||
initials: null,
|
||||
}
|
||||
|
||||
const userSlice = createSlice({
|
||||
name: 'user',
|
||||
initialState,
|
||||
reducers: {
|
||||
setUser(state, action: PayloadAction<{ token: string; username: string; role: Role }>) {
|
||||
const { token, username, role } = action.payload
|
||||
return { ...state, token, username, role }
|
||||
setUser(_state, action: PayloadAction<UserState>) {
|
||||
return { ...action.payload }
|
||||
},
|
||||
clearUser(state) {
|
||||
state.token = null
|
||||
state.username = null
|
||||
state.role = Role.ReadOnly
|
||||
clearUser() {
|
||||
return { ...initialState }
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const { setUser, clearUser } = userSlice.actions
|
||||
export const userReducer = userSlice.reducer
|
||||
export const { useTryLoginMutation, useGetAllUsersQuery, useGetUserDetailsQuery } = userApi
|
||||
export const { useTryLoginMutation } = userApi
|
||||
|
||||
Reference in New Issue
Block a user