mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Added PersonTable, PersonDetails, UserTable, UserDetails
This commit is contained in:
@@ -6,6 +6,7 @@ import museumRouter from './routes/museum'
|
||||
import referenceRouter from './routes/reference'
|
||||
import sedimentaryStructureRouter from './routes/sedimentaryStructure'
|
||||
import regionRouter from './routes/region'
|
||||
import personRouter from './routes/person'
|
||||
import projectRouter from './routes/project'
|
||||
import speciesRouter from './routes/species'
|
||||
import timeBoundRouter from './routes/timeBound'
|
||||
@@ -35,6 +36,7 @@ if (BACKEND_MODE !== 'dev') app.use(requireLogin)
|
||||
app.use('/locality', localityRouter)
|
||||
app.use('/reference', referenceRouter)
|
||||
app.use('/region', regionRouter)
|
||||
app.use('/person', personRouter)
|
||||
app.use('/project', projectRouter)
|
||||
app.use('/species', speciesRouter)
|
||||
app.use('/time-bound', timeBoundRouter)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Router } from 'express'
|
||||
import { getAllPersons, getPersonDetails } from '../services/person'
|
||||
|
||||
const router = Router()
|
||||
|
||||
router.get('/all', async (_req, res) => {
|
||||
const persons = await getAllPersons()
|
||||
return res.status(200).send(persons)
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res) => {
|
||||
const id = req.params.id
|
||||
const person = await getPersonDetails(id)
|
||||
res.status(200).send(person)
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -4,6 +4,7 @@ import * as jwt from 'jsonwebtoken'
|
||||
import { SECRET, LOGIN_VALID_MS, USER_CREATION_SECRET } from '../utils/config'
|
||||
import * as bcrypt from 'bcrypt'
|
||||
import { prisma } from '../utils/db'
|
||||
import { getAllUsers, getUserDetails } from '../services/user'
|
||||
|
||||
const router = Router()
|
||||
|
||||
@@ -27,6 +28,17 @@ router.post('/login', async (req, res) => {
|
||||
return res.status(200).send({ token, username: result.user_name })
|
||||
})
|
||||
|
||||
router.get('/all', async (_req, res) => {
|
||||
const users = await getAllUsers()
|
||||
return res.status(200).send(users)
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res) => {
|
||||
const id = parseInt(req.params.id)
|
||||
const user = await getUserDetails(id)
|
||||
res.status(200).send(user)
|
||||
})
|
||||
|
||||
router.post('/create', async (req, res) => {
|
||||
// TODO type guards
|
||||
const { username, password, secret } = req.body
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { where } from 'sequelize'
|
||||
import { prisma } from '../utils/db'
|
||||
|
||||
export const getAllPersons = async () => {
|
||||
const result = await prisma.com_people.findMany({})
|
||||
return result
|
||||
}
|
||||
|
||||
export const getPersonDetails = async (id: string) => {
|
||||
// TODO: Check if user has access
|
||||
|
||||
const result = await prisma.com_people.findUnique({
|
||||
where: { initials: id },
|
||||
})
|
||||
return result
|
||||
}
|
||||
@@ -26,3 +26,14 @@ export const getAllUsers = async () => {
|
||||
const result = await prisma.com_users.findMany({})
|
||||
return result
|
||||
}
|
||||
|
||||
export const getUserDetails = async (id: number) => {
|
||||
// TODO: Check if user has access
|
||||
|
||||
const result = await prisma.com_users.findUnique({
|
||||
where: {
|
||||
user_id: id,
|
||||
},
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import { Login } from './components/Login'
|
||||
import {
|
||||
localityPage,
|
||||
personPage,
|
||||
projectPage,
|
||||
referencePage,
|
||||
regionPage,
|
||||
speciesPage,
|
||||
timeBoundPage,
|
||||
timeUnitPage,
|
||||
userPage,
|
||||
} from './components/pages'
|
||||
|
||||
const App = () => {
|
||||
@@ -31,7 +33,9 @@ const App = () => {
|
||||
<Route element={timeUnitPage} path="/time-unit/:id?" />
|
||||
<Route element={timeBoundPage} path="/time-bound/:id?" />
|
||||
<Route element={regionPage} path="/region/:id?" />
|
||||
<Route element={personPage} path="/person/:id?" />
|
||||
<Route element={projectPage} path="/project/:id?" />
|
||||
<Route element={userPage} path="/user/:id?" />
|
||||
<Route element={<FrontPage />} path="/" />
|
||||
<Route element={<Login />} path="/login" />
|
||||
<Route element={<div>Page not found.</div>} path="*" />
|
||||
|
||||
Vendored
+4
@@ -27,6 +27,8 @@ export type SpeciesLocality = Prisma.now_ls & { now_loc: Prisma.now_loc }
|
||||
export type LocalityUpdate = Prisma.now_lau & { now_lr: Prisma.now_lr }
|
||||
export type SpeciesUpdate = Prisma.now_sau & { now_lr: Prisma.now_sr }
|
||||
export type Museum = Prisma.com_mlist
|
||||
export type Person = Prisma.com_people
|
||||
export type PersonDetailsType = Prisma.com_people
|
||||
export type ProjectPeople = Prisma.now_proj_people
|
||||
export type ProjectDetailsType = Prisma.now_proj & { now_proj_people: Array<ProjectPeople> }
|
||||
export type Project = Prisma.now_proj
|
||||
@@ -142,3 +144,5 @@ export type TimeUnitDetailsType = EditDataType<
|
||||
export type ReferenceType = Prisma.ref_ref_type & { ref_field_name: Prisma.ref_field_name[] }
|
||||
|
||||
export type ReferenceField = Prisma.ref_field_name
|
||||
export type User = Prisma.com_users
|
||||
export type UserDetailsType = EditDataType<Prisma.com_users>
|
||||
|
||||
@@ -34,6 +34,7 @@ export const NavBar = () => {
|
||||
children: [
|
||||
{ title: 'Region', url: '/region' },
|
||||
{ title: 'Project', url: '/project' },
|
||||
{ title: 'Person', url: '/person' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useGetPersonDetailsQuery } from '../../redux/personReducer'
|
||||
import { CircularProgress } from '@mui/material'
|
||||
import { DetailView, TabType } from '../DetailView/DetailView'
|
||||
import { PersonTab } from './Tabs/PersonTab'
|
||||
|
||||
export const PersonDetails = () => {
|
||||
const { id } = useParams()
|
||||
const { isLoading, isError, data } = useGetPersonDetailsQuery(id!)
|
||||
|
||||
if (isError) return <div>Error loading data</div>
|
||||
if (isLoading || !data) return <CircularProgress />
|
||||
|
||||
const tabs: TabType[] = [
|
||||
{
|
||||
title: 'Person',
|
||||
content: <PersonTab />,
|
||||
},
|
||||
]
|
||||
|
||||
return <DetailView tabs={tabs} data={data} onWrite={() => {}} validator={() => ({ name: '', error: null })} />
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useMemo } from 'react'
|
||||
import { type MRT_ColumnDef } from 'material-react-table'
|
||||
import { useGetAllPersonsQuery } from '../../redux/personReducer'
|
||||
import { Person } from '@/backendTypes'
|
||||
import { TableView } from '../TableView/TableView'
|
||||
|
||||
export const PersonTable = ({ selectorFn }: { selectorFn?: (id: Person) => void }) => {
|
||||
const personQuery = useGetAllPersonsQuery()
|
||||
const columns = useMemo<MRT_ColumnDef<Person>[]>(
|
||||
() => [
|
||||
{
|
||||
id: 'id',
|
||||
accessorKey: 'initials',
|
||||
header: 'Person Id',
|
||||
},
|
||||
{
|
||||
accessorKey: 'first_name',
|
||||
header: 'First name',
|
||||
},
|
||||
{
|
||||
accessorKey: 'surname',
|
||||
header: 'Surname',
|
||||
},
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: 'Email',
|
||||
},
|
||||
{
|
||||
accessorKey: 'organization',
|
||||
header: 'Organisation',
|
||||
},
|
||||
{
|
||||
accessorKey: 'country',
|
||||
header: 'Country',
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
const checkRowRestriction = () => {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
<TableView<Person>
|
||||
selectorFn={selectorFn}
|
||||
checkRowRestriction={checkRowRestriction}
|
||||
idFieldName="initials"
|
||||
columns={columns}
|
||||
data={personQuery.data}
|
||||
url="person"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { PersonDetailsType } from '@/backendTypes'
|
||||
import { ArrayFrame } from '../../DetailView/common/tabLayoutHelpers'
|
||||
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
|
||||
|
||||
export const PersonTab = () => {
|
||||
const { textField } = useDetailContext<PersonDetailsType>()
|
||||
|
||||
const person = [
|
||||
['User ID', textField('user_id', 'number')],
|
||||
['Initials', textField('initials')],
|
||||
['First Name', textField('first_name')],
|
||||
['Surname', textField('surname')],
|
||||
['Email', textField('email')],
|
||||
['Organization', textField('organization')],
|
||||
['Country', textField('country')],
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<ArrayFrame array={person} title="Person" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { UserDetailsType } from '@/backendTypes'
|
||||
import { ArrayFrame } from '../../DetailView/common/tabLayoutHelpers'
|
||||
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
|
||||
|
||||
export const UserTab = () => {
|
||||
const { textField } = useDetailContext<UserDetailsType>()
|
||||
|
||||
const user = [
|
||||
['User Name', textField('user_name')],
|
||||
['Password', textField('password')],
|
||||
['Confirm Password', textField('password')],
|
||||
['Last log in', textField('last_login')],
|
||||
['User Group', textField('now_user_group')],
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<ArrayFrame array={user} title="User" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useGetUserDetailsQuery } from '../../redux/userReducer'
|
||||
import { CircularProgress } from '@mui/material'
|
||||
import { DetailView, TabType } from '../DetailView/DetailView'
|
||||
import { UserTab } from './Tabs/UserTab'
|
||||
|
||||
export const UserDetails = () => {
|
||||
const { id } = useParams()
|
||||
const { isLoading, isError, data } = useGetUserDetailsQuery(id!)
|
||||
|
||||
if (isError) return <div>Error loading data</div>
|
||||
if (isLoading || !data) return <CircularProgress />
|
||||
|
||||
const tabs: TabType[] = [
|
||||
{
|
||||
title: 'Person',
|
||||
content: <UserTab />,
|
||||
},
|
||||
]
|
||||
|
||||
return <DetailView tabs={tabs} data={data} onWrite={() => {}} validator={() => ({ name: '', error: null })} />
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useMemo } from 'react'
|
||||
import { type MRT_ColumnDef } from 'material-react-table'
|
||||
import { useGetAllUsersQuery } from '../../redux/userReducer'
|
||||
import { User } from '@/backendTypes'
|
||||
import { TableView } from '../TableView/TableView'
|
||||
|
||||
export const UserTable = ({ selectorFn }: { selectorFn?: (id: User) => void }) => {
|
||||
const userQuery = useGetAllUsersQuery()
|
||||
const columns = useMemo<MRT_ColumnDef<User>[]>(
|
||||
() => [
|
||||
{
|
||||
id: 'id',
|
||||
accessorKey: 'User_id',
|
||||
header: 'Id',
|
||||
size: 20,
|
||||
},
|
||||
{
|
||||
accessorKey: 'user_name',
|
||||
header: 'User name',
|
||||
},
|
||||
{
|
||||
accessorKey: 'last_login',
|
||||
header: 'Last login',
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
const checkRowRestriction = () => {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
<TableView<User>
|
||||
selectorFn={selectorFn}
|
||||
checkRowRestriction={checkRowRestriction}
|
||||
idFieldName="initials"
|
||||
columns={columns}
|
||||
data={userQuery.data}
|
||||
url="user"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -4,8 +4,10 @@ import {
|
||||
SpeciesDetailsType,
|
||||
LocalityDetailsType,
|
||||
TimeBoundDetailsType,
|
||||
PersonDetailsType,
|
||||
ProjectDetailsType,
|
||||
RegionDetails as RegionDetailsType,
|
||||
UserDetailsType,
|
||||
} from '@/backendTypes'
|
||||
import { LocalityDetails } from './Locality/LocalityDetails'
|
||||
import { LocalityTable } from './Locality/LocalityTable'
|
||||
@@ -18,10 +20,14 @@ import { TimeBoundTable } from './TimeBound/TimeBoundTable'
|
||||
import { TimeUnitTable } from './TimeUnit/TimeUnitTable'
|
||||
import { TimeUnitDetails } from './TimeUnit/TimeUnitDetails'
|
||||
import { ReferenceDetails } from './Reference/ReferenceDetails'
|
||||
import { PersonTable } from './Person/PersonTable'
|
||||
import { PersonDetails } from './Person/PersonDetails'
|
||||
import { ProjectTable } from './Project/ProjectTable'
|
||||
import { ProjectDetails } from './Project/ProjectDetails'
|
||||
import { RegionTable } from './Region/RegionTable'
|
||||
import { RegionDetails } from './Region/RegionDetails'
|
||||
import { UserTable } from './User/UserTable'
|
||||
import { UserDetails } from './User/UserDetails'
|
||||
|
||||
export const timeBoundPage = (
|
||||
<Page
|
||||
@@ -73,6 +79,16 @@ export const localityPage = (
|
||||
/>
|
||||
)
|
||||
|
||||
export const personPage = (
|
||||
<Page
|
||||
tableView={<PersonTable />}
|
||||
detailView={<PersonDetails />}
|
||||
viewName="person"
|
||||
idFieldName="initials"
|
||||
createTitle={(person: PersonDetailsType) => `${person.surname}`}
|
||||
/>
|
||||
)
|
||||
|
||||
export const projectPage = (
|
||||
<Page
|
||||
tableView={<ProjectTable />}
|
||||
@@ -92,3 +108,13 @@ export const regionPage = (
|
||||
createTitle={(region: RegionDetailsType) => `${region.region}`}
|
||||
/>
|
||||
)
|
||||
|
||||
export const userPage = (
|
||||
<Page
|
||||
tableView={<UserTable />}
|
||||
detailView={<UserDetails />}
|
||||
viewName="user"
|
||||
idFieldName="id"
|
||||
createTitle={(user: UserDetailsType) => `${user.user_name}`}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { api } from './api'
|
||||
import { Person, PersonDetailsType } from '@/backendTypes'
|
||||
|
||||
const personsApi = api.injectEndpoints({
|
||||
endpoints: builder => ({
|
||||
getAllPersons: builder.query<Person[], void>({
|
||||
query: () => ({
|
||||
url: `/person/all`,
|
||||
}),
|
||||
}),
|
||||
getPersonDetails: builder.query<PersonDetailsType, string>({
|
||||
query: id => ({
|
||||
url: `/person/${id}`,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const { useGetAllPersonsQuery, useGetPersonDetailsQuery } = personsApi
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { api } from '@/redux/api'
|
||||
import { UserFields } from '@shared/types'
|
||||
import { User, UserDetailsType } from '@/backendTypes'
|
||||
|
||||
type LoginData = { username: string; password: string }
|
||||
|
||||
@@ -16,6 +17,16 @@ const userApi = api.injectEndpoints({
|
||||
method: 'POST',
|
||||
}),
|
||||
}),
|
||||
getAllUsers: builder.query<User[], void>({
|
||||
query: () => ({
|
||||
url: `/user/all`,
|
||||
}),
|
||||
}),
|
||||
getUserDetails: builder.query<UserDetailsType, string>({
|
||||
query: id => ({
|
||||
url: `/user/${id}`,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -46,4 +57,4 @@ const userSlice = createSlice({
|
||||
|
||||
export const { setUser, clearUser } = userSlice.actions
|
||||
export const userReducer = userSlice.reducer
|
||||
export const { useTryLoginMutation } = userApi
|
||||
export const { useTryLoginMutation, useGetAllUsersQuery, useGetUserDetailsQuery } = userApi
|
||||
|
||||
Reference in New Issue
Block a user