Merge pull request #1153 from nowcommunity/codex/reference-validation-display-labels

[codex] Show reference validation display labels
This commit is contained in:
Kari Lintulaakso
2026-05-18 17:00:30 +03:00
committed by GitHub
3 changed files with 98 additions and 3 deletions
@@ -10,7 +10,7 @@ import {
useDeleteReferenceMutation,
useGetReferenceTypesQuery,
} from '@/redux/referenceReducer'
import { EditDataType, ReferenceDetailsType, ValidationErrors } from '@/shared/types'
import { EditDataType, ReferenceDetailsType } from '@/shared/types'
import { emptyReference } from '../DetailView/common/defaultValues'
import {
createReferenceValidatorWithLabels,
@@ -23,6 +23,7 @@ import { createReferenceTitle } from './referenceFormatting'
import { useReturnNavigation } from '@/hooks/useReturnNavigation'
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'
import type { SerializedError } from '@reduxjs/toolkit'
import { formatReferenceValidationErrorMessage } from './referenceValidationErrors'
const REFERENCE_DELETE_CONFLICT_MESSAGE = 'The Reference with associated updates cannot be deleted.'
const GENERIC_DELETE_MESSAGE = 'Could not delete item. Error happened.'
@@ -101,8 +102,8 @@ export const ReferenceDetails = ({
notify('Saved reference successfully.')
setTimeout(() => navigate(`/reference/${rid}`), 15)
} catch (e) {
const error = e as ValidationErrors
notify('Following validators failed: ' + error.data.map(e => e.name).join(', '), 'error')
const message = formatReferenceValidationErrorMessage(e, editData.ref_type_id, referenceFieldDisplayLabelMap)
notify(message, 'error')
}
}
@@ -0,0 +1,48 @@
import type { EditDataType, ReferenceDetailsType, ValidationErrors } from '@/shared/types'
import type { ReferenceDisplayLabelMap } from '@/shared/validators/reference'
const GENERIC_REFERENCE_SAVE_ERROR = 'Could not edit item. Error happened.'
const GROUPED_REQUIRED_PREFIX = 'At least one of the following fields is required:'
const isValidationErrors = (error: unknown): error is ValidationErrors => {
if (typeof error !== 'object' || error === null || !('data' in error)) {
return false
}
const possibleError = error as { data?: unknown }
return Array.isArray(possibleError.data)
}
const getReferenceFieldDisplayLabel = (
fieldName: string,
refTypeId: number | null | undefined,
displayLabelMap?: ReferenceDisplayLabelMap
) => {
if (!refTypeId) return fieldName
const fieldLabels = displayLabelMap?.[refTypeId]
return fieldLabels?.[fieldName as keyof EditDataType<ReferenceDetailsType>] ?? fieldName
}
export const formatReferenceValidationErrorMessage = (
error: unknown,
refTypeId: number | null | undefined,
displayLabelMap?: ReferenceDisplayLabelMap
) => {
if (!isValidationErrors(error) || error.data.length === 0) {
return GENERIC_REFERENCE_SAVE_ERROR
}
const messages = error.data.map(validationError => {
const errorText = validationError.error.trim()
if (errorText.startsWith(GROUPED_REQUIRED_PREFIX)) {
return errorText
}
const fieldLabel = getReferenceFieldDisplayLabel(validationError.name, refTypeId, displayLabelMap)
return errorText.length > 0 ? `${fieldLabel}: ${errorText}` : fieldLabel
})
return `Following validators failed: ${[...new Set(messages)].join(', ')}`
}
@@ -0,0 +1,46 @@
import { describe, expect, it } from '@jest/globals'
import { formatReferenceValidationErrorMessage } from '@/components/Reference/referenceValidationErrors'
describe('formatReferenceValidationErrorMessage', () => {
it('uses reference display labels for backend validation field names', () => {
const message = formatReferenceValidationErrorMessage(
{
status: '403',
data: [
{ name: 'title_primary', error: 'This field is required' },
{ name: 'gen_notes', error: 'This field is required' },
],
},
4,
{
4: {
title_primary: 'Title',
gen_notes: 'Notes',
},
}
)
expect(message).toBe('Following validators failed: Title: This field is required, Notes: This field is required')
})
it('deduplicates grouped title errors that already contain display labels', () => {
const groupedError = 'At least one of the following fields is required: Title, Notes'
const message = formatReferenceValidationErrorMessage(
{
status: '403',
data: [
{ name: 'title_primary', error: groupedError },
{ name: 'gen_notes', error: groupedError },
],
},
4
)
expect(message).toBe(`Following validators failed: ${groupedError}`)
})
it('falls back to a generic save error for unexpected responses', () => {
expect(formatReferenceValidationErrorMessage({ status: 500 }, 4)).toBe('Could not edit item. Error happened.')
})
})