SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/tasks/nomenclature/filter/components/filters/FacetRelationships.vue

Summary

Maintainability
Test Coverage
<template>
  <FacetContainer>
    <h3>Relationships</h3>
    <smart-selector
      v-if="taxon"
      class="separate-bottom"
      :options="options"
      v-model="view"
    />
    <div>
      <SmartSelectorItem
        v-if="taxon"
        :item="taxon"
        label="object_tag"
        @unset="taxon = undefined"
      />
      <autocomplete
        v-else
        placeholder="Select a taxon name for the relationship"
        url="/taxon_names/autocomplete"
        param="term"
        label="label_html"
        clear-after
        @get-item="loadTaxonName"
      />
    </div>
    <div
      class="separate-top"
      v-if="taxon"
    >
      <tree-display
        v-if="view == smartOptions.all"
        @close="view = undefined"
        :object-lists="mergeLists"
        modal-title="Relationships"
        :display="display"
        @selected="addRelationshipType"
      />
      <ul
        v-if="view == smartOptions.common"
        class="no_bullets"
      >
        <li
          v-for="(item, key) in mergeLists.common"
          :key="key"
        >
          <label>
            <input
              :value="key"
              @click="
                () => {
                  item.type = key
                  addRelationshipType(item)
                }
              "
              type="radio"
            />
            {{ item[display] }}
          </label>
        </li>
      </ul>
      <autocomplete
        v-if="view == smartOptions.advanced"
        url=""
        :array-list="
          Object.keys(mergeLists.all).map((key) => {
            mergeLists.all[key].type = key
            return mergeLists.all[key]
          })
        "
        :label="display"
        :clear-after="true"
        min="3"
        time="0"
        @get-item="addRelationshipType"
        placeholder="Search"
        event-send="autocompleteRelationshipSelected"
        param="term"
      />
    </div>
    <list-component
      :list="relationships"
      @flip="flipRelationship"
      @delete="removeItem"
    />
  </FacetContainer>
</template>

<script>
import SmartSelectorItem from '@/components/ui/SmartSelectorItem.vue'
import FacetContainer from '@/components/Filter/Facets/FacetContainer.vue'
import SmartSelector from '@/components/ui/VSwitch'
import TreeDisplay from '../treeDisplay'
import Autocomplete from '@/components/ui/Autocomplete'
import ListComponent from '../relationshipsList'
import { URLParamsToJSON } from '@/helpers/url/parse.js'
import { TaxonName, TaxonNameRelationship } from '@/routes/endpoints'

const OPTIONS = {
  common: 'common',
  advanced: 'advanced',
  all: 'all'
}

export default {
  components: {
    SmartSelector,
    TreeDisplay,
    Autocomplete,
    ListComponent,
    FacetContainer,
    SmartSelectorItem
  },

  props: {
    modelValue: {
      type: Object,
      default: () => ({})
    }
  },

  emits: ['update:modelValue'],

  computed: {
    smartOptions() {
      return OPTIONS
    },

    params: {
      get() {
        return this.modelValue
      },
      set(value) {
        this.$emit('update:modelValue', value)
      }
    }
  },
  data() {
    return {
      options: Object.values(OPTIONS),
      lists: [],
      paramRelationships: undefined,
      view: OPTIONS.common,
      relationshipsList: {},
      mergeLists: {},
      showAll: false,
      relationships: [],
      taxon: undefined,
      display: 'subject_status_tag',
      types: [
        {
          label: 'as subject',
          value: 'subject_status_tag'
        },
        {
          label: 'as object',
          value: 'object_status_tag'
        }
      ],
      typeSelected: undefined
    }
  },
  watch: {
    modelValue(newVal) {
      if (newVal?.taxon_name_relationship?.length || !this.relationships.length)
        return
      this.taxon = undefined
      this.typeSelected = undefined
      this.relationships = []
    },

    relationships: {
      handler() {
        const newList = this.relationships.map((item) => {
          const name =
            item.type_name === 'subject_status_tag'
              ? 'subject_taxon_name_id'
              : 'object_taxon_name_id'

          return {
            type: item.type,
            [name]: item.taxonId
          }
        })

        this.params.taxon_name_relationship = newList
      },
      deep: true
    }
  },

  created() {
    TaxonNameRelationship.types().then((response) => {
      this.relationshipsList = response.body
      this.merge()

      const params = URLParamsToJSON(location.href)

      if (params.taxon_name_relationship) {
        Object.values(params.taxon_name_relationship).forEach(
          (relationship) => {
            const isSubject = relationship.hasOwnProperty(
              'subject_taxon_name_id'
            )
            const typeName = isSubject
              ? 'subject_status_tag'
              : 'object_status_tag'
            const taxonId =
              relationship[
                isSubject ? 'subject_taxon_name_id' : 'object_taxon_name_id'
              ]
            TaxonName.find(taxonId).then((taxon) => {
              this.relationships.push({
                type_object: this.mergeLists.all[relationship.type],
                type: relationship.type,
                taxon_label: taxon.body.name,
                type_label:
                  this.mergeLists.all[relationship.type][
                    isSubject ? 'subject_status_tag' : 'object_status_tag'
                  ],
                type_name: typeName,
                taxonId
              })
            })
          }
        )
      }
    })
  },
  methods: {
    loadTaxonName(taxon) {
      TaxonName.find(taxon.id).then((response) => {
        this.taxon = response.body
        this.merge()
      })
    },

    merge() {
      const relationshipsList = JSON.parse(
        JSON.stringify(this.relationshipsList)
      )
      const nomenclatureCode = this.taxon?.nomenclatural_code
      const newList = {
        all: {},
        common: {},
        tree: {}
      }
      const nomenclatureCodes = nomenclatureCode
        ? [nomenclatureCode]
        : Object.keys(relationshipsList)

      nomenclatureCodes.forEach((key) => {
        newList.all = Object.assign(newList.all, relationshipsList[key].all)
        newList.tree = Object.assign(newList.tree, relationshipsList[key].tree)
        for (const keyType in relationshipsList[key].common) {
          relationshipsList[key].common[
            keyType
          ].subject_status_tag = `${relationshipsList[key].common[keyType].subject_status_tag} (${key})`
        }
        newList.common = Object.assign(
          newList.common,
          relationshipsList[key].common
        )
      })
      this.getTreeList(newList.tree, newList.all)
      this.mergeLists = newList
    },

    getTreeList(list, ranksList) {
      for (const key in list) {
        if (key in ranksList) {
          Object.defineProperty(list[key], 'type', {
            writable: true,
            value: key
          })
          Object.defineProperty(list[key], 'object_status_tag', {
            writable: true,
            value: ranksList[key].object_status_tag
          })
          Object.defineProperty(list[key], 'subject_status_tag', {
            writable: true,
            value: ranksList[key].subject_status_tag
          })
          Object.defineProperty(list[key], 'valid_subject_ranks', {
            writable: true,
            value: ranksList[key].valid_subject_ranks
          })
        }
        this.getTreeList(list[key], ranksList)
      }
    },

    addRelationshipType(relationship) {
      this.view = undefined
      this.typeSelected = relationship
      this.addRelationship()
    },

    addRelationship() {
      this.relationships.push({
        type_object: this.typeSelected,
        type: this.typeSelected.type,
        taxon_label: this.taxon.label,
        type_label: this.typeSelected[this.display],
        type_name: this.display,
        taxonId: this.taxon.id
      })
      this.typeSelected = undefined
      this.taxon = undefined
      this.view = OPTIONS.common
    },

    removeItem(index) {
      this.relationships.splice(index, 1)
    },

    flipRelationship(relationship) {
      const index = this.relationships.findIndex(
        (item) =>
          item.type == relationship.type &&
          item.type_name == relationship.type_name
      )
      const flipType =
        relationship.type_name === 'subject_status_tag'
          ? 'object_status_tag'
          : 'subject_status_tag'
      relationship.type_name = flipType
      relationship.type_label = relationship.type_object[flipType]
      this.relationships[index] = relationship
    }
  }
}
</script>
<style scoped>
:deep(.vue-autocomplete-input) {
  width: 100%;
}

.type-list {
  min-width: 84px;
}
</style>