322 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|     <div :id="'select-item-button-' + _uid" class="w-full border-b">
 | |
|         <button type="button" class="w-full h-10 flex items-center justify-center text-purple font-medium disabled:bg-gray-200 hover:bg-gray-100" @click="showItems">
 | |
|             <span class="material-icons-outlined text-base font-bold ltr:mr-1 rtl:ml-1">
 | |
|                 add
 | |
|             </span>
 | |
|             {{ addItemText }}
 | |
|         </button>
 | |
|  
 | |
|         <div :class="[{'is-open': show.item_list}]" tabindex="-1">
 | |
|             <div class="-mt-10.5 left-0 right-0 bg-white border rounded-lg" v-if="show.item_list">
 | |
|                 <div class="relative">
 | |
|                     <span class="material-icons-round absolute left-4 top-3 text-lg">search</span>
 | |
|                     <input 
 | |
|                         type="text"
 | |
|                         data-input="true"
 | |
|                         class="w-full text-sm px-10 py-2.5 mt-1rounded-none border border-gray-200 border-t-0 border-l-0 border-r-0 text-black placeholder-light-gray bg-white disabled:bg-gray-200 focus:outline-none focus:ring-transparent focus:border-purple"
 | |
|                         autocapitalize="default" 
 | |
|                         autocorrect="ON" 
 | |
|                         :placeholder="placeholder"
 | |
|                         :value="search"
 | |
|                         @input="onInput($event)"
 | |
|                         :ref="'input-item-field-' + _uid"
 | |
|                     />
 | |
|                 </div>
 | |
| 
 | |
|                 <ul class="w-full text-sm p-0 mt-0 rounded-lg border-0 border-light-gray text-black placeholder-light-gray bg-white cursor-pointer disabled:bg-gray-200 focus:outline-none focus:ring-transparent focus:border-purple">
 | |
|                     <div 
 | |
|                         class="hover:bg-gray-100 px-4" 
 | |
|                         v-for="(item, index) in sortedItems" 
 | |
|                         :key="index" 
 | |
|                         @click="onItemSelected(item)"
 | |
|                     >
 | |
|                         <div class="w-full flex items-center justify-between">
 | |
|                             <span class="w-9/12">{{ item.name }}</span>
 | |
| 
 | |
|                             <money 
 | |
|                                 :name="'item-id-' + item.id"
 | |
|                                 :value="item.amount"
 | |
|                                 v-bind="money"
 | |
|                                 masked
 | |
|                                 disabled
 | |
|                                 class="w-1/12 text-right disabled-money text-gray"
 | |
|                             ></money>
 | |
|                             -
 | |
|                             <money 
 | |
|                                 :name="'item-id-' + item.id"
 | |
|                                 :value="item.paid"
 | |
|                                 v-bind="money"
 | |
|                                 masked
 | |
|                                 disabled
 | |
|                                 class="w-1/12 text-right disabled-money text-gray"
 | |
|                             ></money>
 | |
|                             =
 | |
|                             <money 
 | |
|                                 :name="'item-id-' + item.id"
 | |
|                                 :value="item.open"
 | |
|                                 v-bind="money"
 | |
|                                 masked
 | |
|                                 disabled
 | |
|                                 class="w-1/12 text-right disabled-money text-gray"
 | |
|                             ></money>
 | |
|                         </div>
 | |
|                     </div>
 | |
| 
 | |
|                     <div class="hover:bg-gray-100 text-center py-2 px-4" v-if="!sortedItems.length">
 | |
|                         <div class="text-center">
 | |
|                             <span>{{ noDataText }}</span>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                 </ul>
 | |
|             </div>
 | |
|         </div>
 | |
|     </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import Vue from 'vue';
 | |
| 
 | |
| import {Money} from 'v-money';
 | |
| 
 | |
| export default {
 | |
|     name: 'akaunting-document-button',
 | |
| 
 | |
|     components: {
 | |
|         Money,
 | |
|     },
 | |
| 
 | |
|     props: {
 | |
|         placeholder: {
 | |
|             type: String,
 | |
|             default: 'Type an item name',
 | |
|             description: 'Input placeholder'
 | |
|         },
 | |
|         items: {
 | |
|             type: Array,
 | |
|             default: () => [],
 | |
|             description: 'List of Items'
 | |
|         },
 | |
|         selectedItems: {
 | |
|             type: Array,
 | |
|             default: () => [],
 | |
|             description: 'List of Selected Items'
 | |
|         },
 | |
|         addItemText: {
 | |
|             type: String,
 | |
|             default: 'Add an item',
 | |
|             description: ""
 | |
|         },
 | |
|         noDataText: {
 | |
|             type: String,
 | |
|             default: 'No Data',
 | |
|             description: "Selectbox empty options message"
 | |
|         },
 | |
|         dynamicCurrency: {
 | |
|             type: Object,
 | |
|             default: function () {
 | |
|                 return {
 | |
|                     decimal_mark: '.',
 | |
|                     thousands_separator: ',',
 | |
|                     symbol_first: 1,
 | |
|                     symbol: '$',
 | |
|                     precision: 2,
 | |
|                 };
 | |
|             },
 | |
|             description: "Dynamic currency"
 | |
|         },
 | |
|         currency: {
 | |
|             type: Object,
 | |
|             default: function () {
 | |
|                 return {
 | |
|                     decimal_mark: '.',
 | |
|                     thousands_separator: ',',
 | |
|                     symbol_first: 1,
 | |
|                     symbol: '$',
 | |
|                     precision: 2,
 | |
|                 };
 | |
|             },
 | |
|             description: "Default currency"
 | |
|         },
 | |
|     },
 | |
| 
 | |
|     data() {
 | |
|         return {
 | |
|             item_list: [],
 | |
|             search: '', // search column model
 | |
|             show: {
 | |
|                 item_list: false,
 | |
|             },
 | |
|             money: {
 | |
|                 decimal: this.currency.decimal_mark,
 | |
|                 thousands: this.currency.thousands_separator,
 | |
|                 prefix: (this.currency.symbol_first) ? this.currency.symbol : '',
 | |
|                 suffix: (!this.currency.symbol_first) ? this.currency.symbol : '',
 | |
|                 precision: parseInt(this.currency.precision),
 | |
|                 masked: this.masked
 | |
|             }
 | |
|         };
 | |
|     },
 | |
| 
 | |
|     created() {
 | |
|         this.setItemList(this.items);
 | |
|     },
 | |
| 
 | |
|     mounted() {
 | |
|         if (this.dynamicCurrency.code != this.currency.code) {
 | |
|             if (!this.dynamicCurrency.decimal) {
 | |
|                 this.money = {
 | |
|                     decimal: this.dynamicCurrency.decimal_mark,
 | |
|                     thousands: this.dynamicCurrency.thousands_separator,
 | |
|                     prefix: (this.dynamicCurrency.symbol_first) ? this.dynamicCurrency.symbol : '',
 | |
|                     suffix: (!this.dynamicCurrency.symbol_first) ? this.dynamicCurrency.symbol : '',
 | |
|                     precision: parseInt(this.dynamicCurrency.precision),
 | |
|                     masked: this.masked
 | |
|                 };
 | |
|             } else {
 | |
|                 this.money = this.dynamicCurrency;
 | |
|             }
 | |
|         }
 | |
|     },
 | |
| 
 | |
|     methods: {
 | |
|         setItemList(items) {
 | |
|             this.item_list = [];
 | |
| 
 | |
|             // Option set sort_option data
 | |
|             if (Array.isArray(items)) {
 | |
|                 items.forEach(function (item, index) {
 | |
|                     let selected = this.selectedItems.find(function (selected_item) {
 | |
|                         return selected_item.id === item.id;
 | |
|                     }, this);
 | |
| 
 | |
|                     if (selected) {
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
|                     this.item_list.push({
 | |
|                         id: item.id,
 | |
|                         name: item.document_number + ' | ' + item.contact_name + (item.notes ? ' | ' + item.notes : ''),
 | |
|                         amount: item.amount,
 | |
|                         paid: item.paid,
 | |
|                         open: item.amount - item.paid,
 | |
|                     });
 | |
|                 }, this);
 | |
|             }
 | |
|         },
 | |
| 
 | |
|         showItems() {
 | |
|             this.show.item_list = true;
 | |
| 
 | |
|             setTimeout(function() {
 | |
|                 this.$refs['input-item-field-' + this._uid].focus();
 | |
|             }.bind(this), 100);
 | |
|         },
 | |
| 
 | |
|         onInput(event) {
 | |
|             this.search = event.target.value;
 | |
| 
 | |
|             //to optimize performance we kept the condition that checks for if search exists or not
 | |
|             if (!this.search) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             //condition that checks if input is below the given character limit 
 | |
|             this.setItemList(this.items); //once the user deletes the search input, we show the overall item list
 | |
|             this.sortItems(); // we order it as wanted
 | |
|             this.$emit('input', this.search); // keep the input binded to v-model
 | |
|         },
 | |
| 
 | |
|         onItemSelected(param_item) {
 | |
|             let selected_item = this.items.find(function (item) {
 | |
|                 return item.id === param_item.id
 | |
|             }, this);
 | |
| 
 | |
|             this.$emit('document-selected', selected_item);
 | |
| 
 | |
|             this.show.item_list = false;
 | |
| 
 | |
|             this.search = '';
 | |
| 
 | |
|             // Set default item list
 | |
|             this.setItemList(this.items);
 | |
|         },
 | |
| 
 | |
|         closeIfClickedOutside(event) {
 | |
|             if (!document.getElementById('select-item-button-' + this._uid).contains(event.target)) {
 | |
|                 this.show.item_list = false;
 | |
|                 this.search = '';
 | |
| 
 | |
|                 document.removeEventListener('click', this.closeIfClickedOutside);
 | |
| 
 | |
|                 this.setItemList(this.items);
 | |
|             }
 | |
|         },
 | |
| 
 | |
|         sortItems() {
 | |
|             this.item_list.sort(function (a, b) {
 | |
|                 var nameA = a.name.toUpperCase(); // ignore upper and lowercase
 | |
|                 var nameB = b.name.toUpperCase(); // ignore upper and lowercase
 | |
| 
 | |
|                 if (nameA < nameB) {
 | |
|                     return -1;
 | |
|                 }
 | |
| 
 | |
|                 if (nameA > nameB) {
 | |
|                     return 1;
 | |
|                 }
 | |
| 
 | |
|                 // names must be equal
 | |
|                 return 0;
 | |
|             });
 | |
| 
 | |
|             const sortedItemList = this.item_list.filter(item => 
 | |
|                 item.name.toLowerCase().includes(this.search.toLowerCase())
 | |
|             );
 | |
| 
 | |
|             return sortedItemList;
 | |
|         },
 | |
|     },
 | |
| 
 | |
|     computed: {
 | |
|         sortedItems() {
 | |
|             return this.sortItems();
 | |
|         },
 | |
|     },
 | |
| 
 | |
|     watch: {
 | |
|         dynamicCurrency: function (currency) {
 | |
|             if (!currency) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             this.money = {
 | |
|                 decimal: currency.decimal_mark,
 | |
|                 thousands: currency.thousands_separator,
 | |
|                 prefix: (currency.symbol_first) ? currency.symbol : '',
 | |
|                 suffix: (!currency.symbol_first) ? currency.symbol : '',
 | |
|                 precision: parseInt(currency.precision),
 | |
|                 masked: this.masked
 | |
|             };
 | |
|         },
 | |
| 
 | |
|         show: {
 | |
|             handler: function(newValue) {
 | |
|                 if (newValue) {
 | |
|                     document.addEventListener('click', this.closeIfClickedOutside);
 | |
|                 }
 | |
|             },
 | |
|             deep: true
 | |
|         },
 | |
| 
 | |
|         items: function (items) {
 | |
|             this.setItemList(items);
 | |
|         },
 | |
| 
 | |
|         selectedItems: function () {
 | |
|             this.setItemList(this.items);
 | |
|         }
 | |
|     },
 | |
| };
 | |
| </script>
 |