mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Re-do editdata type: make all fields strings and optional for edit data
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
} from '../services/locality'
|
||||
import { fixBigInt } from '../utils/common'
|
||||
import { detailedDiff } from 'deep-object-diff'
|
||||
import { LocalityDetailsType } from '../../../frontend/src/backendTypes'
|
||||
import { EditDataType, LocalityDetailsType } from '../../../frontend/src/backendTypes'
|
||||
|
||||
const router = Router()
|
||||
|
||||
@@ -25,10 +25,10 @@ router.get('/:id', async (req, res) => {
|
||||
res.status(200).send(fixBigInt(locality))
|
||||
})
|
||||
|
||||
router.put('/', async (req: Request<object, object, { locality: LocalityDetailsType }>, res) => {
|
||||
router.put('/', async (req: Request<object, object, { locality: EditDataType<LocalityDetailsType> }>, res) => {
|
||||
const editedLocality = req.body.locality
|
||||
const fixedEditedLocality = fixEditedLocality(editedLocality)
|
||||
const oldLocality = await getLocalityDetails(fixedEditedLocality.lid)
|
||||
const oldLocality = await getLocalityDetails(parseInt(fixedEditedLocality.lid!))
|
||||
const difference = detailedDiff(oldLocality!, fixedEditedLocality)
|
||||
const filteredLoc = filterLocality(difference.updated)
|
||||
const validationErrors = validateEntireLocality(filteredLoc)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { logger } from '../utils/logger'
|
||||
import { prisma } from '../utils/db'
|
||||
import { LocalityDetailsType } from '../../../frontend/src/backendTypes'
|
||||
import { EditDataType, LocalityDetailsType } from '../../../frontend/src/backendTypes'
|
||||
import Prisma from '@prisma/client'
|
||||
import { validateLocality } from '../../../frontend/src/validators/locality'
|
||||
|
||||
@@ -60,7 +60,7 @@ export const getLocalityDetails = async (id: number) => {
|
||||
return { ...locality, museums: now_mus.map(museum => museum.com_mlist) }
|
||||
}
|
||||
|
||||
export const fixEditedLocality = (editedLocality: LocalityDetailsType) => {
|
||||
export const fixEditedLocality = (editedLocality: EditDataType<LocalityDetailsType>) => {
|
||||
if (editedLocality.now_lau) {
|
||||
editedLocality.now_lau = editedLocality.now_lau.map(lau => ({
|
||||
...lau,
|
||||
@@ -83,13 +83,13 @@ export const validateEntireLocality = (editedFields: Partial<Prisma.now_loc>) =>
|
||||
const keys = Object.keys(editedFields)
|
||||
const errors = []
|
||||
for (const key of keys) {
|
||||
const error = validateLocality(editedFields as LocalityDetailsType, key as keyof LocalityDetailsType)
|
||||
const error = validateLocality(editedFields as EditDataType<LocalityDetailsType>, key as keyof LocalityDetailsType)
|
||||
if (error !== null) errors.push(error)
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
export const filterLocality = (editedFields: Partial<LocalityDetailsType>) => {
|
||||
export const filterLocality = (editedFields: EditDataType<LocalityDetailsType>) => {
|
||||
const fields = Object.keys(prisma.now_loc.fields)
|
||||
const filteredLoc = Object.entries(editedFields)
|
||||
.filter(([field]) => fields.includes(field))
|
||||
|
||||
Vendored
+17
@@ -1,5 +1,22 @@
|
||||
import * as Prisma from '../../backend/node_modules/@prisma/client/default'
|
||||
|
||||
type PartialString<T> = {
|
||||
[P in keyof T]: T[P] extends string | null ? T[P] | undefined : T[P]
|
||||
}
|
||||
|
||||
// Makes all number fields strings, and then all string fields optional! Used for editData
|
||||
type EditFields<T> = PartialString<{
|
||||
[P in keyof T]: T[P] extends number | bigint | null ? string : T[P]
|
||||
}>
|
||||
|
||||
type EditDataType<T> = EditFields<{
|
||||
[P in keyof T]: T[P] extends (infer U)[]
|
||||
? EditDataType<U>[]
|
||||
: T[P] extends object
|
||||
? EditDataType<PartialString<EditFields<T[P]>>>
|
||||
: T[P]
|
||||
}>
|
||||
|
||||
export type RowState = 'new' | 'removed' | 'cancelled' | 'clean'
|
||||
export type UpdateComment = { update_comment: string }
|
||||
// Use this for fields that include array that has to be edited by EditableTable.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { ReactNode, createContext, useState, JSX, useEffect, Context, useContext } from 'react'
|
||||
import { DropdownOption } from '../common/editingComponents'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { ValidationObject } from '@/validators/validator'
|
||||
import { EditDataType } from '@/backendTypes'
|
||||
|
||||
export type ModeOptions = 'read' | 'new' | 'edit' | 'staging-edit' | 'staging-new'
|
||||
|
||||
@@ -49,35 +51,54 @@ export type DetailContextType<T> = {
|
||||
data: T
|
||||
mode: ModeType
|
||||
setMode: (newMode: ModeOptions) => void
|
||||
editData: T
|
||||
setEditData: (editData: T) => void
|
||||
editData: EditDataType<T>
|
||||
setEditData: (editData: EditDataType<T>) => void
|
||||
textField: (field: keyof T, type?: React.HTMLInputTypeAttribute) => JSX.Element
|
||||
bigTextField: (field: keyof T) => JSX.Element
|
||||
dropdown: (field: keyof T, options: Array<DropdownOption | string>, name: string) => JSX.Element
|
||||
dropdown: (field: keyof T, options: Array<DropdownOption | string>, name: string, disabled?: boolean) => JSX.Element
|
||||
radioSelection: (field: keyof T, options: Array<DropdownOption | string>, name: string) => JSX.Element
|
||||
validator: (editData: T, field: keyof T) => string | null
|
||||
validator: (editData: EditDataType<T>, field: keyof T) => ValidationObject
|
||||
}
|
||||
|
||||
export const DetailContext = createContext<DetailContextType<unknown>>(null!)
|
||||
|
||||
export const DetailContextProvider = <T extends object>({
|
||||
/* This changes data so that all number or bigint fields become string */
|
||||
export const makeEditData = <T,>(data: T): EditDataType<T> => {
|
||||
const editData: EditDataType<T> = {} as EditDataType<T>
|
||||
for (const field in data) {
|
||||
if (Array.isArray(data[field])) {
|
||||
editData[field] = (data[field] as never[]).map(item => makeEditData(item)) as never
|
||||
} else if (typeof data[field] === 'object' && data[field] !== null) {
|
||||
editData[field] = makeEditData(data[field]) as never
|
||||
} else if (typeof data[field] === 'number') {
|
||||
editData[field] = ('' + (data[field] as number)) as never
|
||||
} else if (typeof data[field] === 'bigint') {
|
||||
editData[field] = ('' + (data[field] as bigint)) as never
|
||||
} else {
|
||||
editData[field] = cloneDeep(data[field]) as never
|
||||
}
|
||||
}
|
||||
return editData
|
||||
}
|
||||
|
||||
export const DetailContextProvider = <T extends EditDataType<object>>({
|
||||
children,
|
||||
contextState,
|
||||
}: {
|
||||
children: ReactNode | ReactNode[]
|
||||
contextState: Omit<DetailContextType<T>, 'setEditData'>
|
||||
}) => {
|
||||
const [editData, setEditData] = useState<T>(cloneDeep(contextState.data))
|
||||
|
||||
useEffect(() => setEditData(cloneDeep(contextState.data)), [contextState.data])
|
||||
const [editData, setEditData] = useState<EditDataType<T>>(makeEditData(contextState.data))
|
||||
|
||||
useEffect(() => setEditData(makeEditData(contextState.data)), [contextState.data])
|
||||
return (
|
||||
<DetailContext.Provider
|
||||
value={{
|
||||
...contextState,
|
||||
editData,
|
||||
setEditData: (data: unknown) => setEditData(data as T),
|
||||
validator: (editData: unknown, fieldName: keyof T) => contextState.validator(editData as T, fieldName),
|
||||
setEditData: (data: unknown) => setEditData(data as EditDataType<T>),
|
||||
validator: (editData: unknown, fieldName: keyof T) =>
|
||||
contextState.validator(editData as EditDataType<T>, fieldName),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -2,12 +2,13 @@ import { useSearchParams } from 'react-router-dom'
|
||||
import { Box, Button, Paper, Stack, Tab, Tabs } from '@mui/material'
|
||||
import EditIcon from '@mui/icons-material/Edit'
|
||||
import { useEffect, useState, JSX } from 'react'
|
||||
import { DetailContextProvider, ModeOptions, ModeType, modeOptionToMode } from './Context/DetailContext'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { DetailContextProvider, ModeOptions, ModeType, makeEditData, modeOptionToMode } from './Context/DetailContext'
|
||||
import { DropdownOption, DropdownSelector, EditableTextField, RadioSelector } from './common/editingComponents'
|
||||
import { DetailBrowser } from './DetailBrowser'
|
||||
import { StagingView } from './StagingView'
|
||||
import { ReturnButton, WriteButton } from './components'
|
||||
import { ValidationObject } from '@/validators/validator'
|
||||
import { EditDataType } from '@/backendTypes'
|
||||
|
||||
export type TabType = {
|
||||
title: string
|
||||
@@ -22,8 +23,8 @@ export const DetailView = <T extends object>({
|
||||
}: {
|
||||
tabs: TabType[]
|
||||
data: T
|
||||
onWrite: (editData: T) => void
|
||||
validator: (editData: T, field: keyof T) => string | null
|
||||
onWrite: (editData: EditDataType<T>) => void
|
||||
validator: (editData: EditDataType<T>, field: keyof EditDataType<T>) => ValidationObject
|
||||
}) => {
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const getUrl = () => {
|
||||
@@ -67,7 +68,7 @@ export const DetailView = <T extends object>({
|
||||
data,
|
||||
mode,
|
||||
setMode,
|
||||
editData: cloneDeep(data),
|
||||
editData: makeEditData(data),
|
||||
textField,
|
||||
dropdown,
|
||||
radioSelection,
|
||||
|
||||
@@ -20,7 +20,7 @@ export const EditableTable = <T extends Editable<MRT_RowData>, ParentType extend
|
||||
field,
|
||||
}: {
|
||||
tableData?: Array<T> | null
|
||||
editTableData?: Array<T> | null
|
||||
editTableData?: Array<Editable<T>> | null
|
||||
columns: MRT_ColumnDef<T>[]
|
||||
editable?: boolean
|
||||
field: keyof ParentType
|
||||
@@ -34,7 +34,7 @@ export const EditableTable = <T extends Editable<MRT_RowData>, ParentType extend
|
||||
// TODO: Using static index - need to use some id, probably sorting breaks this
|
||||
const rowClicked = (index: number | undefined) => {
|
||||
if (index === undefined) return
|
||||
const items = [...editData[field]]
|
||||
const items = [...(editData[field] as Array<Editable<object>>)]
|
||||
if (items[index].rowState === 'new') {
|
||||
items.splice(index, 1)
|
||||
setEditData({ ...editData, [field]: items })
|
||||
|
||||
@@ -43,7 +43,7 @@ export const DropdownSelector = <T extends object>({
|
||||
id={`${name}-multiselect`}
|
||||
value={(editData[field] || '') as string}
|
||||
onChange={(event: SelectChangeEvent) => setEditData({ ...editData, [field]: event.target.value })}
|
||||
sx={{ width: fieldWidth }}
|
||||
sx={{ width: fieldWidth, backgroundColor: disabled ? 'grey' : '' }}
|
||||
size="small"
|
||||
disabled={disabled}
|
||||
>
|
||||
@@ -150,10 +150,10 @@ export const EditableTextField = <T extends object>({
|
||||
disabled?: boolean
|
||||
}) => {
|
||||
const { setEditData, editData, validator } = useDetailContext<T>()
|
||||
const error = validator(editData, field)
|
||||
const { error } = validator(editData, field)
|
||||
const editingComponent = (
|
||||
<TextField
|
||||
sx={{ width: fieldWidth }}
|
||||
sx={{ width: fieldWidth, backgroundColor: disabled ? 'grey' : '' }}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||
setEditData({ ...editData, [field]: event?.currentTarget?.value })
|
||||
}
|
||||
@@ -214,6 +214,7 @@ export const FieldWithTableSelection = <T extends object, ParentType extends obj
|
||||
value={editData[targetField]}
|
||||
onClick={() => setOpen(true)}
|
||||
disabled={disabled}
|
||||
sx={{ backgroundColor: disabled ? 'grey' : '' }}
|
||||
/>
|
||||
)
|
||||
return <DataValue<ParentType> field={targetField} EditElement={editingComponent} />
|
||||
|
||||
@@ -4,8 +4,9 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import SaveIcon from '@mui/icons-material/Save'
|
||||
import { usePageContext } from '../Page'
|
||||
import { useDetailContext } from './Context/DetailContext'
|
||||
import { EditDataType } from '@/backendTypes'
|
||||
|
||||
export const WriteButton = <T,>({ onWrite, text }: { onWrite: (editData: T) => void; text?: string }) => {
|
||||
export const WriteButton = <T,>({ onWrite, text }: { onWrite: (editData: EditDataType<T>) => void; text?: string }) => {
|
||||
const { editData, mode, setMode } = useDetailContext<T>()
|
||||
return (
|
||||
<Button
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ProjectTab } from './Tabs/ProjectTab'
|
||||
import { SpeciesTab } from './Tabs/SpeciesTab'
|
||||
import { TaphonomyTab } from './Tabs/TaphonomyTab'
|
||||
import { UpdateTab } from './Tabs/UpdateTab'
|
||||
import { LocalityDetailsType } from '@/backendTypes'
|
||||
import { EditDataType, LocalityDetailsType } from '@/backendTypes'
|
||||
import { validateLocality } from '@/validators/locality'
|
||||
|
||||
export const LocalityDetails = () => {
|
||||
@@ -22,7 +22,7 @@ export const LocalityDetails = () => {
|
||||
if (isError) return <div>Error loading data</div>
|
||||
if (isLoading || !data) return <CircularProgress />
|
||||
|
||||
const onWrite = (editData: LocalityDetailsType) => {
|
||||
const onWrite = (editData: EditDataType<LocalityDetailsType>) => {
|
||||
void editLocalityRequest(editData)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
|
||||
import { TimeUnitTable } from '@/components/TimeUnit/TimeUnitTable'
|
||||
|
||||
export const AgeTab = () => {
|
||||
const { textField, radioSelection, dropdown, bigTextField } = useDetailContext<LocalityDetailsType>()
|
||||
const { textField, radioSelection, dropdown, bigTextField, editData } = useDetailContext<LocalityDetailsType>()
|
||||
const fracOptions = [
|
||||
{ value: '', display: 'None' },
|
||||
'Early half 1:2',
|
||||
@@ -54,26 +54,28 @@ export const AgeTab = () => {
|
||||
[
|
||||
'Minimum age',
|
||||
textField('min_age', 'number'),
|
||||
dropdown('bfa_min_abs', bfa_abs_options, 'Min Basis for age (absolute)'),
|
||||
dropdown('bfa_min_abs', bfa_abs_options, 'Min Basis for age (absolute)', editData.date_meth === 'time_unit'),
|
||||
<FieldWithTableSelection<TimeUnitDetailsType, LocalityDetailsType>
|
||||
key="bfa_min"
|
||||
sourceField="tu_name"
|
||||
targetField="bfa_min"
|
||||
selectorTable={<TimeUnitTable />}
|
||||
disabled={editData.date_meth === 'absolute'}
|
||||
/>,
|
||||
dropdown('frac_min', fracOptions, 'Minimum fraction'),
|
||||
dropdown('frac_min', fracOptions, 'Minimum fraction', editData.date_meth === 'absolute'),
|
||||
],
|
||||
[
|
||||
'Maximum age',
|
||||
textField('max_age'),
|
||||
dropdown('bfa_max_abs', bfa_abs_options, 'Max Basis for age (absolute)'),
|
||||
textField('max_age', 'number'),
|
||||
dropdown('bfa_max_abs', bfa_abs_options, 'Max Basis for age (absolute)', editData.date_meth === 'time_unit'),
|
||||
<FieldWithTableSelection<TimeUnitDetailsType, LocalityDetailsType>
|
||||
key="bfa_max"
|
||||
sourceField="tu_name"
|
||||
targetField="bfa_max"
|
||||
selectorTable={<TimeUnitTable />}
|
||||
disabled={editData.date_meth === 'absolute'}
|
||||
/>,
|
||||
dropdown('frac_max', fracOptions, 'Maximum fraction'),
|
||||
dropdown('frac_max', fracOptions, 'Maximum fraction', editData.date_meth === 'absolute'),
|
||||
],
|
||||
[''],
|
||||
['Chronostrathigraphic age', textField('chron')],
|
||||
|
||||
@@ -194,10 +194,10 @@ export const LithologyTab = () => {
|
||||
editingAction={(newSed: SedimentaryStructureValues) => {
|
||||
setEditData({
|
||||
...editData,
|
||||
now_ss: [...editData.now_ss, { lid: editData.lid, sed_struct: newSed.ss_value, rowState: 'new' }],
|
||||
now_ss: [...editData.now_ss, { lid: editData.lid!, sed_struct: newSed.ss_value, rowState: 'new' }],
|
||||
})
|
||||
}}
|
||||
selectedValues={editData.now_ss.map(ss => ss.sed_struct)}
|
||||
selectedValues={editData.now_ss.map(ss => ss.sed_struct!)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -58,5 +58,15 @@ export const SpeciesDetails = () => {
|
||||
},
|
||||
]
|
||||
|
||||
return <DetailView tabs={tabs} data={data} onWrite={() => {}} validator={() => null} />
|
||||
return (
|
||||
<DetailView
|
||||
tabs={tabs}
|
||||
data={data}
|
||||
onWrite={() => {}}
|
||||
validator={() => ({
|
||||
name: 'unknown',
|
||||
error: null,
|
||||
})}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const LocalityTab = () => {
|
||||
header: 'Min Age',
|
||||
},
|
||||
]
|
||||
|
||||
// TODO fix
|
||||
// Here is code for adding new locality from Species > Localities tab.
|
||||
// Let's add it later, as it is quite complicated to handle.
|
||||
|
||||
@@ -67,8 +67,11 @@ export const LocalityTab = () => {
|
||||
)}
|
||||
<EditableTable<Editable<Locality>, SpeciesDetailsType>
|
||||
columns={columns}
|
||||
editTableData={editData.now_ls.map(item =>
|
||||
!item.now_loc ? (item as unknown as Editable<Locality>) : item.now_loc
|
||||
editTableData={editData.now_ls.map(
|
||||
item =>
|
||||
!item.now_loc
|
||||
? (item as unknown as Editable<Locality>)
|
||||
: ({ ...item.now_loc, lid: item.now_loc.lid! } as never) // TODO fix type
|
||||
)}
|
||||
tableData={data.now_ls.map(item => (!item.now_loc ? (item as unknown as Editable<Locality>) : item.now_loc))}
|
||||
editable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { api } from './api'
|
||||
import { Locality, LocalityDetailsType } from '@/backendTypes'
|
||||
import { EditDataType, Locality, LocalityDetailsType } from '@/backendTypes'
|
||||
|
||||
const localitiesApi = api.injectEndpoints({
|
||||
endpoints: builder => ({
|
||||
@@ -14,7 +14,7 @@ const localitiesApi = api.injectEndpoints({
|
||||
}),
|
||||
providesTags: result => (result ? [{ type: 'locality', id: result.lid }] : []),
|
||||
}),
|
||||
editLocality: builder.mutation<LocalityDetailsType, LocalityDetailsType>({
|
||||
editLocality: builder.mutation<LocalityDetailsType, EditDataType<LocalityDetailsType>>({
|
||||
query: locality => ({
|
||||
url: `/locality/`,
|
||||
method: 'PUT',
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import { LocalityDetailsType } from '../backendTypes'
|
||||
import { EditDataType, LocalityDetailsType } from '../backendTypes'
|
||||
import { Validators, validator } from './validator'
|
||||
|
||||
export const validateLocality = (editData: LocalityDetailsType, fieldName: keyof LocalityDetailsType) => {
|
||||
const validators: Validators<Partial<LocalityDetailsType>> = {
|
||||
bfa_max_abs: {
|
||||
name: 'Basis for age (absolute)',
|
||||
export const validateLocality = (editData: EditDataType<LocalityDetailsType>, fieldName: keyof LocalityDetailsType) => {
|
||||
const validators: Validators<Partial<EditDataType<LocalityDetailsType>>> = {
|
||||
max_age: {
|
||||
name: 'Age (max)',
|
||||
asNumber: (num: number) => {
|
||||
if (num < parseInt(editData.bfa_min_abs ?? '0')) return 'Max value cannot be lower than min'
|
||||
return
|
||||
const min_age = editData.min_age
|
||||
if (!min_age) return
|
||||
if (num < parseInt(min_age)) return 'Max value cannot be lower than min'
|
||||
return null
|
||||
},
|
||||
},
|
||||
bfa_min_abs: {
|
||||
name: 'Basis for age (minimum)',
|
||||
min_age: {
|
||||
name: 'Age (min)',
|
||||
asNumber: (num: number) => {
|
||||
const bfa_max_abs = editData.bfa_max_abs
|
||||
if (!bfa_max_abs) return
|
||||
if (parseInt(bfa_max_abs) > num) return 'Min value cannot be higher than max'
|
||||
return
|
||||
const max_age = editData.max_age
|
||||
if (!max_age) return
|
||||
if (num > parseInt(max_age)) return 'Min value cannot be higher than max'
|
||||
return null
|
||||
},
|
||||
},
|
||||
loc_name: {
|
||||
@@ -27,5 +29,6 @@ export const validateLocality = (editData: LocalityDetailsType, fieldName: keyof
|
||||
},
|
||||
}
|
||||
|
||||
return validator(validators, editData, fieldName)
|
||||
const returnval = validator<LocalityDetailsType>(validators, editData, fieldName)
|
||||
return returnval
|
||||
}
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import { EditDataType } from '../backendTypes'
|
||||
|
||||
const isValidNumber = (value: string) => !Number.isNaN(parseInt(value))
|
||||
|
||||
type ValidationReturn = string | null | undefined
|
||||
|
||||
export type ValidationError = string | null | undefined
|
||||
export type ValidationObject = { name: string; error: ValidationError }
|
||||
type Validator = {
|
||||
name: string
|
||||
required?: boolean
|
||||
minLength?: number
|
||||
maxLength?: number
|
||||
asNumber?: ((num: number) => ValidationReturn) | boolean
|
||||
asString?: ((str: string) => ValidationReturn) | boolean
|
||||
asNumber?: ((num: number) => ValidationError) | boolean
|
||||
asString?: ((str: string) => ValidationError) | boolean
|
||||
}
|
||||
|
||||
export type Validators<T> = { [field in keyof T]: Validator }
|
||||
|
||||
const validate: (validator: Validator, value: unknown) => ValidationReturn = (validator: Validator, value: unknown) => {
|
||||
const validate: (validator: Validator, value: unknown) => ValidationError = (validator: Validator, value: unknown) => {
|
||||
const { required, minLength, maxLength, asNumber, asString } = validator
|
||||
if (value === null || value === undefined) return required ? 'This field is required' : null
|
||||
if (asNumber) {
|
||||
if (typeof value !== 'string') return 'Error in type' // TODO: How to do this...
|
||||
if (typeof value !== 'string') return 'Error in type' // Shouldn't happen due to editdata being all string
|
||||
if (!isValidNumber(value)) return 'Value must be a valid number'
|
||||
if (typeof asNumber === 'function') return asNumber(parseInt(value))
|
||||
return isValidNumber(value) ? 'Value must be a valid number' : null
|
||||
return null
|
||||
}
|
||||
if (asString) {
|
||||
if (typeof value !== 'string') return 'Value must be of type string'
|
||||
@@ -32,10 +34,13 @@ const validate: (validator: Validator, value: unknown) => ValidationReturn = (va
|
||||
return null
|
||||
}
|
||||
|
||||
export const validator = <T>(validators: Validators<T>, editData: T, fieldName: keyof T) => {
|
||||
export const validator = <T>(
|
||||
validators: Validators<Partial<T>>,
|
||||
editData: EditDataType<T>,
|
||||
fieldName: keyof T
|
||||
): ValidationObject => {
|
||||
const validator = validators[fieldName]
|
||||
if (validator === undefined) return null
|
||||
if (validator === undefined) return { name: fieldName as string, error: null }
|
||||
const validationError = validate(validator, editData[fieldName])
|
||||
if (!validationError) return null
|
||||
return `Field validation failed for ${validator.name}: ${validationError}`
|
||||
return { name: validator.name, error: validationError }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user