mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 04:57:51 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			204 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <div class="flex flex-col w-full max-h-[12.5rem]">
 | 
						|
    <div class="flex items-center justify-center mb-1">
 | 
						|
      <h4
 | 
						|
        class="text-sm text-slate-800 dark:text-slate-100 m-0 overflow-hidden whitespace-nowrap text-ellipsis flex-grow"
 | 
						|
      >
 | 
						|
        {{ $t('CONTACT_PANEL.LABELS.LABEL_SELECT.TITLE') }}
 | 
						|
      </h4>
 | 
						|
      <hotkey
 | 
						|
        custom-class="text-slate-800 dark:text-slate-100 bg-slate-50 dark:bg-slate-600 text-xxs border border-solid border-slate-75 dark:border-slate-600"
 | 
						|
      >
 | 
						|
        L
 | 
						|
      </hotkey>
 | 
						|
    </div>
 | 
						|
    <div class="mb-2 flex-shrink-0 flex-grow-0 flex-auto max-h-8">
 | 
						|
      <input
 | 
						|
        ref="searchbar"
 | 
						|
        v-model="search"
 | 
						|
        type="text"
 | 
						|
        class="search-input"
 | 
						|
        autofocus="true"
 | 
						|
        :placeholder="$t('CONTACT_PANEL.LABELS.LABEL_SELECT.PLACEHOLDER')"
 | 
						|
      />
 | 
						|
    </div>
 | 
						|
    <div
 | 
						|
      class="flex justify-start items-start flex-grow flex-shrink flex-auto overflow-auto"
 | 
						|
    >
 | 
						|
      <div class="w-full">
 | 
						|
        <woot-dropdown-menu>
 | 
						|
          <label-dropdown-item
 | 
						|
            v-for="label in filteredActiveLabels"
 | 
						|
            :key="label.title"
 | 
						|
            :title="label.title"
 | 
						|
            :color="label.color"
 | 
						|
            :selected="selectedLabels.includes(label.title)"
 | 
						|
            @click="onAddRemove(label)"
 | 
						|
          />
 | 
						|
        </woot-dropdown-menu>
 | 
						|
        <div
 | 
						|
          v-if="noResult"
 | 
						|
          class="flex justify-center py-4 px-2.5 font-medium text-xs text-slate-700 dark:text-slate-200"
 | 
						|
        >
 | 
						|
          {{ $t('CONTACT_PANEL.LABELS.LABEL_SELECT.NO_RESULT') }}
 | 
						|
        </div>
 | 
						|
        <div
 | 
						|
          v-if="allowCreation && shouldShowCreate"
 | 
						|
          class="flex pt-1 border-t border-solid border-slate-100 dark:border-slate-900"
 | 
						|
        >
 | 
						|
          <woot-button
 | 
						|
            size="small"
 | 
						|
            variant="clear"
 | 
						|
            color-scheme="secondary"
 | 
						|
            icon="add"
 | 
						|
            is-expanded
 | 
						|
            class="button-new-label"
 | 
						|
            :is-disabled="hasExactMatchInResults"
 | 
						|
            @click="showCreateModal"
 | 
						|
          >
 | 
						|
            {{ createLabelPlaceholder }}
 | 
						|
            {{ parsedSearch }}
 | 
						|
          </woot-button>
 | 
						|
 | 
						|
          <woot-modal
 | 
						|
            :show.sync="createModalVisible"
 | 
						|
            :on-close="hideCreateModal"
 | 
						|
          >
 | 
						|
            <add-label-modal
 | 
						|
              :prefill-title="parsedSearch"
 | 
						|
              @close="hideCreateModal"
 | 
						|
            />
 | 
						|
          </woot-modal>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
 | 
						|
<script>
 | 
						|
import LabelDropdownItem from './LabelDropdownItem.vue';
 | 
						|
import Hotkey from 'dashboard/components/base/Hotkey.vue';
 | 
						|
import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.vue';
 | 
						|
import { picoSearch } from '@scmmishra/pico-search';
 | 
						|
import { sanitizeLabel } from 'shared/helpers/sanitizeData';
 | 
						|
 | 
						|
export default {
 | 
						|
  components: {
 | 
						|
    LabelDropdownItem,
 | 
						|
    AddLabelModal,
 | 
						|
    Hotkey,
 | 
						|
  },
 | 
						|
 | 
						|
  props: {
 | 
						|
    accountLabels: {
 | 
						|
      type: Array,
 | 
						|
      default: () => [],
 | 
						|
    },
 | 
						|
    selectedLabels: {
 | 
						|
      type: Array,
 | 
						|
      default: () => [],
 | 
						|
    },
 | 
						|
    allowCreation: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
  },
 | 
						|
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      search: '',
 | 
						|
      createModalVisible: false,
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  computed: {
 | 
						|
    createLabelPlaceholder() {
 | 
						|
      const label = this.$t('CONTACT_PANEL.LABELS.LABEL_SELECT.CREATE_LABEL');
 | 
						|
      return this.search ? `${label}:` : label;
 | 
						|
    },
 | 
						|
 | 
						|
    filteredActiveLabels() {
 | 
						|
      if (!this.search) return this.accountLabels;
 | 
						|
 | 
						|
      return picoSearch(this.accountLabels, this.search, ['title'], {
 | 
						|
        threshold: 0.9,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    noResult() {
 | 
						|
      return this.filteredActiveLabels.length === 0;
 | 
						|
    },
 | 
						|
 | 
						|
    hasExactMatchInResults() {
 | 
						|
      return this.filteredActiveLabels.some(
 | 
						|
        label => label.title === this.search
 | 
						|
      );
 | 
						|
    },
 | 
						|
 | 
						|
    shouldShowCreate() {
 | 
						|
      return this.allowCreation && this.filteredActiveLabels.length < 3;
 | 
						|
    },
 | 
						|
 | 
						|
    parsedSearch() {
 | 
						|
      return sanitizeLabel(this.search);
 | 
						|
    },
 | 
						|
  },
 | 
						|
 | 
						|
  mounted() {
 | 
						|
    this.focusInput();
 | 
						|
  },
 | 
						|
 | 
						|
  methods: {
 | 
						|
    focusInput() {
 | 
						|
      this.$refs.searchbar.focus();
 | 
						|
    },
 | 
						|
 | 
						|
    updateLabels(label) {
 | 
						|
      this.$emit('update', label);
 | 
						|
    },
 | 
						|
 | 
						|
    onAdd(label) {
 | 
						|
      this.$emit('add', label);
 | 
						|
    },
 | 
						|
 | 
						|
    onRemove(label) {
 | 
						|
      this.$emit('remove', label);
 | 
						|
    },
 | 
						|
 | 
						|
    onAddRemove(label) {
 | 
						|
      if (this.selectedLabels.includes(label.title)) {
 | 
						|
        this.onRemove(label.title);
 | 
						|
      } else {
 | 
						|
        this.onAdd(label);
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    showCreateModal() {
 | 
						|
      this.createModalVisible = true;
 | 
						|
    },
 | 
						|
 | 
						|
    hideCreateModal() {
 | 
						|
      this.createModalVisible = false;
 | 
						|
    },
 | 
						|
  },
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<style lang="scss" scoped>
 | 
						|
.hotkey {
 | 
						|
  @apply flex-shrink-0;
 | 
						|
}
 | 
						|
 | 
						|
.search-input {
 | 
						|
  @apply m-0 w-full border border-solid border-transparent h-8 text-sm text-slate-700 dark:text-slate-100 rounded-md focus:border-woot-500 bg-slate-50 dark:bg-slate-900;
 | 
						|
}
 | 
						|
 | 
						|
.button-new-label {
 | 
						|
  @apply whitespace-nowrap text-ellipsis overflow-hidden items-center;
 | 
						|
 | 
						|
  .icon {
 | 
						|
    @apply min-w-0;
 | 
						|
  }
 | 
						|
}
 | 
						|
</style>
 |