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

@ -38,32 +38,82 @@ const props = defineProps({
},
});
const emit = defineEmits(['update:modelValue']);
const computedValue = computed({
// get: () => props.modelValue,
get: () => {
// const ids = props.modelValue.map((obj) => obj.id);
// return ids;
if (Array.isArray(props.modelValue)) {
if (props.modelValue.every((item) => typeof item === 'number')) {
return props.modelValue;
} else if (props.modelValue.every((item) => hasIdAttribute(item))) {
const ids = props.modelValue.map((obj) => obj.id);
return ids;
}
return props.modelValue;
}
// return props.modelValue;
},
set: (value) => {
emit('update:modelValue', value);
},
});
// const computedValue = computed({
// // get: () => props.modelValue,
// get: () => {
// // const ids = props.modelValue.map((obj) => obj.id);
// // return ids;
// if (Array.isArray(props.modelValue)) {
// if (props.modelValue.every((item) => typeof item === 'number')) {
// return props.modelValue;
// } else if (props.modelValue.every((item) => hasIdAttribute(item))) {
// const ids = props.modelValue.map((obj) => obj.id);
// return ids;
// }
// return props.modelValue;
// }
// // return props.modelValue;
// },
// set: (value) => {
// emit('update:modelValue', value);
// },
// });
// Define a type guard to check if an object has an 'id' attribute
// function hasIdAttribute(obj: any): obj is { id: any } {
// return typeof obj === 'object' && 'id' in obj;
// }
const computedValue = computed({
get: () => {
if (!props.modelValue) return props.modelValue;
if (Array.isArray(props.modelValue)) {
// Handle empty array
if (props.modelValue.length === 0) return [];
// If all items are objects with id property
if (props.modelValue.every((item) => hasIdAttribute(item))) {
return props.modelValue.map((obj) => {
// Ensure we return the correct type based on the options keys
const id = obj.id;
// Check if options keys are numbers or strings
const optionKeys = Object.keys(props.options);
if (optionKeys.length > 0) {
// If option keys are numeric strings, return number
if (optionKeys.every(key => !isNaN(Number(key)))) {
return Number(id);
}
}
return String(id);
});
}
// If all items are numbers
if (props.modelValue.every((item) => typeof item === 'number')) {
return props.modelValue;
}
// If all items are strings that represent numbers
if (props.modelValue.every((item) => typeof item === 'string' && !isNaN(Number(item)))) {
// Convert to numbers if options keys are numeric
const optionKeys = Object.keys(props.options);
if (optionKeys.length > 0 && optionKeys.every(key => !isNaN(Number(key)))) {
return props.modelValue.map(item => Number(item));
}
return props.modelValue;
}
// Return as-is for other cases
return props.modelValue;
}
return props.modelValue;
},
set: (value) => {
emit('update:modelValue', value);
},
});
const hasIdAttribute = (obj: any): obj is { id: any } => {
return typeof obj === 'object' && 'id' in obj;
};
@ -110,7 +160,7 @@ const inputElClass = computed(() => {
</div>
<!-- <FormCheckRadio v-for="(value, key) in options" :key="key" v-model="computedValue" :type="type"
:name="name" :input-value="key" :label="value" :class="componentClass" /> -->
<FormCheckRadio v-for="(value, key) in options" :key="key" v-model="computedValue" :type="type"
<FormCheckRadio v-for="(value, key) in options" key="`${name}-${key}-${JSON.stringify(computedValue)}`" v-model="computedValue" :type="type"
:name="name" :input-value="isNaN(Number(key)) ? key : Number(key)" :label="value" :class="componentClass" />
</div>
</template>