Use persondetails for userpage instead of own component

Still requires removing staging mode from this and other data types; also write not supported yet.
This commit is contained in:
ShootingStar91
2024-07-12 23:29:39 +03:00
parent 97c4d99f14
commit 5df2bbc02d
10 changed files with 51 additions and 53 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ app.use('/reference', referenceRouter)
app.use('/time-unit', timeUnitRouter)
app.use('/time-bound', requireOneOf([Role.Admin]), timeBoundRouter)
app.use('/region', requireOneOf([Role.Admin]), regionRouter)
app.use('/person', requireOneOf([Role.Admin]), personRouter)
app.use('/person', personRouter)
app.use('/project', projectRouter)
app.use('/museum', museumRouter)
app.use('/sedimentary-structure', sedimentaryStructureRouter)
+10 -2
View File
@@ -1,17 +1,25 @@
import { Router } from 'express'
import { getAllPersons, getPersonDetails } from '../services/person'
import { Role } from '../../../frontend/src/types'
import { requireOneOf } from '../middlewares/authorizer'
const router = Router()
router.get('/all', async (_req, res) => {
router.get('/all', requireOneOf([Role.Admin]), async (_req, res) => {
const persons = await getAllPersons()
return res.status(200).send(persons)
})
router.get('/:id', async (req, res) => {
const id = req.params.id
/* Access checking happens differently for this route, since we want to allow the route to return the user's own data for any user */
if (!req.user) return res.status(401).send()
if (req.user.role !== Role.Admin && req.user.initials !== id) return res.status(401).send()
const person = await getPersonDetails(id)
res.status(200).send(person)
if (!person) return res.status(404).send()
return res.status(200).send(person)
})
export default router
+4 -1
View File
@@ -18,7 +18,10 @@ export const getPersonDetails = async (id: string) => {
if (!person.user_id) return { ...person, user: null }
const user = await nowDb.com_users.findUnique({ where: { user_id: person.user_id } })
const user = await nowDb.com_users.findUnique({
where: { user_id: person.user_id },
select: { user_id: true, user_name: true, last_login: true, now_user_group: true },
})
return { ...person, user }
}
-2
View File
@@ -13,7 +13,6 @@ import {
timeBoundPage,
timeUnitPage,
} from './components/pages'
import { UserPage } from './components/UserPage'
const App = () => {
return (
@@ -36,7 +35,6 @@ const App = () => {
<Route element={personPage} path="/person/:id?" />
<Route element={projectPage} path="/project/:id?" />
<Route element={<Login />} path="/login" />
<Route element={<UserPage />} path="/userpage" />
<Route element={<FrontPage />} path="/" />
<Route element={<div>Page not found.</div>} path="*" />
</Routes>
+1 -1
View File
@@ -48,7 +48,7 @@ export type SpeciesLocality = FixBigInt<Prisma.now_ls> & { now_loc: Prisma.now_l
export type LocalityUpdate = Prisma.now_lau & { now_lr: LocalityReference[] } & { updates: UpdateLog[] }
export type SpeciesUpdate = Prisma.now_sau & { now_sr: Prisma.now_sr }
export type Museum = Prisma.com_mlist
export type PersonDetailsType = Prisma.com_people & { user: Prisma.com_users | null }
export type PersonDetailsType = Prisma.com_people & { user: Omit<Prisma.com_users, 'password, newpassword'> | null }
export type ProjectPeople = Prisma.now_proj_people
export type ProjectDetailsType = Prisma.now_proj & { now_proj_people: Array<ProjectPeople> }
export type Project = Prisma.now_proj
@@ -34,12 +34,14 @@ export const DetailView = <T extends object>({
onWrite,
validator,
isNew = false,
isUserPage = false,
}: {
tabs: TabType[]
data: T
onWrite?: (editData: EditDataType<T>) => Promise<void>
validator: (editData: EditDataType<T>, field: keyof EditDataType<T>) => ValidationObject
isNew?: boolean
isUserPage?: boolean
}) => {
const [searchParams, setSearchParams] = useSearchParams()
const theme = useTheme()
@@ -132,7 +134,7 @@ export const DetailView = <T extends object>({
<DetailContextProvider contextState={initialState}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignContent: 'center', marginTop: 'auto' }}>
<Box sx={{ display: 'flex' }} gap={10}>
<ReturnButton />
{!isUserPage && <ReturnButton />}
{!mode.staging && !initialState.mode.new && (
<Button
onClick={() => setMode(!mode.read ? 'read' : 'edit')}
@@ -144,9 +146,11 @@ export const DetailView = <T extends object>({
)}
{(!mode.read || initialState.mode.new) && onWrite && <WriteButton onWrite={onWrite} />}
</Box>
<Box sx={{ marginRight: '3em' }}>
<DetailBrowser<T> />
</Box>
{!isUserPage && (
<Box sx={{ marginRight: '3em' }}>
<DetailBrowser<T> />
</Box>
)}
</Box>
{mode.staging ? stagingView : tabView}
</DetailContextProvider>
+1 -1
View File
@@ -42,7 +42,7 @@ export const NavBar = () => {
},
]
if (user.username) pages.push({ title: 'User', url: '/userpage' })
if (user.username) pages.push({ title: 'User', url: '/person/user-page' })
const logout = () => {
dispatch(clearUser())
@@ -3,11 +3,27 @@ import { useGetPersonDetailsQuery } from '../../redux/personReducer'
import { CircularProgress } from '@mui/material'
import { DetailView, TabType } from '../DetailView/DetailView'
import { PersonTab } from './Tabs/PersonTab'
import { useUser } from '@/hooks/user'
import { EditDataType, PersonDetailsType } from '@/backendTypes'
export const PersonDetails = () => {
const { id } = useParams()
const { id: idFromUrl } = useParams()
const user = useUser()
// A bit convoluted, but this is to allow using the same component for user's own page
// while omitting some detailview buttons like 'return to table' and browsing.
// We designate special id 'user-page' instead of normal initials to mean current user's own page.
const isUserPage = idFromUrl === 'user-page'
const id = isUserPage ? user.initials : idFromUrl
const { isLoading, isError, data } = useGetPersonDetailsQuery(id!)
// eslint-disable-next-line @typescript-eslint/require-await
const onWrite = async (editData: EditDataType<PersonDetailsType>) => {
// eslint-disable-next-line no-console
console.log(editData)
}
if (isError) return <div>Error loading data</div>
if (isLoading || !data) return <CircularProgress />
@@ -18,5 +34,13 @@ export const PersonDetails = () => {
},
]
return <DetailView tabs={tabs} data={data} validator={() => ({ name: '', error: null })} />
return (
<DetailView
onWrite={onWrite}
isUserPage={isUserPage}
tabs={tabs}
data={data}
validator={() => ({ name: '', error: null })}
/>
)
}
-38
View File
@@ -1,38 +0,0 @@
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
View File
@@ -80,7 +80,6 @@ export const localityPage = (
export const personPage = (
<Page
allowedRoles={[Role.Admin]}
tableView={<PersonTable />}
detailView={<PersonDetails />}
viewName="person"