hot-fix: Add ORCID validation and improve dataset editing UX

### Major Features
- Add comprehensive ORCID validation with checksum verification
- Implement unsaved changes detection and auto-save functionality
- Enhanced form component reactivity and state management

### ORCID Implementation
- Create custom VineJS ORCID validation rule with MOD-11-2 algorithm
- Add ORCID fields to Person model and TablePersons component
- Update dataset validators to include ORCID validation
- Add descriptive placeholder text for ORCID input fields

### UI/UX Improvements
- Add UnsavedChangesWarning component with detailed change tracking
- Improve FormCheckRadio and FormCheckRadioGroup reactivity
- Enhanced BaseButton with proper disabled state handling
- Better error handling and user feedback in file validation

### Data Management
- Implement sophisticated change detection for all dataset fields
- Add proper handling of array ordering for authors/contributors
- Improve license selection with better state management
- Enhanced subject/keyword processing with duplicate detection

### Technical Improvements
- Optimize search indexing with conditional updates based on modification dates
- Update person model column mapping for ORCID
- Improve validation error messages and user guidance
- Better handling of file uploads and deletion tracking

### Dependencies
- Update various npm packages (AWS SDK, Babel, Vite, etc.)
- Add baseline-browser-mapping for better browser compatibility

### Bug Fixes
- Fix form reactivity issues with checkbox/radio groups
- Improve error handling in file validation rules
- Better handling of edge cases in change detection
This commit is contained in:
Kaimbacher 2025-09-15 14:07:59 +02:00
parent 06ed2f3625
commit 8f67839f93
16 changed files with 2657 additions and 1168 deletions

View file

@ -2,7 +2,7 @@
|--------------------------------------------------------------------------
| Preloaded File - node ace make:preload rules/dependentArrayMinLength
|--------------------------------------------------------------------------
|*/
*/
import { FieldContext } from '@vinejs/vine/types';
import vine, { VineArray } from '@vinejs/vine';
@ -17,39 +17,75 @@ type Options = {
};
async function dependentArrayMinLength(value: unknown, options: Options, field: FieldContext) {
const fileInputs = field.data[options.dependentArray]; // Access the dependent array
const isArrayValue = Array.isArray(value);
const isArrayFileInputs = Array.isArray(fileInputs);
if (isArrayValue && isArrayFileInputs) {
if (value.length >= options.min) {
return true; // Valid if the main array length meets the minimum
} else if (value.length === 0 && fileInputs.length >= options.min) {
return true; // Valid if the main array is empty and the dependent array meets the minimum
} else {
field.report(
`At least {{ min }} item for {{field}} field must be defined`,
'array.dependentArrayMinLength',
field,
options,
);
}
} else {
// Report if either value or dependentArray is not an array
const dependentArrayValue = field.data[options.dependentArray];
// Both values can be null/undefined or arrays, but not other types
const isMainValueValid = value === null || value === undefined || Array.isArray(value);
const isDependentValueValid = dependentArrayValue === null || dependentArrayValue === undefined || Array.isArray(dependentArrayValue);
if (!isMainValueValid || !isDependentValueValid) {
field.report(
`Both the {{field}} field and dependent array {{dependentArray}} must be arrays.`,
`Invalid file data format. Please contact support if this error persists.`,
'array.dependentArrayMinLength',
field,
options,
);
return false;
}
// Convert null/undefined to empty arrays for length checking
const mainArray = Array.isArray(value) ? value : [];
const dependentArray = Array.isArray(dependentArrayValue) ? dependentArrayValue : [];
// Calculate total count across both arrays
const totalCount = mainArray.length + dependentArray.length;
// Check if minimum requirement is met
if (totalCount >= options.min) {
return true;
}
// Special case: if dependent array has items, main array can be empty/null
if (dependentArray.length >= options.min && mainArray.length === 0) {
return true;
}
// Determine appropriate error message based on context
const hasExistingFiles = dependentArray.length > 0;
const hasNewFiles = mainArray.length > 0;
if (!hasExistingFiles && !hasNewFiles) {
// No files at all
field.report(
`Your dataset must include at least {{ min }} file. Please upload a new file to continue.`,
'array.dependentArrayMinLength',
field,
options,
);
} else if (hasExistingFiles && !hasNewFiles && dependentArray.length < options.min) {
// Has existing files but marked for deletion, no new files
field.report(
`You have marked all existing files for deletion. Please upload at least {{ min }} new file or keep some existing files.`,
'array.dependentArrayMinLength',
field,
options,
);
} else {
// Generic fallback message
field.report(
`Your dataset must have at least {{ min }} file. You can either upload new files or keep existing ones.`,
'array.dependentArrayMinLength',
field,
options,
);
}
return false; // Invalid if none of the conditions are met
return false;
}
export const dependentArrayMinLengthRule = vine.createRule(dependentArrayMinLength);
// Extend the VineArray interface with the same type parameters
// Extend the VineArray interface
declare module '@vinejs/vine' {
interface VineArray<Schema extends SchemaTypes> {
dependentArrayMinLength(options: Options): this;
@ -58,4 +94,4 @@ declare module '@vinejs/vine' {
VineArray.macro('dependentArrayMinLength', function <Schema extends SchemaTypes>(this: VineArray<Schema>, options: Options) {
return this.use(dependentArrayMinLengthRule(options));
});
});