SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/tasks/uniquify/people/components/CompareTable.vue

Summary

Maintainability
Test Coverage
<template>
  <div>
    <div class="horizontal-left-content align-start">
      <div class="column-buttons">
        <h2>&nbsp;</h2>
        <div class="horizontal-left-content">
          <button
            type="button"
            class="button normal-input button-default separate-right"
            :disabled="!(selected.id && selectedMergePerson.id)"
            @click="flipPeople"
          >
            Flip
          </button>
          <ConfirmationModal ref="confirmationModal" />
          <button
            class="button normal-input button-submit"
            @click="sendMerge"
            :disabled="isMergeEmpty"
          >
            Merge people
          </button>
        </div>
      </div>
      <div class="title-person">
        <h2>Selected person</h2>
        <template v-if="selectedEmpty">
          <p>This person will remain.</p>
        </template>
      </div>
      <div class="title-merge">
        <h2>Person to merge</h2>
        <template v-if="!isMergeEmpty">
          <p data-icon="warning">This person(s) will be deleted.</p>
          <switch-component
            v-model="personIndex"
            use-index
            :options="peopleList"
          />
        </template>
      </div>
    </div>
    <table>
      <tbody>
        <tr v-if="selected.id">
          <td />
          <td>
            <div class="horizontal-left-content gap-small">
              <a
                target="_blank"
                :href="`/people/${selected.id}`"
              >
                {{ selected.cached }}
              </a>
              <template v-if="selected.global_id">
                <RadialAnnotator :global-id="selected.global_id" />
                <RadialNavigation
                  :global-id="selected.global_id"
                  :redirect="false"
                  @delete="
                    () => store.dispatch(ActionNames.RemovePerson, selected)
                  "
                />
              </template>
            </div>
          </td>
          <td>
            <div class="horizontal-left-content gap-small">
              <a
                target="_blank"
                :href="`/people/${selectedMergePerson.id}`"
              >
                {{ selectedMergePerson.cached }}
              </a>
              <template v-if="selectedMergePerson.global_id">
                <RadialAnnotator :global-id="selectedMergePerson.global_id" />
                <RadialNavigation
                  :global-id="selectedMergePerson.global_id"
                  :redirect="false"
                  @delete="
                    () =>
                      store.dispatch(
                        ActionNames.RemovePerson,
                        selectedMergePerson
                      )
                  "
                />
              </template>
            </div>
          </td>
        </tr>
        <template
          v-for="(value, key, index) in selected"
          :key="key"
        >
          <tr
            v-if="!isNestedProperty(value)"
            class="contextMenuCells"
            :class="{
              even: index % 2 == 0,
              repeated:
                value !== selectedMergePerson[key] && selectedMergePerson[key]
            }"
          >
            <td
              class="column-property"
              v-text="humanizeValue(key)"
            />
            <td
              class="column-person"
              v-html="humanizeValue(value)"
            />
            <td
              class="column-merge"
              v-html="humanizeValue(selectedMergePerson[key])"
            />
          </tr>
        </template>
      </tbody>
    </table>

    <TableGrid
      :columns="2"
      :gap="12"
    >
      <div>
        <table-person-roles
          v-show="selected.id"
          title="Selected role types"
          :person="selected"
        />
        <table-annotations
          :person="selected"
          title="Selected annotations"
        />
      </div>
      <div>
        <table-person-roles
          v-show="selectedMergePerson.id"
          title="Merge role types"
          :person="selectedMergePerson"
        />
        <table-annotations
          :person="selectedMergePerson"
          title="Merge annotations"
        />
      </div>
    </TableGrid>
  </div>
</template>

<script setup>
import { computed, ref } from 'vue'
import { capitalize, humanize } from '@/helpers/strings'
import { GetterNames } from '../store/getters/getters'
import { ActionNames } from '../store/actions/actions'
import { useStore } from 'vuex'
import TableAnnotations from './Table/TableAnnotations.vue'
import TablePersonRoles from './Table/TableDescription.vue'
import RadialAnnotator from '@/components/radials/annotator/annotator'
import RadialNavigation from '@/components/radials/navigation/radial.vue'
import SwitchComponent from '@/components/ui/VSwitch'
import ConfirmationModal from '@/components/ConfirmationModal.vue'
import TableGrid from '@/components/layout/Table/TableGrid.vue'

const confirmationModal = ref(null)
const personIndex = ref(0)
const store = useStore()

const selected = computed(() => store.getters[GetterNames.GetSelectedPerson])
const selectedEmpty = computed(() => Object.keys(selected.value).length > 0)
const isMergeEmpty = computed(() => mergeList.value.length === 0)
const mergeList = computed(() => store.getters[GetterNames.GetMergePeople])
const peopleList = computed(() => mergeList.value.map((p) => p.cached))
const selectedMergePerson = computed(
  () => mergeList.value[personIndex.value] || {}
)

function isNestedProperty(value) {
  return Array.isArray(value) || (typeof value === 'object' && value != null)
}

async function sendMerge() {
  const confirmed = await confirmationModal.value.show({
    title: 'Merge people',
    message: 'This will merge all selected match people to selected person.',
    okButton: 'Merge',
    confirmationWord: 'merge',
    cancelButton: 'Cancel',
    typeButton: 'submit'
  })

  if (confirmed) {
    store.dispatch(ActionNames.ProcessMerge)
  }
}

function humanizeValue(value) {
  return capitalize(humanize(value))
}

function flipPeople() {
  store.dispatch(ActionNames.FlipPeople, personIndex.value)
}
</script>

<style lang="scss" scoped>
.repeated {
  color: red;
}
.no-difference {
  display: none;
}
.column-property {
  min-width: 105px;
}
.column-buttons {
  min-width: 136px;
}
.column-person,
.column-merge {
  min-width: 250px;
}
.title-merge,
.title-person {
  min-width: 250px;
  padding-left: 1em;
  padding-right: 1em;
}
.circle-info-project {
  border-radius: 50%;
  width: 24px;
  height: 24px;
}
.nulled {
  background-color: #e5d2be;
}
.in-project {
  background-color: #5d9ece;
}
.no-in-project {
  background-color: #c38a8a;
}
</style>