From c901b8beae2802c134218dbcc472c81c5a77d9e2 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Thu, 6 Feb 2025 08:29:49 -0600 Subject: [PATCH 1/9] Give FO recent list fos cursor-pointer css to make them look clickable --- .../vue/tasks/field_occurrences/new/components/Recent.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue b/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue index f448343016..1838a6a2a5 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue @@ -28,7 +28,10 @@ :key="item.id" @click="selectFO(item)" > - + From c41a46e4394d84f4e0084606e2f950269f07e65a Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Thu, 6 Feb 2025 08:31:09 -0600 Subject: [PATCH 2/9] Catch FO save js error in identifier; reset isLoading on error Happens for me when I try to save a local identifier (not permitted on FOs). Note the catch on the Identifier endpoint *and* the catch on the Promise.all from the header bar are *both* required to avoid an RSOD (red screen of death). --- .../vue/tasks/field_occurrences/new/store/identifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js b/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js index d30bda3475..662c044354 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js +++ b/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js @@ -74,7 +74,7 @@ export default defineStore('identifiers', { .then(({ body }) => { this.identifier = body }) - .catch({}) + .catch(() => {}) return request } From b789881026c3ac54a113b73dd035f8737649dfc6 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Thu, 6 Feb 2025 09:13:53 -0600 Subject: [PATCH 3/9] Enforce FO total > 0 when not absent (frontend and backend) --- .../new/components/FieldOccurenceForm/FieldTotal.vue | 1 + app/models/field_occurrence.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/FieldTotal.vue b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/FieldTotal.vue index b69b5770bd..85e028eb55 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/FieldTotal.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/FieldTotal.vue @@ -3,6 +3,7 @@ diff --git a/app/models/field_occurrence.rb b/app/models/field_occurrence.rb index c3943b5393..f2d53a0269 100644 --- a/app/models/field_occurrence.rb +++ b/app/models/field_occurrence.rb @@ -63,6 +63,7 @@ class FieldOccurrence < ApplicationRecord validate :check_that_either_total_or_ranged_lot_category_id_is_present validate :check_that_both_of_category_and_total_are_not_present validate :total_zero_when_absent + validate :total_positive_when_present accepts_nested_attributes_for :collecting_event, allow_destroy: true, reject_if: :reject_collecting_event @@ -76,6 +77,10 @@ def total_zero_when_absent errors.add(:total, 'Must be zero when absent.') if (total != 0) && is_absent end + def total_positive_when_present + errors.add(:total, 'Must be positive when not absent.') if !is_absent && total.present? && total <= 0 + end + def check_that_both_of_category_and_total_are_not_present errors.add(:ranged_lot_category_id, 'Both ranged_lot_category and total can not be set') if ranged_lot_category_id.present? && total.present? end From 436a57c8752b21a4022bd1ca03df579d2c681cb7 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Mon, 10 Feb 2025 07:50:04 -0600 Subject: [PATCH 4/9] Reset "all" of store.identifier.identifier in FO New task on reset Otherwise if you save an FO and then click New, store.reset.identifier still has all of the saved data except for id. (At one point I thought not resetting store.identifier.identifier.namespace_id in particular might be an issue, but I might just be confused.) --- .../tasks/field_occurrences/new/store/identifier.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js b/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js index 662c044354..43ebb2bb0b 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js +++ b/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js @@ -41,15 +41,17 @@ export default defineStore('identifiers', { }, reset({ keepNamespace }) { - this.identifier.id = null + const newIdentifierId = this.increment + ? incrementIdentifier(this.identifier.identifier) + : null + this.identifier = { + ...makeIdentifier(), + identifier: newIdentifierId, + } if (!keepNamespace) { this.namespace = null } - - this.identifier.identifier = this.increment - ? incrementIdentifier(this.identifier.identifier) - : null }, save({ objectId, objectType }) { From 6d8a811a3351321ca8cd4f307995a64f2369ae9c Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Wed, 12 Feb 2025 22:51:14 -0600 Subject: [PATCH 5/9] Fix FO resave on identifier when only the namespace was changed STR (there are multiple ways) * save an FO with an identifier (or load a saved FO with an identifier) * change only the namespace of the identifier, either by searching, selecting, or creating new * click save * reload the page * see the namespace didn't save with the new value, it has the original value (because store.identifier.isUnsaved is never set to true in this case) I gated save on presence of identifier and namespace in addition to isUnsaved since `isUnsaved` can go to true if the user enters only a namespace or only an identifier (or enters both and then removes one), I think the resulting exception might have been swallowed previously? Not sure. --- .../Identifier/IdentifierForm.vue | 14 ++++++++------ .../Identifier/NamespaceForm.vue | 13 ++++++++++--- .../field_occurrences/new/store/identifier.js | 6 +++++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue index b3e9ae5fd9..337142f006 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue @@ -15,7 +15,10 @@ others. - +
@@ -46,7 +49,7 @@ />
Namespace is needed. @@ -102,7 +105,7 @@ function findExistingIdentifier() { if (timeOut) { clearTimeout(timeOut) } - if (store.identifier.identifier) { + if (store.identifier.identifier && store.namespace) { timeOut = setTimeout(() => { Identifier.where({ type: IDENTIFIER_LOCAL_CATALOG_NUMBER, @@ -117,9 +120,8 @@ function findExistingIdentifier() { } } -function setNamespace(namespace) { - store.namespace = namespace - store.isUnsaved = true +function namespaceSelected() { + store.identifier.isUnsaved = true findExistingIdentifier() } function unsetNamespace() { diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/NamespaceForm.vue b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/NamespaceForm.vue index 7caaaa62fa..0d3a9e4f24 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/NamespaceForm.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/NamespaceForm.vue @@ -10,12 +10,12 @@ pin-section="Namespaces" pin-type="Namespace" v-model="namespace" - @selected="(item) => (namespace = item)" + @selected="(item) => (updateNamespace(item))" > @@ -37,10 +37,17 @@ import SmartSelectorItem from '@/components/ui/SmartSelectorItem.vue' import VLock from '@/components/ui/VLock/index.vue' import useSettingStore from '../../../store/settings.js' +const emit = defineEmits(['selected']) + const namespace = defineModel({ type: Object, default: undefined }) const settings = useSettingStore() + +function updateNamespace(newNamespace) { + namespace.value = newNamespace + emit('selected') +} diff --git a/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js b/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js index 43ebb2bb0b..0161218929 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js +++ b/app/javascript/vue/tasks/field_occurrences/new/store/identifier.js @@ -55,7 +55,11 @@ export default defineStore('identifiers', { }, save({ objectId, objectType }) { - if (!this.identifier.isUnsaved) return + if (!this.identifier.isUnsaved || + !this.identifier.identifier || !this.namespace?.id + ) { + return + } const payload = { identifier: { From 8e3e9ad769de3960653966df6ae7e6184b5d8420 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Wed, 12 Feb 2025 22:54:36 -0600 Subject: [PATCH 6/9] Fix display of error when matching identifier already exists in FO --- .../components/FieldOccurenceForm/Identifier/IdentifierForm.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue index 337142f006..dfaf169b94 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/FieldOccurenceForm/Identifier/IdentifierForm.vue @@ -30,7 +30,7 @@ existingIdentifier && !isCreatedIdentifierCurrent }" type="text" - @input="checkIdentifier" + @input="findExistingIdentifier" @change="() => (store.identifier.isUnsaved = true)" v-model="store.identifier.identifier" /> From 2039f66583e6272e44c11d6ffb068b830ea5615c Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Thu, 13 Feb 2025 09:58:04 -0600 Subject: [PATCH 7/9] Remove identifier store usage from New FO Per discussion, leaving the store in-tree in case we decide to bring it back. --- .../tasks/field_occurrences/new/components/HeaderBar.vue | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/HeaderBar.vue b/app/javascript/vue/tasks/field_occurrences/new/components/HeaderBar.vue index 1b44e02e83..ccae1ee10b 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/HeaderBar.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/HeaderBar.vue @@ -88,7 +88,6 @@ import useDeterminationStore from '../store/determinations.js' import useSettingStore from '../store/settings.js' import useBiocurationStore from '../store/biocurations.js' import useBiologicalAssociationStore from '../store/biologicalAssociations.js' -import useIdentifierStore from '../store/identifier.js' import useDepictionStore from '../store/depictions.js' import VBtn from '@/components/ui/VBtn/index.vue' import VRecent from './Recent.vue' @@ -106,7 +105,6 @@ const determinationStore = useDeterminationStore() const biologicalAssociationStore = useBiologicalAssociationStore() const ceStore = useCEStore() const biocurationStore = useBiocurationStore() -const identifierStore = useIdentifierStore() const depictionStore = useDepictionStore() const fieldOccurrenceId = computed(() => foStore.fieldOccurrence.id) const isUnsaved = computed( @@ -115,8 +113,7 @@ const isUnsaved = computed( determinationStore.hasUnsaved || biocurationStore.hasUnsaved || foStore.fieldOccurrence.isUnsaved || - ceStore.isUnsaved || - identifierStore.isUnsaved + ceStore.isUnsaved ) const validateSave = computed(() => { @@ -144,7 +141,6 @@ async function save() { citationStore.save(args), determinationStore.load(args), biocurationStore.save(args), - identifierStore.save(args), biologicalAssociationStore.save(args) ] @@ -180,7 +176,6 @@ function reset() { } depictionStore.$reset() - identifierStore.reset({ keepNamespace: locked.namespace }) determinationStore.reset({ keepRecords: locked.taxonDeterminations }) citationStore.reset({ keepRecords: locked.citations }) biologicalAssociationStore.reset({ @@ -237,7 +232,6 @@ async function loadForms(id) { determinationStore.load(args), biocurationStore.load(args), citationStore.load(args), - identifierStore.load(args), biologicalAssociationStore.load(args), depictionStore.load(args) ] From d4d65f674a46831131c35d037c8bed6ae31eed0a Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Thu, 13 Feb 2025 10:41:58 -0600 Subject: [PATCH 8/9] Refactor catalog_number#assigned_to_valid_object --- app/models/identifier/local/catalog_number.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/identifier/local/catalog_number.rb b/app/models/identifier/local/catalog_number.rb index 933f8f7c2d..dea3a4e8e9 100644 --- a/app/models/identifier/local/catalog_number.rb +++ b/app/models/identifier/local/catalog_number.rb @@ -26,9 +26,15 @@ def dwc_occurrences private def assigned_to_valid_object - # TODO: unkludge - if (identifier_object_type && !(TARGETS.include?(identifier_object_type))) || ( identifier_object && !identifier_object.kind_of?(CollectionObject) && !identifier_object.kind_of?(Container) && !identifier_object.kind_of?(Extract) ) - errors.add(:identifier_object_type, "only assignable to #{TARGETS.join(', ')}") + type_issue = + identifier_object_type && !TARGETS.include?(identifier_object_type) + + object_issue = identifier_object && + TARGETS.none? { |c| identifier_object.kind_of?(c.constantize) } + + if type_issue || object_issue + errors.add(:identifier_object_type, "only assignable to #{TARGETS.join(', ')} + ") end end From c445870db77864eb13a9fb7c3104e2e860282405 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Thu, 13 Feb 2025 12:54:54 -0600 Subject: [PATCH 9/9] Make FO New Recents list selection match other recent lists --- .../new/components/Recent.vue | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue b/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue index 1838a6a2a5..d4ba7fd601 100644 --- a/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue +++ b/app/javascript/vue/tasks/field_occurrences/new/components/Recent.vue @@ -20,19 +20,30 @@ Total + - + + + + + + + + @@ -43,6 +54,7 @@ import VModal from '@/components/ui/Modal' import VSpinner from '@/components/ui/VSpinner.vue' import VBtn from '@/components/ui/VBtn/index.vue' +import VIcon from '@/components/ui/VIcon/index.vue' import { FieldOccurrence } from '@/routes/endpoints' import { ref, watch } from 'vue'