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

175
start/rules/orcid.ts Normal file
View file

@ -0,0 +1,175 @@
/*
|--------------------------------------------------------------------------
| Preloaded File - node ace make:preload rules/orcid
| Do you want to register the preload file in .adonisrc.ts file? (y/N) · true
| DONE: create start/rules/orcid.ts
| DONE: update adonisrc.ts file
|--------------------------------------------------------------------------
*/
import vine, { VineString } from '@vinejs/vine';
import { FieldContext } from '@vinejs/vine/types';
/**
* ORCID Validator Implementation
*
* Validates ORCID identifiers using both format validation and checksum verification.
* ORCID (Open Researcher and Contributor ID) is a persistent digital identifier
* that distinguishes researchers and supports automated linkages between them
* and their professional activities.
*
* Format: 0000-0000-0000-0000 (where the last digit can be X for checksum 10)
* Algorithm: MOD-11-2 checksum validation as per ISO/IEC 7064:2003
*
* @param value - The ORCID value to validate
* @param _options - Unused options parameter (required by VineJS signature)
* @param field - VineJS field context for error reporting
*/
async function orcidValidator(value: unknown, _options: undefined, field: FieldContext) {
/**
* Type guard: We only validate string values
* The "string" rule should handle type validation before this rule runs
*/
if (typeof value !== 'string') {
return;
}
/**
* Handle optional fields: Skip validation for empty strings
* This allows the field to be truly optional when used with .optional()
*/
if (value.trim() === '') {
return;
}
/**
* Normalize the ORCID value:
* - Remove any whitespace characters
* - Convert to uppercase (for potential X check digit)
*/
const cleanOrcid = value.replace(/\s/g, '').toUpperCase();
/**
* Format Validation
*
* ORCID format regex breakdown:
* ^(\d{4}-){3} - Three groups of exactly 4 digits followed by hyphen
* \d{3} - Three more digits
* [\dX]$ - Final character: either digit or 'X' (for checksum 10)
*
* Valid examples: 0000-0002-1825-0097, 0000-0002-1825-009X
*/
const orcidRegex = /^(\d{4}-){3}\d{3}[\dX]$/;
if (!orcidRegex.test(cleanOrcid)) {
field.report('ORCID must be in format: 0000-0000-0000-0000 or 0000-0000-0000-000X', 'orcid', field);
return;
}
/**
* Checksum Validation - MOD-11-2 Algorithm
*
* This implements the official ORCID checksum algorithm based on ISO/IEC 7064:2003
* to verify mathematical validity and detect typos or invalid identifiers.
*/
// Step 1: Extract digits and separate check digit
const digits = cleanOrcid.replace(/-/g, ''); // Remove hyphens: "0000000218250097"
const baseDigits = digits.slice(0, -1); // First 15 digits: "000000021825009"
const checkDigit = digits.slice(-1); // Last character: "7"
/**
* Step 2: Calculate checksum using MOD-11-2 algorithm
*
* For each digit from left to right:
* 1. Add the digit to running total
* 2. Multiply result by 2
*
* Example for "000000021825009":
* - Start with total = 0
* - Process each digit: total = (total + digit) * 2
* - Continue until all 15 digits are processed
*/
let total = 0;
for (const digit of baseDigits) {
total = (total + parseInt(digit)) * 2;
}
/**
* Step 3: Calculate expected check digit
*
* Formula: (12 - (total % 11)) % 11
* - Get remainder when total is divided by 11
* - Subtract from 12 and take modulo 11 again
* - If result is 10, use 'X' (since we need single character)
*
* Example: total = 1314
* - remainder = 1314 % 11 = 5
* - result = (12 - 5) % 11 = 7
* - expectedCheckDigit = "7"
*/
const remainder = total % 11;
const result = (12 - remainder) % 11;
const expectedCheckDigit = result === 10 ? 'X' : result.toString();
/**
* Step 4: Verify checksum matches
*
* Compare the actual check digit with the calculated expected value.
* If they don't match, the ORCID is invalid (likely contains typos or is fabricated).
*/
if (checkDigit !== expectedCheckDigit) {
field.report('Invalid ORCID checksum', 'orcid', field);
return;
}
// If we reach this point, the ORCID is valid (both format and checksum)
}
/**
* Create the VineJS validation rule
*
* This creates a reusable rule that can be chained with other VineJS validators
*/
const orcidRule = vine.createRule(orcidValidator);
/**
* TypeScript module declaration
*
* Extends the VineString interface to include our custom orcid() method.
* This enables TypeScript autocompletion and type checking when using the rule.
*/
declare module '@vinejs/vine' {
interface VineString {
/**
* Validates that a string is a valid ORCID identifier
*
* Checks both format (0000-0000-0000-0000) and mathematical validity
* using the MOD-11-2 checksum algorithm.
*
* @example
* ```typescript
* // Usage in validation schema
* identifier_orcid: vine.string().trim().maxLength(255).orcid().optional()
* ```
*
* @returns {this} The VineString instance for method chaining
*/
orcid(): this;
}
}
/**
* Register the macro with VineJS
*
* This adds the .orcid() method to all VineString instances,
* allowing it to be used in validation schemas.
*
* Usage example:
* ```typescript
* vine.string().orcid().optional()
* ```
*/
VineString.macro('orcid', function (this: VineString) {
return this.use(orcidRule());
});