Merged week 23

This commit is contained in:
arttu
2024-06-04 17:22:36 +03:00
14 changed files with 348 additions and 3 deletions
+2
View File
@@ -8,6 +8,7 @@ import sedimentaryStructureRouter from './routes/sedimentaryStructure'
import regionRouter from './routes/region'
import projectRouter from './routes/project'
import speciesRouter from './routes/species'
import timeBoundRouter from './routes/timeBound'
import timeUnitRouter from './routes/timeUnit'
import userRouter from './routes/user'
import { requestLogger, responseLogger } from './middlewares/requestLogger'
@@ -36,6 +37,7 @@ app.use('/reference', referenceRouter)
app.use('/region', regionRouter)
app.use('/project', projectRouter)
app.use('/species', speciesRouter)
app.use('/time-bound', timeBoundRouter)
app.use('/time-unit', timeUnitRouter)
app.use('/museum', museumRouter)
app.use('/sedimentary-structure', sedimentaryStructureRouter)
+24
View File
@@ -0,0 +1,24 @@
import { Router } from 'express'
import { getAllTimeBounds, getTimeBoundDetails, getTimeBoundTimeUnits } from '../services/timeBound'
import { fixBigInt } from '../utils/common'
const router = Router()
router.get('/all', async (_req, res) => {
const time_bounds = await getAllTimeBounds()
return res.status(200).send(time_bounds)
})
router.get('/:id', async (req, res) => {
const id = parseInt(req.params.id)
const time_bound = await getTimeBoundDetails(id)
res.status(200).send(time_bound)
})
router.get('/time-units/:id', async (req, res) => {
const id = parseInt(req.params.id)
const timeUnits = await getTimeBoundTimeUnits(id)
return res.status(200).send(fixBigInt(timeUnits))
})
export default router
+41
View File
@@ -0,0 +1,41 @@
import { logger } from '../utils/logger'
import { prisma } from '../utils/db'
import { EditDataType, TimeBoundDetailsType } from '../../../frontend/src/backendTypes'
import Prisma from '@prisma/client'
import { validateTimeBound } from '../../../frontend/src/validators/timeBound'
export const getAllTimeBounds = async () => {
const result = await prisma.now_tu_bound.findMany({
select: {
bid: true,
b_name: true,
age: true,
b_comment: true,
},
})
return result
}
export const getTimeBoundDetails = async (id: number) => {
// TODO: Check if user has access
const result = await prisma.now_tu_bound.findUnique({
where: { bid: id },
include: {
now_bau: {
include: {
now_br: true,
},
},
},
})
return result
}
export const getTimeBoundTimeUnits = async (id: number) => {
// TODO: Check if user has access
const result = await prisma.now_time_unit.findMany({
where: { OR: [{ up_bnd: id }, { low_bnd: id }] },
})
return result
}
+2 -1
View File
@@ -7,7 +7,7 @@ import { ProjectDetails } from './components/Project/ProjectDetails'
import { RegionTable } from './components/Region/RegionTable'
import { RegionDetails } from './components/Region/RegionDetails'
import { Login } from './components/Login'
import { localityPage, referencePage, speciesPage, timeUnitPage } from './components/pages'
import { localityPage, referencePage, speciesPage, timeBoundPage, timeUnitPage } from './components/pages'
const App = () => {
return (
@@ -25,6 +25,7 @@ const App = () => {
<Route element={speciesPage} path="/species/:id?" />
<Route element={referencePage} path="/reference/:id?" />
<Route element={timeUnitPage} path="/time-unit/:id?" />
<Route element={timeBoundPage} path="/time-bound/:id?" />
<Route element={<ProjectTable />} path="/project/" />
<Route element={<ProjectDetails />} path="/project/:id" />
<Route element={<RegionTable />} path="/region/" />
+11
View File
@@ -99,6 +99,15 @@ export type RegionDetails = Prisma.now_reg_coord & { now_reg_coord_people: Array
now_reg_coord_country: Array<Editable<RegionCountry>>
}
export type TimeBound = {
bid: number
b_name: string
age: number
b_comment: string
}
export type TimeBoundDetailsType = Prisma.now_tu_bound
export type TimeUnit = {
low_bound: number
up_bound: number
@@ -117,6 +126,8 @@ export type TimeUnit = {
rank: string
}
export type TimeBoundUpdate = Prisma.now_bau & { now_br: Prisma.now_br }
export type TimeUnitDetailsType = EditDataType<
Prisma.now_time_unit & { now_tu_sequence: Array<Editable<TimeUnitSequence>> }
>
+2 -1
View File
@@ -15,10 +15,11 @@ export const NavBar = () => {
{ title: 'Locality', url: '/locality' },
{ title: 'Species', url: '/species' },
{ title: 'Reference', url: '/reference' },
{ title: 'Time unit', url: '/time-unit' },
{ title: 'Time Unit', url: '/time-unit' },
{ title: 'Region', url: '/region' },
{ title: 'Project', url: '/project' },
{ title: 'Admin', url: '/admin' },
{ title: 'Time Bound', url: '/time-bound' },
]
if (user.username === null) pages.push({ title: 'Login', url: '/login' })
@@ -0,0 +1,21 @@
import { TimeBoundDetailsType } from '@/backendTypes'
import { ArrayFrame } from '@/components/DetailView/common/tabLayoutHelpers'
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
export const BoundTab = () => {
const { textField } = useDetailContext<TimeBoundDetailsType>()
const bound = [
// TODO Add static Class field
['Bound Id', textField('bid')],
['Name', textField('b_name')],
['Age (Ma)', textField('age')],
['Comment', textField('b_comment')],
]
return (
<>
<ArrayFrame array={bound} title="Bound" />
</>
)
}
@@ -0,0 +1,59 @@
import { TimeUnit, TimeBoundDetailsType } from '@/backendTypes'
import { useGetTimeBoundTimeUnitsQuery } from '@/redux/timeBoundReducer'
import { CircularProgress } from '@mui/material'
import { TableView } from '../../TableView/TableView'
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
export const TimeUnitTab = () => {
const { data } = useDetailContext<TimeBoundDetailsType>()
const { data: timeUnitsData, isError } = useGetTimeBoundTimeUnitsQuery(data.bid)
if (isError) return 'Error loading Time Units.'
if (!timeUnitsData) return <CircularProgress />
const columns = [
{
accessorKey: 'tu_name',
header: 'ID',
},
{
accessorKey: 'tu_display_name',
header: 'Name',
},
{
accessorKey: 'up_bnd',
// accessorFn: ({ up_bnd }) => up_bnd === data.bid,
header: 'Upper Bound',
},
{
accessorKey: 'low_bnd',
// accessorFn: ({ low_bnd }) => low_bnd === data.bid,
header: 'Lower Bound',
},
{
accessorKey: 'rank',
header: 'Rank',
},
{
accessorKey: 'sequence',
header: 'Sequence',
},
{
accessorKey: 'tu_comment',
header: 'Comment',
},
]
const checkRowRestriction = (row: TimeUnit) => {
return !!0
}
return (
<TableView<TimeUnit>
checkRowRestriction={checkRowRestriction}
idFieldName="tu_name"
columns={columns}
data={timeUnitsData}
url="time-unit"
/>
)
}
@@ -0,0 +1,39 @@
import { Editable, TimeBoundDetailsType, TimeBoundUpdate } from '@/backendTypes'
import { EditableTable } from '@/components/DetailView/common/EditableTable'
import { Grouped } from '@/components/DetailView/common/tabLayoutHelpers'
import { Box } from '@mui/material'
import { MRT_ColumnDef } from 'material-react-table'
export const UpdateTab = () => {
const formatDate = (date: Date | null) => {
if (!date) return 'No date'
return new Date(date).toISOString().split('T')[0]
}
const columns: MRT_ColumnDef<TimeBoundUpdate>[] = [
{
accessorKey: 'bau_date',
header: 'Date',
Cell: ({ cell }) => formatDate(cell.getValue() as Date | null),
},
{
accessorKey: 'bau_authorizer',
header: 'Editor',
},
{
accessorKey: 'bau_coordinator',
header: 'Coordinator',
},
{
accessorKey: 'now_br',
header: 'Reference',
Cell: () => <Box>not implemented</Box>,
},
]
return (
<Grouped title="Updates">
<EditableTable<Editable<TimeBoundUpdate>, TimeBoundDetailsType> columns={columns} field="now_bau" />
</Grouped>
)
}
@@ -0,0 +1,42 @@
import { useParams } from 'react-router-dom'
import { useGetTimeBoundDetailsQuery } from '../../redux/timeBoundReducer'
import { CircularProgress } from '@mui/material'
import { DetailView, TabType } from '../DetailView/DetailView'
import { BoundTab } from './Tabs/BoundTab'
import { TimeUnitTab } from './Tabs/TimeUnitTab.tsx'
import { UpdateTab } from './Tabs/UpdateTab'
export const TimeBoundDetails = () => {
const { id } = useParams()
const { isLoading, isError, data } = useGetTimeBoundDetailsQuery(id!)
if (isError) return <div>Error loading data</div>
if (isLoading || !data) return <CircularProgress />
const tabs: TabType[] = [
{
title: 'Bound',
content: <BoundTab />,
},
{
title: 'Time Units',
content: <TimeUnitTab />,
},
{
title: 'Updates',
content: <UpdateTab />,
},
]
return (
<DetailView
tabs={tabs}
data={data}
onWrite={() => {}}
validator={() => ({
name: 'unknown',
error: null,
})}
/>
)
}
@@ -0,0 +1,46 @@
import { useMemo } from 'react'
import { type MRT_ColumnDef } from 'material-react-table'
import { useGetAllTimeBoundsQuery } from '../../redux/timeBoundReducer'
import { TimeBound } from '@/backendTypes'
import { TableView } from '../TableView/TableView'
export const TimeBoundTable = ({ selectorFn }: { selectorFn?: (newTimeBound: TimeBound) => void }) => {
const time_boundQuery = useGetAllTimeBoundsQuery()
const columns = useMemo<MRT_ColumnDef<TimeBound>[]>(
() => [
{
id: 'bid',
accessorKey: 'bid',
header: 'Time Bound Id',
size: 20,
},
{
accessorKey: 'b_name',
header: 'Name',
},
{
accessorKey: 'age',
header: 'Age',
},
{
accessorKey: 'b_comment',
header: 'Comment',
},
],
[]
)
const checkRowRestriction = () => {
return false
}
return (
<TableView<TimeBound>
selectorFn={selectorFn}
checkRowRestriction={checkRowRestriction}
idFieldName="bid"
columns={columns}
data={time_boundQuery.data}
url="time-bound"
/>
)
}
+19 -1
View File
@@ -1,13 +1,31 @@
import { TimeUnitDetailsType, ReferenceDetailsType, SpeciesDetailsType, LocalityDetailsType } from '@/backendTypes'
import {
TimeUnitDetailsType,
ReferenceDetailsType,
SpeciesDetailsType,
LocalityDetailsType,
TimeBoundDetailsType,
} from '@/backendTypes'
import { LocalityDetails } from './Locality/LocalityDetails'
import { LocalityTable } from './Locality/LocalityTable'
import { Page } from './Page'
import { ReferenceTable } from './Reference/ReferenceTable'
import { SpeciesDetails } from './Species/SpeciesDetails'
import { SpeciesTable } from './Species/SpeciesTable'
import { TimeBoundDetails } from './TimeBound/TimeBoundDetails'
import { TimeBoundTable } from './TimeBound/TimeBoundTable'
import { TimeUnitTable } from './TimeUnit/TimeUnitTable'
import { TimeUnitDetails } from './TimeUnit/TimeUnitDetails'
export const timeBoundPage = (
<Page
tableView={<TimeBoundTable />}
detailView={<TimeBoundDetails />}
viewName="time-bound"
idFieldName="bid"
createTitle={(tb: TimeBoundDetailsType) => `${tb.b_name}`}
/>
)
export const timeUnitPage = (
<Page
tableView={<TimeUnitTable />}
+24
View File
@@ -0,0 +1,24 @@
import { api } from './api'
import { TimeBound, TimeBoundDetailsType, TimeUnit } from '@/backendTypes'
const timeboundsApi = api.injectEndpoints({
endpoints: builder => ({
getAllTimeBounds: builder.query<TimeBound[], void>({
query: () => ({
url: `/time-bound/all`,
}),
}),
getTimeBoundDetails: builder.query<TimeBoundDetailsType, string>({
query: id => ({
url: `/time-bound/${id}`,
}),
}),
getTimeBoundTimeUnits: builder.query<TimeUnit[], string>({
query: id => ({
url: `/time-bound/time-units/${id}`,
}),
}),
}),
})
export const { useGetAllTimeBoundsQuery, useGetTimeBoundDetailsQuery, useGetTimeBoundTimeUnitsQuery } = timeboundsApi
+16
View File
@@ -0,0 +1,16 @@
import { EditDataType, TimeBoundDetailsType } from '../backendTypes'
import { Validators, validator } from './validator'
export const validateTimeBound = (
editData: EditDataType<TimeBoundDetailsType>,
fieldName: keyof TimeBoundDetailsType
) => {
const validators: Validators<Partial<EditDataType<TimeBoundDetailsType>>> = {
b_name: {
name: 'Locality name',
},
}
const returnval = validator<TimeBoundDetailsType>(validators, editData, fieldName)
return returnval
}