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:
parent
06ed2f3625
commit
8f67839f93
16 changed files with 2657 additions and 1168 deletions
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, watch, ref } from 'vue';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
|
@ -13,32 +13,138 @@ const props = defineProps<Props>();
|
|||
|
||||
const emit = defineEmits<{ (e: 'update:modelValue', value: Props['modelValue']): void }>();
|
||||
|
||||
// const computedValue = computed({
|
||||
// get: () => props.modelValue,
|
||||
// set: (value) => {
|
||||
// emit('update:modelValue', props.type === 'radio' ? [value] : value);
|
||||
// },
|
||||
// });
|
||||
const computedValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => {
|
||||
emit('update:modelValue', props.type === 'radio' ? [value] : value);
|
||||
get: () => {
|
||||
if (props.type === 'radio') {
|
||||
// For radio buttons, return boolean indicating if this option is selected
|
||||
if (Array.isArray(props.modelValue)) {
|
||||
return props.modelValue;
|
||||
}
|
||||
return [props.modelValue];
|
||||
} else {
|
||||
// For checkboxes, return boolean indicating if this option is included
|
||||
if (Array.isArray(props.modelValue)) {
|
||||
return props.modelValue.includes(props.inputValue);
|
||||
}
|
||||
return props.modelValue == props.inputValue;
|
||||
}
|
||||
},
|
||||
set: (value: boolean) => {
|
||||
if (props.type === 'radio') {
|
||||
// When radio is selected, emit the new value as array
|
||||
emit('update:modelValue', [value]);
|
||||
} else {
|
||||
// Handle checkboxes
|
||||
let updatedValue = Array.isArray(props.modelValue) ? [...props.modelValue] : [];
|
||||
if (value) {
|
||||
if (!updatedValue.includes(props.inputValue)) {
|
||||
updatedValue.push(props.inputValue);
|
||||
}
|
||||
} else {
|
||||
updatedValue = updatedValue.filter(item => item != props.inputValue);
|
||||
}
|
||||
emit('update:modelValue', updatedValue);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const inputType = computed(() => (props.type === 'radio' ? 'radio' : 'checkbox'));
|
||||
|
||||
// Define isChecked for radio inputs: it's true when the current modelValue equals the inputValue
|
||||
const isChecked = computed(() => {
|
||||
if (Array.isArray(computedValue.value) && computedValue.value.length > 0) {
|
||||
return props.type === 'radio'
|
||||
? computedValue.value[0] === props.inputValue
|
||||
: computedValue.value.includes(props.inputValue);
|
||||
// const isChecked = computed(() => {
|
||||
// if (Array.isArray(computedValue.value) && computedValue.value.length > 0) {
|
||||
// return props.type === 'radio'
|
||||
// ? computedValue.value[0] === props.inputValue
|
||||
// : computedValue.value.includes(props.inputValue);
|
||||
// }
|
||||
// return computedValue.value === props.inputValue;
|
||||
// });
|
||||
// const isChecked = computed(() => {
|
||||
// return computedValue.value[0] === props.inputValue;
|
||||
// });
|
||||
// Fix the isChecked computation with proper type handling
|
||||
// const isChecked = computed(() => {
|
||||
// if (props.type === 'radio') {
|
||||
// // Use loose equality to handle string/number conversion
|
||||
// return computedValue.value == props.inputValue;
|
||||
// }
|
||||
// return computedValue.value === true;
|
||||
// });
|
||||
|
||||
// const isChecked = computed(() => {
|
||||
// if (props.type === 'radio') {
|
||||
// if (Array.isArray(props.modelValue)) {
|
||||
// return props.modelValue.length > 0 && props.modelValue[0] == props.inputValue;
|
||||
// }
|
||||
// return props.modelValue == props.inputValue;
|
||||
// }
|
||||
|
||||
// // For checkboxes
|
||||
// if (Array.isArray(props.modelValue)) {
|
||||
// return props.modelValue.includes(props.inputValue);
|
||||
// }
|
||||
// return props.modelValue == props.inputValue;
|
||||
// });
|
||||
// Use a ref for isChecked and update it with a watcher
|
||||
const isChecked = ref(false);
|
||||
// Calculate initial isChecked value
|
||||
const calculateIsChecked = () => {
|
||||
if (props.type === 'radio') {
|
||||
if (Array.isArray(props.modelValue)) {
|
||||
return props.modelValue.length > 0 && props.modelValue[0] == props.inputValue;
|
||||
}
|
||||
return props.modelValue == props.inputValue;
|
||||
}
|
||||
return computedValue.value === props.inputValue;
|
||||
});
|
||||
|
||||
// For checkboxes
|
||||
if (Array.isArray(props.modelValue)) {
|
||||
return props.modelValue.includes(props.inputValue);
|
||||
}
|
||||
return props.modelValue == props.inputValue;
|
||||
};
|
||||
|
||||
// Set initial value
|
||||
isChecked.value = calculateIsChecked();
|
||||
|
||||
// Watch for changes in modelValue and recalculate isChecked
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
console.log('modelValue changed:', {
|
||||
newValue,
|
||||
inputValue: props.inputValue,
|
||||
type: props.type
|
||||
});
|
||||
isChecked.value = calculateIsChecked();
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
// Also watch inputValue in case it changes
|
||||
watch(
|
||||
() => props.inputValue,
|
||||
() => {
|
||||
isChecked.value = calculateIsChecked();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<label v-if="type === 'radio'" :class="[type]"
|
||||
<label v-if="type === 'radio'" :class="[type]"
|
||||
class="mr-6 mb-3 last:mr-0 inline-flex items-center cursor-pointer relative">
|
||||
<input v-model="computedValue" :type="inputType" :name="name" :value="inputValue"
|
||||
class="absolute left-0 opacity-0 -z-1 focus:outline-none focus:ring-0"
|
||||
:checked="isChecked" />
|
||||
<input
|
||||
v-model="computedValue"
|
||||
:type="inputType"
|
||||
:name="name"
|
||||
:value="inputValue"
|
||||
class="absolute left-0 opacity-0 -z-1 focus:outline-none focus:ring-0" />
|
||||
<span class="check border transition-colors duration-200 dark:bg-slate-800 block w-5 h-5 rounded-full" :class="{
|
||||
'border-gray-700': !isChecked,
|
||||
'bg-radio-checked bg-no-repeat bg-center bg-lime-600 border-lime-600 border-4': isChecked
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue