SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/components/Filter/Facets/shared/ByAttribute.vue

Summary

Maintainability
Test Coverage
<template>
  <div class="horizontal-left-content align-start">
    <div class="field separate-right full_width">
      <label>Field</label>
      <br />
      <select
        class="normal-input full_width"
        v-model="selectedField"
      >
        <template
          v-for="field in fields"
          :key="field.name"
        >
          <option
            v-if="!selectedFields.find((item) => item.param === field.name)"
            :value="field"
          >
            {{ field.name }}
          </option>
        </template>
      </select>
    </div>
  </div>
  <AttributeForm
    v-if="selectedField"
    class="horizontal-left-content"
    :field="selectedField"
    @add="addField"
  />

  <div v-if="selectedFields.length">
    <table class="full_width">
      <thead>
        <tr>
          <th>Field</th>
          <th>Value</th>
          <th>Exact</th>
          <th />
        </tr>
      </thead>
      <tbody>
        <tr
          v-for="(field, index) in selectedFields"
          :key="field.param"
        >
          <td>{{ field.param }}</td>
          <td>{{ field.value }}</td>
          <td>
            <input
              v-if="checkForMatch(field.type) && !field.any && field.value"
              v-model="field.exact"
              type="checkbox"
            />
            <template v-else>
              <span v-if="field.any">Any</span>
              <span v-else>Empty</span>
            </template>
          </td>
          <td>
            <span
              class="button circle-button btn-delete button-default"
              @click="removeField(index)"
            />
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script setup>
import { ref, watch, computed, onBeforeMount } from 'vue'
import ajaxCall from '@/helpers/ajaxCall'
import AttributeForm from '@/components/Filter/Facets/CollectingEvent/FacetCollectingEvent/AttributeForm.vue'

const props = defineProps({
  modelValue: {
    type: Object,
    required: true
  },

  controller: {
    type: String,
    required: true
  },

  exclude: {
    type: Array,
    default: () => []
  }
})

const emit = defineEmits(['update:modelValue'])

const params = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

const fields = ref([])
const selectedFields = ref([])
const selectedField = ref(undefined)

watch(
  selectedFields,
  (newVal) => {
    const attributes = {}
    const matches = newVal
      .filter((item) => !item.exact && !item.any && item.value)
      .map((item) => item.param)

    params.value.any_value_attribute = newVal
      .filter((item) => item.any)
      .map((item) => item.param)

    params.value.no_value_attribute = newVal
      .filter((item) => !item.value && !item.any)
      .map((item) => item.param)

    params.value.wildcard_attribute = matches

    fields.value.forEach(({ name }) => {
      attributes[name] = undefined
    })

    newVal.forEach(({ param, value }) => {
      attributes[param] = value
    })

    Object.assign(params.value, attributes)
  },
  { deep: true }
)

watch(
  () => props.modelValue,
  (newVal) => {
    const parameters = Object.keys(newVal)
    const isAttributeSetted = fields.value.some(({ name }) =>
      parameters.includes(name)
    )

    if (!parameters.includes('wildcard_attribute') && !isAttributeSetted) {
      selectedFields.value = []
    }
  }
)

onBeforeMount(() => {
  ajaxCall('get', `/${props.controller}/attributes`).then((response) => {
    fields.value = response.body.filter(
      ({ name }) => !props.exclude.includes(name)
    )

    fields.value.forEach(({ name, type }) => {
      const value = params.value[name]
      const any = params.value.any_value_attribute?.includes(name)
      const exact = !params.value.wildcard_attribute?.includes(name)
      const noValue = params.value.no_value_attribute?.includes(name)

      if (value === undefined && !noValue && !any) {
        return
      }

      const field = {
        param: name,
        type,
        any
      }

      selectedFields.value.push(
        any
          ? field
          : {
              ...field,
              value,
              exact
            }
      )
    })
  })
})

const addField = (field) => {
  selectedFields.value.push(field)
  selectedField.value = undefined
}

const removeField = (index) => {
  selectedFields.value.splice(index, 1)
}

const checkForMatch = (type) => {
  return type === 'string' || type === 'text'
}
</script>