fix: resolve LibrarySelectionField validation error for non-admin users

Fixed validation error 'At least one library must be selected for non-admin users' that appeared even when libraries were selected. The issue was caused by a data format mismatch between backend and frontend.

The backend sends user data with libraries as an array of objects, but the LibrarySelectionField component expects libraryIds as an array of IDs. Added data transformation in the data provider's getOne method to automatically convert libraries array to libraryIds format when fetching user records.

Also extracted validation logic into a separate userValidation module for better code organization and added comprehensive test coverage to prevent similar issues.
This commit is contained in:
Deluan 2025-07-16 17:50:29 -04:00
commit b9386ab41a
4 changed files with 107 additions and 14 deletions

View file

@ -139,7 +139,19 @@ const wrapperDataProvider = {
},
getOne: (resource, params) => {
const [r, p] = mapResource(resource, params)
return dataProvider.getOne(r, p)
const response = dataProvider.getOne(r, p)
// Transform user data to ensure libraryIds is present for form compatibility
if (resource === 'user') {
return response.then((result) => {
if (result.data.libraries && Array.isArray(result.data.libraries)) {
result.data.libraryIds = result.data.libraries.map((lib) => lib.id)
}
return result
})
}
return response
},
getMany: (resource, params) => {
const [r, p] = mapResource(resource, params)

View file

@ -18,11 +18,13 @@ import {
useRefresh,
FormDataConsumer,
usePermissions,
useRecordContext,
} from 'react-admin'
import { Typography } from '@material-ui/core'
import { Title } from '../common'
import DeleteUserButton from './DeleteUserButton'
import { LibrarySelectionField } from './LibrarySelectionField.jsx'
import { validateUserForm } from './userValidation'
const useStyles = makeStyles({
toolbar: {
@ -103,18 +105,8 @@ const UserEdit = (props) => {
)
// Custom validation function
const validateUserForm = (values) => {
const errors = {}
// Only require library selection for non-admin users
if (
!values.isAdmin &&
(!values.libraryIds || values.libraryIds.length === 0)
) {
errors.libraryIds = translate(
'resources.user.validation.librariesRequired',
)
}
return errors
const validateForm = (values) => {
return validateUserForm(values, translate)
}
return (
@ -123,7 +115,7 @@ const UserEdit = (props) => {
variant={'outlined'}
toolbar={<UserToolbar showDelete={canDelete} />}
save={save}
validate={validateUserForm}
validate={validateForm}
>
{permissions === 'admin' && (
<TextInput

View file

@ -0,0 +1,19 @@
// User form validation utilities
export const validateUserForm = (values, translate) => {
const errors = {}
// Only require library selection for non-admin users
if (!values.isAdmin) {
// Check both libraryIds (array of IDs) and libraries (array of objects)
const hasLibraryIds = values.libraryIds && values.libraryIds.length > 0
const hasLibraries = values.libraries && values.libraries.length > 0
if (!hasLibraryIds && !hasLibraries) {
errors.libraryIds = translate(
'resources.user.validation.librariesRequired',
)
}
}
return errors
}

View file

@ -0,0 +1,70 @@
import { describe, it, expect, vi } from 'vitest'
import { validateUserForm } from './userValidation'
describe('User Validation Utilities', () => {
const mockTranslate = vi.fn((key) => key)
describe('validateUserForm', () => {
it('should not return errors for admin users', () => {
const values = {
isAdmin: true,
libraryIds: [],
}
const errors = validateUserForm(values, mockTranslate)
expect(errors).toEqual({})
})
it('should not return errors for non-admin users with libraries', () => {
const values = {
isAdmin: false,
libraryIds: [1, 2, 3],
}
const errors = validateUserForm(values, mockTranslate)
expect(errors).toEqual({})
})
it('should return error for non-admin users without libraries', () => {
const values = {
isAdmin: false,
libraryIds: [],
}
const errors = validateUserForm(values, mockTranslate)
expect(errors.libraryIds).toBe(
'resources.user.validation.librariesRequired',
)
})
it('should return error for non-admin users with undefined libraryIds', () => {
const values = {
isAdmin: false,
}
const errors = validateUserForm(values, mockTranslate)
expect(errors.libraryIds).toBe(
'resources.user.validation.librariesRequired',
)
})
it('should not return errors for non-admin users with libraries array', () => {
const values = {
isAdmin: false,
libraries: [
{ id: 1, name: 'Library 1' },
{ id: 2, name: 'Library 2' },
],
}
const errors = validateUserForm(values, mockTranslate)
expect(errors).toEqual({})
})
it('should return error for non-admin users with empty libraries array', () => {
const values = {
isAdmin: false,
libraries: [],
}
const errors = validateUserForm(values, mockTranslate)
expect(errors.libraryIds).toBe(
'resources.user.validation.librariesRequired',
)
})
})
})