mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-31 05:31:10 -04:00 
			
		
		
		
	build version 400
This commit is contained in:
		| @ -1,132 +1,161 @@ | ||||
| <template> | ||||
|   <div class="main-content item-create"> | ||||
|     <div class="page-header"> | ||||
|       <h3 class="page-title">{{ isEdit ? $t('items.edit_item') : $t('items.new_item') }}</h3> | ||||
|       <ol class="breadcrumb"> | ||||
|         <li class="breadcrumb-item"><router-link slot="item-title" to="/admin/dashboard">{{ $t('general.home') }}</router-link></li> | ||||
|         <li class="breadcrumb-item"><router-link slot="item-title" to="/admin/items">{{ $tc('items.item',2) }}</router-link></li> | ||||
|         <li class="breadcrumb-item"><a href="#"> {{ isEdit ? $t('items.edit_item') : $t('items.new_item') }}</a></li> | ||||
|       </ol> | ||||
|     </div> | ||||
|     <div class="row"> | ||||
|       <div class="col-sm-6"> | ||||
|         <div class="card"> | ||||
|           <form action="" @submit.prevent="submitItem"> | ||||
|             <div class="card-body"> | ||||
|               <div class="form-group"> | ||||
|                 <label class="control-label">{{ $t('items.name') }}</label><span class="text-danger"> *</span> | ||||
|                 <base-input | ||||
|                   v-model.trim="formData.name" | ||||
|                   :invalid="$v.formData.name.$error" | ||||
|                   focus | ||||
|                   type="text" | ||||
|                   name="name" | ||||
|                   @input="$v.formData.name.$touch()" | ||||
|                 /> | ||||
|                 <div v-if="$v.formData.name.$error"> | ||||
|                   <span v-if="!$v.formData.name.required" class="text-danger">{{ $t('validation.required') }} </span> | ||||
|                   <span v-if="!$v.formData.name.minLength" class="text-danger"> | ||||
|                     {{ $tc('validation.name_min_length', $v.formData.name.$params.minLength.min, { count: $v.formData.name.$params.minLength.min }) }} | ||||
|                   </span> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="form-group"> | ||||
|                 <label>{{ $t('items.price') }}</label><span class="text-danger"> *</span> | ||||
|                 <div class="base-input"> | ||||
|                   <money | ||||
|                     :class="{'invalid' : $v.formData.price.$error}" | ||||
|                     v-model="price" | ||||
|                     v-bind="defaultCurrencyForInput" | ||||
|                     class="input-field" | ||||
|   <base-page> | ||||
|     <!-- Page Header --> | ||||
|     <sw-page-header class="mb-3" :title="pageTitle"> | ||||
|       <sw-breadcrumb slot="breadcrumbs"> | ||||
|         <sw-breadcrumb-item to="/admin/dashboard" :title="$t('general.home')" /> | ||||
|         <sw-breadcrumb-item to="/admin/items" :title="$tc('items.item', 2)" /> | ||||
|         <sw-breadcrumb-item | ||||
|           v-if="$route.name === 'items.edit'" | ||||
|           to="#" | ||||
|           :title="$t('items.edit_item')" | ||||
|           active | ||||
|         /> | ||||
|         <sw-breadcrumb-item | ||||
|           v-else | ||||
|           to="#" | ||||
|           :title="$t('items.new_item')" | ||||
|           active | ||||
|         /> | ||||
|       </sw-breadcrumb> | ||||
|     </sw-page-header> | ||||
|  | ||||
|     <div class="grid grid-cols-12"> | ||||
|       <div class="col-span-12 md:col-span-6"> | ||||
|         <form action="" @submit.prevent="submitItem"> | ||||
|           <sw-card> | ||||
|             <sw-input-group | ||||
|               :label="$t('items.name')" | ||||
|               :error="nameError" | ||||
|               class="mb-4" | ||||
|               required | ||||
|             > | ||||
|               <sw-input | ||||
|                 v-model.trim="formData.name" | ||||
|                 :invalid="$v.formData.name.$error" | ||||
|                 class="mt-2" | ||||
|                 focus | ||||
|                 type="text" | ||||
|                 name="name" | ||||
|                 @input="$v.formData.name.$touch()" | ||||
|               /> | ||||
|             </sw-input-group> | ||||
|  | ||||
|             <sw-input-group | ||||
|               :label="$t('items.price')" | ||||
|               :error="priceError" | ||||
|               class="mb-4" | ||||
|               required | ||||
|             > | ||||
|               <sw-money | ||||
|                 v-model.trim="price" | ||||
|                 :invalid="$v.formData.price.$error" | ||||
|                 :currency="defaultCurrencyForInput" | ||||
|                 class="relative w-full focus:border focus:border-solid focus:border-primary-500" | ||||
|                 @input="$v.formData.price.$touch()" | ||||
|               /> | ||||
|             </sw-input-group> | ||||
|  | ||||
|             <sw-input-group :label="$t('items.unit')" class="mb-4"> | ||||
|               <sw-select | ||||
|                 v-model="formData.unit" | ||||
|                 class="mt-2" | ||||
|                 :options="itemUnits" | ||||
|                 :searchable="true" | ||||
|                 :show-labels="false" | ||||
|                 :placeholder="$t('items.select_a_unit')" | ||||
|                 label="name" | ||||
|               > | ||||
|                 <div | ||||
|                   slot="afterList" | ||||
|                   class="flex items-center justify-center w-full px-6 py-3 text-base bg-gray-200 cursor-pointer text-primary-400" | ||||
|                   @click="addItemUnit" | ||||
|                 > | ||||
|                   <shopping-cart-icon | ||||
|                     class="h-5 mr-2 -ml-2 text-center text-primary-400" | ||||
|                   /> | ||||
|  | ||||
|                   <label class="ml-2 text-sm leading-none text-primary-400">{{ | ||||
|                     $t('settings.customization.items.add_item_unit') | ||||
|                   }}</label> | ||||
|                 </div> | ||||
|                 <div v-if="$v.formData.price.$error"> | ||||
|                   <span v-if="!$v.formData.price.required" class="text-danger">{{ $t('validation.required') }} </span> | ||||
|                   <span v-if="!$v.formData.price.maxLength" class="text-danger">{{ $t('validation.price_maxlength') }}</span> | ||||
|                   <span v-if="!$v.formData.price.minValue" class="text-danger">{{ $t('validation.price_minvalue') }}</span> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="form-group"> | ||||
|                 <label>{{ $t('items.unit') }}</label> | ||||
|                 <base-select | ||||
|                   v-model="formData.unit" | ||||
|                   :options="itemUnits" | ||||
|                   :searchable="true" | ||||
|                   :show-labels="false" | ||||
|                   :placeholder="$t('items.select_a_unit')" | ||||
|                   label="name" | ||||
|                 > | ||||
|                   <div slot="afterList"> | ||||
|                     <button type="button" class="list-add-button" @click="addItemUnit"> | ||||
|                       <font-awesome-icon class="icon" icon="cart-plus" /> | ||||
|                       <label>{{ $t('settings.customization.items.add_item_unit') }}</label> | ||||
|                     </button> | ||||
|                   </div> | ||||
|                 </base-select> | ||||
|               </div> | ||||
|               <div v-if="isTaxPerItem" class="form-group"> | ||||
|                 <label>{{ $t('items.taxes') }}</label> | ||||
|                 <base-select | ||||
|                   v-model="formData.taxes" | ||||
|                   :options="getTaxTypes" | ||||
|                   :searchable="true" | ||||
|                   :show-labels="false" | ||||
|                   :allow-empty="true" | ||||
|                   :multiple="true" | ||||
|                   track-by="tax_type_id" | ||||
|                   label="tax_name" | ||||
|                 /> | ||||
|               </div> | ||||
|               <div class="form-group"> | ||||
|                 <label for="description">{{ $t('items.description') }}</label> | ||||
|                 <base-text-area | ||||
|                   v-model="formData.description" | ||||
|                   rows="2" | ||||
|                   name="description" | ||||
|                   @input="$v.formData.description.$touch()" | ||||
|                 /> | ||||
|                 <div v-if="$v.formData.description.$error"> | ||||
|                   <span v-if="!$v.formData.description.maxLength" class="text-danger"> | ||||
|                     {{ $t('validation.description_maxlength') }} | ||||
|                   </span> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="form-group"> | ||||
|                 <base-button | ||||
|                   :loading="isLoading" | ||||
|                   :disabled="isLoading" | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                   class="collapse-button" | ||||
|                 > | ||||
|                   {{ isEdit ? $t('items.update_item') : $t('items.save_item') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|               </sw-select> | ||||
|             </sw-input-group> | ||||
|  | ||||
|             <sw-input-group | ||||
|               v-if="isTaxPerItem" | ||||
|               :label="$t('items.taxes')" | ||||
|               class="mb-4" | ||||
|             > | ||||
|               <sw-select | ||||
|                 v-model="formData.taxes" | ||||
|                 class="mt-2" | ||||
|                 :options="getTaxTypes" | ||||
|                 :searchable="true" | ||||
|                 :show-labels="false" | ||||
|                 :allow-empty="true" | ||||
|                 :multiple="true" | ||||
|                 track-by="tax_type_id" | ||||
|                 label="tax_name" | ||||
|               /> | ||||
|             </sw-input-group> | ||||
|  | ||||
|             <sw-input-group | ||||
|               :label="$t('items.description')" | ||||
|               :error="descriptionError" | ||||
|               class="mb-4" | ||||
|             > | ||||
|               <sw-textarea | ||||
|                 v-model="formData.description" | ||||
|                 rows="2" | ||||
|                 name="description" | ||||
|                 @input="$v.formData.description.$touch()" | ||||
|               /> | ||||
|             </sw-input-group> | ||||
|  | ||||
|             <div class="mb-4"> | ||||
|               <sw-button | ||||
|                 :loading="isLoading" | ||||
|                 variant="primary" | ||||
|                 size="lg" | ||||
|                 class="flex w-full justify-center md:w-auto" | ||||
|               > | ||||
|                 <save-icon v-if="!isLoading" class="mr-2 -ml-1" /> | ||||
|                 {{ isEdit ? $t('items.update_item') : $t('items.save_item') }} | ||||
|               </sw-button> | ||||
|             </div> | ||||
|           </form> | ||||
|         </div> | ||||
|           </sw-card> | ||||
|         </form> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   </base-page> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { validationMixin } from 'vuelidate' | ||||
| import { mapActions, mapGetters } from 'vuex' | ||||
| const { required, minLength, numeric, minValue, maxLength } = require('vuelidate/lib/validators') | ||||
| import { ShoppingCartIcon } from '@vue-hero-icons/solid' | ||||
| import TheSiteHeaderVue from '../layouts/partials/TheSiteHeader.vue' | ||||
| const { | ||||
|   required, | ||||
|   minLength, | ||||
|   numeric, | ||||
|   minValue, | ||||
|   maxLength, | ||||
| } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   mixins: { | ||||
|     validationMixin | ||||
|   components: { | ||||
|     ShoppingCartIcon, | ||||
|   }, | ||||
|   data () { | ||||
|  | ||||
|   data() { | ||||
|     return { | ||||
|       isLoading: false, | ||||
|       title: 'Add Item', | ||||
|       units: [], | ||||
|       taxes: [], | ||||
|       taxPerItem: '', | ||||
|  | ||||
|       formData: { | ||||
|         name: '', | ||||
|         description: '', | ||||
| @ -134,142 +163,246 @@ export default { | ||||
|         unit_id: null, | ||||
|         unit: null, | ||||
|         taxes: [], | ||||
|         tax_per_item: false | ||||
|       }, | ||||
|  | ||||
|       money: { | ||||
|         decimal: '.', | ||||
|         thousands: ',', | ||||
|         prefix: '$ ', | ||||
|         precision: 2, | ||||
|         masked: false | ||||
|       } | ||||
|         masked: false, | ||||
|       }, | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     ...mapGetters('currency', [ | ||||
|       'defaultCurrencyForInput' | ||||
|     ]), | ||||
|     ...mapGetters('item', [ | ||||
|       'itemUnits' | ||||
|     ]), | ||||
|     ...mapGetters('company', ['defaultCurrencyForInput']), | ||||
|  | ||||
|     ...mapGetters('item', ['itemUnits']), | ||||
|  | ||||
|     ...mapGetters('taxType', ['taxTypes']), | ||||
|  | ||||
|     price: { | ||||
|       get: function () { | ||||
|         return this.formData.price / 100 | ||||
|       }, | ||||
|       set: function (newValue) { | ||||
|         this.formData.price = newValue * 100 | ||||
|       } | ||||
|       }, | ||||
|     }, | ||||
|     ...mapGetters('taxType', [ | ||||
|       'taxTypes' | ||||
|     ]), | ||||
|     isEdit () { | ||||
|  | ||||
|     pageTitle() { | ||||
|       if (this.$route.name === 'items.edit') { | ||||
|         return this.$t('items.edit_item') | ||||
|       } | ||||
|       return this.$t('items.new_item') | ||||
|     }, | ||||
|  | ||||
|     ...mapGetters('taxType', ['taxTypes']), | ||||
|  | ||||
|     isEdit() { | ||||
|       if (this.$route.name === 'items.edit') { | ||||
|         return true | ||||
|       } | ||||
|       return false | ||||
|     }, | ||||
|     isTaxPerItem () { | ||||
|  | ||||
|     isTaxPerItem() { | ||||
|       return this.taxPerItem === 'YES' ? 1 : 0 | ||||
|     }, | ||||
|     getTaxTypes () { | ||||
|       return this.taxTypes.map(tax => { | ||||
|         return {...tax, tax_type_id: tax.id, tax_name: tax.name + ' (' + tax.percent + '%)'} | ||||
|  | ||||
|     getTaxTypes() { | ||||
|       return this.taxTypes.map((tax) => { | ||||
|         return { | ||||
|           ...tax, | ||||
|           tax_type_id: tax.id, | ||||
|           tax_name: tax.name + ' (' + tax.percent + '%)', | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|     }, | ||||
|  | ||||
|     nameError() { | ||||
|       if (!this.$v.formData.name.$error) { | ||||
|         return '' | ||||
|       } | ||||
|  | ||||
|       if (!this.$v.formData.name.required) { | ||||
|         return this.$t('validation.required') | ||||
|       } | ||||
|  | ||||
|       if (!this.$v.formData.name.minLength) { | ||||
|         return this.$tc( | ||||
|           'validation.name_min_length', | ||||
|           this.$v.formData.name.$params.minLength.min, | ||||
|           { count: this.$v.formData.name.$params.minLength.min } | ||||
|         ) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     priceError() { | ||||
|       if (!this.$v.formData.price.$error) { | ||||
|         return '' | ||||
|       } | ||||
|  | ||||
|       if (!this.$v.formData.price.required) { | ||||
|         return this.$t('validation.required') | ||||
|       } | ||||
|  | ||||
|       if (!this.$v.formData.price.maxLength) { | ||||
|         return this.$t('validation.price_maxlength') | ||||
|       } | ||||
|  | ||||
|       if (!this.$v.formData.price.minValue) { | ||||
|         return this.$t('validation.price_minvalue') | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     descriptionError() { | ||||
|       if (!this.$v.formData.description.$error) { | ||||
|         return '' | ||||
|       } | ||||
|  | ||||
|       if (!this.$v.formData.description.maxLength) { | ||||
|         return this.$t('validation.description_maxlength') | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   created () { | ||||
|  | ||||
|   created() { | ||||
|     this.loadData() | ||||
|   }, | ||||
|  | ||||
|   mounted() { | ||||
|     this.setTaxPerItem() | ||||
|     if (this.isEdit) { | ||||
|       this.loadEditData() | ||||
|     } | ||||
|  | ||||
|     this.$v.formData.$reset() | ||||
|   }, | ||||
|  | ||||
|   validations: { | ||||
|     formData: { | ||||
|       name: { | ||||
|         required, | ||||
|         minLength: minLength(3) | ||||
|         minLength: minLength(3), | ||||
|       }, | ||||
|  | ||||
|       price: { | ||||
|         required, | ||||
|         numeric, | ||||
|         maxLength: maxLength(20), | ||||
|         minValue: minValue(0.1) | ||||
|         minValue: minValue(0.1), | ||||
|       }, | ||||
|  | ||||
|       description: { | ||||
|         maxLength: maxLength(255) | ||||
|       } | ||||
|     } | ||||
|         maxLength: maxLength(255), | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     ...mapActions('item', [ | ||||
|       'addItem', | ||||
|       'fetchItem', | ||||
|       'updateItem' | ||||
|       'updateItem', | ||||
|       'fetchItemUnits', | ||||
|     ]), | ||||
|     ...mapActions('modal', [ | ||||
|       'openModal' | ||||
|     ]), | ||||
|     async setTaxPerItem () { | ||||
|       let res = await axios.get('/api/settings/get-setting?key=tax_per_item') | ||||
|       if (res.data && res.data.tax_per_item === 'YES') { | ||||
|         this.taxPerItem = 'YES' | ||||
|       } else { | ||||
|         this.taxPerItem = 'FALSE' | ||||
|  | ||||
|     ...mapActions('taxType', ['fetchTaxTypes']), | ||||
|  | ||||
|     ...mapActions('company', ['fetchCompanySettings']), | ||||
|  | ||||
|     ...mapActions('modal', ['openModal']), | ||||
|  | ||||
|     async setTaxPerItem() { | ||||
|       let response = await this.fetchCompanySettings(['tax_per_item']) | ||||
|  | ||||
|       if (response.data) { | ||||
|         response.data.tax_per_item === 'YES' | ||||
|           ? (this.taxPerItem = 'YES') | ||||
|           : (this.taxPerItem = 'NO') | ||||
|       } | ||||
|     }, | ||||
|     async loadEditData () { | ||||
|       let response = await this.fetchItem(this.$route.params.id) | ||||
|  | ||||
|       this.formData = {...response.data.item, unit: null} | ||||
|       this.formData.taxes = response.data.item.taxes.map(tax => { | ||||
|         return {...tax, tax_name: tax.name + ' (' + tax.percent + '%)'} | ||||
|       }) | ||||
|     async loadData() { | ||||
|       if (this.isEdit) { | ||||
|         let response = await this.fetchItem(this.$route.params.id) | ||||
|  | ||||
|       this.formData.unit = this.itemUnits.find(_unit => response.data.item.unit_id === _unit.id) | ||||
|       this.fractional_price = response.data.item.price | ||||
|         this.formData = { ...response.data.item, unit: null } | ||||
|  | ||||
|         this.fractional_price = response.data.item.price | ||||
|  | ||||
|         if (this.formData.unit_id) { | ||||
|           await this.fetchItemUnits({ limit: 'all' }) | ||||
|           this.formData.unit = this.itemUnits.find( | ||||
|             (_unit) => response.data.item.unit_id === _unit.id | ||||
|           ) | ||||
|         } | ||||
|  | ||||
|         if (this.formData.taxes) { | ||||
|           await this.fetchTaxTypes({ limit: 'all' }) | ||||
|           this.formData.taxes = response.data.item.taxes.map((tax) => { | ||||
|             return { ...tax, tax_name: tax.name + '(' + tax.percent + '%)' } | ||||
|           }) | ||||
|         } | ||||
|       } else { | ||||
|         this.fetchItemUnits({ limit: 'all' }) | ||||
|         this.fetchTaxTypes({ limit: 'all' }) | ||||
|       } | ||||
|     }, | ||||
|     async submitItem () { | ||||
|  | ||||
|     async submitItem() { | ||||
|       this.$v.formData.$touch() | ||||
|  | ||||
|       if (this.$v.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       if (this.formData.unit) { | ||||
|         this.formData.unit_id = this.formData.unit.id | ||||
|       } | ||||
|  | ||||
|       let response | ||||
|       this.isLoading = true | ||||
|  | ||||
|       if (this.isEdit) { | ||||
|         this.isLoading = true | ||||
|         response = await this.updateItem(this.formData) | ||||
|       } else { | ||||
|         let data = { | ||||
|           ...this.formData, | ||||
|           taxes: this.formData.taxes.map(tax => { | ||||
|           taxes: this.formData.taxes.map((tax) => { | ||||
|             return { | ||||
|               tax_type_id: tax.id, | ||||
|               amount: ((this.formData.price * tax.percent) / 100), | ||||
|               amount: (this.formData.price * tax.percent) / 100, | ||||
|               percent: tax.percent, | ||||
|               name: tax.name, | ||||
|               collective_tax: 0 | ||||
|               collective_tax: 0, | ||||
|             } | ||||
|           }) | ||||
|           }), | ||||
|         } | ||||
|         response = await this.addItem(data) | ||||
|       } | ||||
|  | ||||
|       if (response.data) { | ||||
|         this.isLoading = false | ||||
|         window.toastr['success'](this.$tc('items.updated_message')) | ||||
|         this.$router.push('/admin/items') | ||||
|         return true | ||||
|  | ||||
|         if (!this.isEdit) { | ||||
|           window.toastr['success'](this.$tc('items.created_message')) | ||||
|           this.$router.push('/admin/items') | ||||
|           return true | ||||
|         } else { | ||||
|           window.toastr['success'](this.$tc('items.updated_message')) | ||||
|           this.$router.push('/admin/items') | ||||
|           return true | ||||
|         } | ||||
|         window.toastr['error'](response.data.error) | ||||
|       } | ||||
|       window.toastr['error'](response.data.error) | ||||
|     }, | ||||
|     async addItemUnit () { | ||||
|  | ||||
|     async addItemUnit() { | ||||
|       this.openModal({ | ||||
|         'title': this.$t('settings.customization.items.add_item_unit'), | ||||
|         'componentName': 'ItemUnit' | ||||
|         title: this.$t('settings.customization.items.add_item_unit'), | ||||
|         componentName: 'ItemUnit', | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -1,295 +1,342 @@ | ||||
| <template> | ||||
|   <div class="items main-content"> | ||||
|     <div class="page-header"> | ||||
|       <div class="d-flex flex-row"> | ||||
|         <div> | ||||
|           <h3 class="page-title">{{ $tc('items.item', 2) }}</h3> | ||||
|         </div> | ||||
|       </div> | ||||
|       <ol class="breadcrumb"> | ||||
|         <li class="breadcrumb-item"> | ||||
|           <router-link | ||||
|             slot="item-title" | ||||
|             to="dashboard"> | ||||
|             {{ $t('general.home') }} | ||||
|           </router-link> | ||||
|         </li> | ||||
|         <li class="breadcrumb-item"> | ||||
|           <router-link | ||||
|             slot="item-title" | ||||
|             to="#"> | ||||
|             {{ $tc('items.item', 2) }} | ||||
|           </router-link> | ||||
|         </li> | ||||
|       </ol> | ||||
|       <div class="page-actions row"> | ||||
|         <div class="col-xs-2 mr-4"> | ||||
|           <base-button | ||||
|             v-show="totalItems || filtersApplied" | ||||
|             :outline="true" | ||||
|             :icon="filterIcon" | ||||
|             color="theme" | ||||
|             size="large" | ||||
|             right-icon | ||||
|             @click="toggleFilter" | ||||
|           > | ||||
|             {{ $t('general.filter') }} | ||||
|           </base-button> | ||||
|         </div> | ||||
|         <router-link slot="item-title" class="col-xs-2" to="items/create"> | ||||
|           <base-button | ||||
|             color="theme" | ||||
|             icon="plus" | ||||
|             size="large" | ||||
|           > | ||||
|             {{ $t('items.add_item') }} | ||||
|           </base-button> | ||||
|         </router-link> | ||||
|       </div> | ||||
|     </div> | ||||
|   <base-page> | ||||
|     <sw-page-header :title="$t('items.title')"> | ||||
|       <sw-breadcrumb slot="breadcrumbs"> | ||||
|         <sw-breadcrumb-item to="dashboard" :title="$t('general.home')" /> | ||||
|         <sw-breadcrumb-item to="#" :title="$tc('items.item', 2)" active /> | ||||
|       </sw-breadcrumb> | ||||
|  | ||||
|     <transition name="fade"> | ||||
|       <div v-show="showFilters" class="filter-section"> | ||||
|         <div class="row"> | ||||
|           <div class="col-sm-4"> | ||||
|             <label class="form-label"> {{ $tc('items.name') }} </label> | ||||
|             <base-input | ||||
|               v-model="filters.name" | ||||
|               type="text" | ||||
|               name="name" | ||||
|               autocomplete="off" | ||||
|             /> | ||||
|           </div> | ||||
|           <div class="col-sm-4"> | ||||
|             <label class="form-label"> {{ $tc('items.unit') }} </label> | ||||
|             <base-select | ||||
|               v-model="filters.unit" | ||||
|               :options="itemUnits" | ||||
|               :searchable="true" | ||||
|               :show-labels="false" | ||||
|               :placeholder="$t('items.select_a_unit')" | ||||
|               label="name" | ||||
|               autocomplete="off" | ||||
|             /> | ||||
|           </div> | ||||
|           <div class="col-sm-4"> | ||||
|             <label class="form-label"> {{ $tc('items.price') }} </label> | ||||
|             <base-input | ||||
|               v-model="filters.price" | ||||
|               type="text" | ||||
|               name="name" | ||||
|               autocomplete="off" | ||||
|             /> | ||||
|           </div> | ||||
|           <label class="clear-filter" @click="clearFilter"> {{ $t('general.clear_all') }}</label> | ||||
|         </div> | ||||
|       </div> | ||||
|     </transition> | ||||
|  | ||||
|     <div v-cloak v-show="showEmptyScreen" class="col-xs-1 no-data-info" align="center"> | ||||
|       <satellite-icon class="mt-5 mb-4"/> | ||||
|       <div class="row" align="center"> | ||||
|         <label class="col title">{{ $t('items.no_items') }}</label> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|         <label class="description col mt-1" align="center">{{ $t('items.list_of_items') }}</label> | ||||
|       </div> | ||||
|       <div class="btn-container"> | ||||
|         <base-button | ||||
|           :outline="true" | ||||
|           color="theme" | ||||
|           class="mt-3" | ||||
|           size="large" | ||||
|           @click="$router.push('items/create')" | ||||
|       <template slot="actions"> | ||||
|         <sw-button | ||||
|           v-show="totalItems" | ||||
|           variant="primary-outline" | ||||
|           size="lg" | ||||
|           @click="toggleFilter" | ||||
|         > | ||||
|           {{ $t('items.add_new_item') }} | ||||
|         </base-button> | ||||
|       </div> | ||||
|     </div> | ||||
|           {{ $t('general.filter') }} | ||||
|           <component :is="filterIcon" class="w-4 h-4 ml-2 -mr-1" /> | ||||
|         </sw-button> | ||||
|  | ||||
|     <div v-show="!showEmptyScreen" class="table-container"> | ||||
|       <div class="table-actions mt-5"> | ||||
|         <p class="table-stats">{{ $t('general.showing') }}: <b>{{ items.length }}</b> {{ $t('general.of') }} <b>{{ totalItems }}</b></p> | ||||
|         <transition name="fade"> | ||||
|           <v-dropdown v-if="selectedItems.length" :show-arrow="false"> | ||||
|             <span slot="activator" href="#" class="table-actions-button dropdown-toggle"> | ||||
|         <sw-button | ||||
|           tag-name="router-link" | ||||
|           to="items/create" | ||||
|           variant="primary" | ||||
|           size="lg" | ||||
|           class="ml-4" | ||||
|         > | ||||
|           <plus-icon class="w-6 h-6 mr-1 -ml-2" /> | ||||
|           {{ $t('items.add_item') }} | ||||
|         </sw-button> | ||||
|       </template> | ||||
|     </sw-page-header> | ||||
|  | ||||
|     <slide-y-up-transition> | ||||
|       <sw-filter-wrapper v-show="showFilters"> | ||||
|         <sw-input-group :label="$tc('items.name')" class="flex-1 mt-2 ml-0"> | ||||
|           <sw-input | ||||
|             v-model="filters.name" | ||||
|             type="text" | ||||
|             name="name" | ||||
|             class="mt-2" | ||||
|             autocomplete="off" | ||||
|           /> | ||||
|         </sw-input-group> | ||||
|  | ||||
|         <sw-input-group | ||||
|           :label="$tc('items.unit')" | ||||
|           class="flex-1 mt-2 ml-0 lg:ml-6" | ||||
|         > | ||||
|           <sw-select | ||||
|             v-model="filters.unit" | ||||
|             :options="itemUnits" | ||||
|             :searchable="true" | ||||
|             class="mt-2" | ||||
|             :show-labels="false" | ||||
|             :placeholder="$t('items.select_a_unit')" | ||||
|             label="name" | ||||
|             autocomplete="off" | ||||
|           /> | ||||
|         </sw-input-group> | ||||
|  | ||||
|         <sw-input-group | ||||
|           :label="$tc('items.price')" | ||||
|           class="flex-1 mt-2 ml-0 lg:ml-6" | ||||
|         > | ||||
|           <sw-input | ||||
|             v-model="filters.price" | ||||
|             type="text" | ||||
|             name="name" | ||||
|             class="mt-2" | ||||
|             autocomplete="off" | ||||
|           /> | ||||
|         </sw-input-group> | ||||
|  | ||||
|         <label | ||||
|           class="absolute text-sm leading-snug text-gray-900 cursor-pointer" | ||||
|           style="top: 10px; right: 15px" | ||||
|           @click="clearFilter" | ||||
|         > | ||||
|           {{ $t('general.clear_all') }}</label | ||||
|         > | ||||
|       </sw-filter-wrapper> | ||||
|     </slide-y-up-transition> | ||||
|  | ||||
|     <sw-empty-table-placeholder | ||||
|       v-show="showEmptyScreen" | ||||
|       :title="$t('items.no_items')" | ||||
|       :description="$t('items.list_of_items')" | ||||
|     > | ||||
|       <satellite-icon class="mt-5 mb-4" /> | ||||
|  | ||||
|       <sw-button | ||||
|         slot="actions" | ||||
|         tag-name="router-link" | ||||
|         to="/admin/items/create" | ||||
|         size="lg" | ||||
|         variant="primary-outline" | ||||
|       > | ||||
|         <plus-icon class="w-6 h-6 mr-1 -ml-2" /> | ||||
|         {{ $t('items.add_new_item') }} | ||||
|       </sw-button> | ||||
|     </sw-empty-table-placeholder> | ||||
|  | ||||
|     <div v-show="!showEmptyScreen" class="relative table-container"> | ||||
|       <div | ||||
|         class="relative flex items-center justify-between h-10 mt-5 list-none border-b-2 border-gray-200 border-solid" | ||||
|       > | ||||
|         <p class="text-sm"> | ||||
|           {{ $t('general.showing') }}: <b>{{ items.length }}</b> | ||||
|           {{ $t('general.of') }} <b>{{ totalItems }}</b> | ||||
|         </p> | ||||
|  | ||||
|         <sw-transition> | ||||
|           <sw-dropdown v-if="selectedItems.length"> | ||||
|             <span | ||||
|               slot="activator" | ||||
|               class="flex block text-sm font-medium cursor-pointer select-none text-primary-400" | ||||
|             > | ||||
|               {{ $t('general.actions') }} | ||||
|               <chevron-down-icon class="h-5" /> | ||||
|             </span> | ||||
|             <v-dropdown-item> | ||||
|               <div class="dropdown-item" @click="removeMultipleItems"> | ||||
|                 <font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> | ||||
|                 {{ $t('general.delete') }} | ||||
|               </div> | ||||
|             </v-dropdown-item> | ||||
|           </v-dropdown> | ||||
|         </transition> | ||||
|  | ||||
|             <sw-dropdown-item @click="removeMultipleItems"> | ||||
|               <trash-icon class="h-5 mr-3 text-gray-600" /> | ||||
|               {{ $t('general.delete') }} | ||||
|             </sw-dropdown-item> | ||||
|           </sw-dropdown> | ||||
|         </sw-transition> | ||||
|       </div> | ||||
|  | ||||
|       <div class="custom-control custom-checkbox"> | ||||
|         <input | ||||
|           id="select-all" | ||||
|       <div class="absolute z-10 items-center pl-4 mt-2 select-none md:mt-12"> | ||||
|         <sw-checkbox | ||||
|           v-model="selectAllFieldStatus" | ||||
|           type="checkbox" | ||||
|           class="custom-control-input" | ||||
|           variant="primary" | ||||
|           size="sm" | ||||
|           class="hidden md:inline" | ||||
|           @change="selectAllItems" | ||||
|         > | ||||
|         <label v-show="!isRequestOngoing" for="select-all" class="custom-control-label selectall"> | ||||
|           <span class="select-all-label">{{ $t('general.select_all') }} </span> | ||||
|         </label> | ||||
|         /> | ||||
|  | ||||
|         <sw-checkbox | ||||
|           v-model="selectAllFieldStatus" | ||||
|           :label="$t('general.select_all')" | ||||
|           variant="primary" | ||||
|           size="sm" | ||||
|           class="md:hidden" | ||||
|           @change="selectAllItems" | ||||
|         /> | ||||
|       </div> | ||||
|  | ||||
|       <table-component | ||||
|       <sw-table-component | ||||
|         ref="table" | ||||
|         :data="fetchData" | ||||
|         :show-filter="false" | ||||
|         table-class="table" | ||||
|       > | ||||
|  | ||||
|         <table-column | ||||
|         <sw-table-column | ||||
|           :sortable="false" | ||||
|           :filterable="false" | ||||
|           cell-class="no-click" | ||||
|         > | ||||
|           <div slot-scope="row" class="custom-control custom-checkbox"> | ||||
|             <sw-checkbox | ||||
|               :id="row.id" | ||||
|               v-model="selectField" | ||||
|               :value="row.id" | ||||
|               variant="primary" | ||||
|               size="sm" | ||||
|             /> | ||||
|           </div> | ||||
|         </sw-table-column> | ||||
|  | ||||
|         <sw-table-column :sortable="true" :label="$t('items.name')" show="name"> | ||||
|           <template slot-scope="row"> | ||||
|             <div class="custom-control custom-checkbox"> | ||||
|               <input | ||||
|                 :id="row.id" | ||||
|                 v-model="selectField" | ||||
|                 :value="row.id" | ||||
|                 type="checkbox" | ||||
|                 class="custom-control-input" | ||||
|               > | ||||
|               <label :for="row.id" class="custom-control-label"/> | ||||
|             </div> | ||||
|             <span>{{ $t('items.name') }}</span> | ||||
|             <router-link | ||||
|               :to="{ path: `items/${row.id}/edit` }" | ||||
|               class="font-medium text-primary-500" | ||||
|             > | ||||
|               {{ row.name }} | ||||
|             </router-link> | ||||
|           </template> | ||||
|         </table-column> | ||||
|         <table-column | ||||
|           :label="$t('items.name')" | ||||
|           show="name" | ||||
|         /> | ||||
|         <table-column | ||||
|         </sw-table-column> | ||||
|  | ||||
|         <sw-table-column | ||||
|           :sortable="true" | ||||
|           :label="$t('items.unit')" | ||||
|           show="unit_name" | ||||
|         /> | ||||
|         <table-column | ||||
|         > | ||||
|           <template slot-scope="row"> | ||||
|             <span>{{ $t('items.unit') }}</span> | ||||
|  | ||||
|             <span> | ||||
|               {{ row.unit_name ? row.unit_name : 'Not selected' }} | ||||
|             </span> | ||||
|           </template> | ||||
|         </sw-table-column> | ||||
|  | ||||
|         <sw-table-column | ||||
|           :sortable="true" | ||||
|           :label="$t('items.price')" | ||||
|           show="price" | ||||
|         > | ||||
|           <template slot-scope="row"> | ||||
|             <span> {{ $t('items.price') }} </span> | ||||
|  | ||||
|             <div v-html="$utils.formatMoney(row.price, defaultCurrency)" /> | ||||
|           </template> | ||||
|         </table-column> | ||||
|         <table-column | ||||
|         </sw-table-column> | ||||
|  | ||||
|         <sw-table-column | ||||
|           :sortable="true" | ||||
|           :label="$t('items.added_on')" | ||||
|           sort-as="created_at" | ||||
|           show="formattedCreatedAt" | ||||
|         /> | ||||
|         <table-column | ||||
|           :sortable="false" | ||||
|  | ||||
|         <sw-table-column | ||||
|           :sortable="true" | ||||
|           :filterable="false" | ||||
|           cell-class="action-dropdown" | ||||
|         > | ||||
|           <template slot-scope="row"> | ||||
|             <span> {{ $t('items.action') }} </span> | ||||
|             <v-dropdown> | ||||
|               <a slot="activator" href="#"> | ||||
|                 <dot-icon /> | ||||
|               </a> | ||||
|               <v-dropdown-item> | ||||
|  | ||||
|                 <router-link :to="{path: `items/${row.id}/edit`}" class="dropdown-item"> | ||||
|                   <font-awesome-icon :icon="['fas', 'pencil-alt']" class="dropdown-item-icon" /> | ||||
|                   {{ $t('general.edit') }} | ||||
|                 </router-link> | ||||
|             <sw-dropdown> | ||||
|               <dot-icon slot="activator" /> | ||||
|  | ||||
|               </v-dropdown-item> | ||||
|               <v-dropdown-item> | ||||
|                 <div class="dropdown-item" @click="removeItems(row.id)"> | ||||
|                   <font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> | ||||
|                   {{ $t('general.delete') }} | ||||
|                 </div> | ||||
|               </v-dropdown-item> | ||||
|             </v-dropdown> | ||||
|               <sw-dropdown-item | ||||
|                 tag-name="router-link" | ||||
|                 :to="`items/${row.id}/edit`" | ||||
|               > | ||||
|                 <pencil-icon class="h-5 mr-3 text-gray-600" /> | ||||
|                 {{ $t('general.edit') }} | ||||
|               </sw-dropdown-item> | ||||
|  | ||||
|               <sw-dropdown-item @click="removeItems(row.id)"> | ||||
|                 <trash-icon class="h-5 mr-3 text-gray-600" /> | ||||
|                 {{ $t('general.delete') }} | ||||
|               </sw-dropdown-item> | ||||
|             </sw-dropdown> | ||||
|           </template> | ||||
|         </table-column> | ||||
|       </table-component> | ||||
|         </sw-table-column> | ||||
|       </sw-table-component> | ||||
|     </div> | ||||
|   </div> | ||||
|   </base-page> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapActions, mapGetters } from 'vuex' | ||||
| import DotIcon from '../../components/icon/DotIcon' | ||||
| import { | ||||
|   FilterIcon, | ||||
|   XIcon, | ||||
|   ChevronDownIcon, | ||||
|   PencilIcon, | ||||
|   TrashIcon, | ||||
|   PlusIcon, | ||||
| } from '@vue-hero-icons/solid' | ||||
| import SatelliteIcon from '../../components/icon/SatelliteIcon' | ||||
| import BaseButton from '../../../js/components/base/BaseButton' | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
|     DotIcon, | ||||
|     SatelliteIcon, | ||||
|     BaseButton | ||||
|     FilterIcon, | ||||
|     XIcon, | ||||
|     PlusIcon, | ||||
|     ChevronDownIcon, | ||||
|     PencilIcon, | ||||
|     TrashIcon, | ||||
|   }, | ||||
|   data () { | ||||
|  | ||||
|   data() { | ||||
|     return { | ||||
|       id: null, | ||||
|       showFilters: false, | ||||
|       sortedBy: 'created_at', | ||||
|       isRequestOngoing: true, | ||||
|       filtersApplied: false, | ||||
|  | ||||
|       filters: { | ||||
|         name: '', | ||||
|         unit: '', | ||||
|         price: '' | ||||
|       } | ||||
|         price: '', | ||||
|       }, | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     ...mapGetters('item', [ | ||||
|       'items', | ||||
|       'selectedItems', | ||||
|       'totalItems', | ||||
|       'selectAllField', | ||||
|       'itemUnits' | ||||
|       'itemUnits', | ||||
|     ]), | ||||
|     ...mapGetters('currency', [ | ||||
|       'defaultCurrency' | ||||
|     ]), | ||||
|     showEmptyScreen () { | ||||
|       return !this.totalItems && !this.isRequestOngoing && !this.filtersApplied | ||||
|  | ||||
|     ...mapGetters('company', ['defaultCurrency']), | ||||
|  | ||||
|     showEmptyScreen() { | ||||
|       return !this.totalItems && !this.isRequestOngoing | ||||
|     }, | ||||
|     filterIcon () { | ||||
|       return (this.showFilters) ? 'times' : 'filter' | ||||
|  | ||||
|     filterIcon() { | ||||
|       return this.showFilters ? 'x-icon' : 'filter-icon' | ||||
|     }, | ||||
|  | ||||
|     selectField: { | ||||
|       get: function () { | ||||
|         return this.selectedItems | ||||
|       }, | ||||
|       set: function (val) { | ||||
|         this.selectItem(val) | ||||
|       } | ||||
|       }, | ||||
|     }, | ||||
|  | ||||
|     selectAllFieldStatus: { | ||||
|       get: function () { | ||||
|         return this.selectAllField | ||||
|       }, | ||||
|       set: function (val) { | ||||
|         this.setSelectAllState(val) | ||||
|       } | ||||
|     } | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   watch: { | ||||
|     filters: { | ||||
|       handler: 'setFilters', | ||||
|       deep: true | ||||
|     } | ||||
|       deep: true, | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   destroyed () { | ||||
|   mounted() { | ||||
|     this.fetchItemUnits({ limit: 'all' }) | ||||
|   }, | ||||
|  | ||||
|   destroyed() { | ||||
|     if (this.selectAllField) { | ||||
|       this.selectAllItems() | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     ...mapActions('item', [ | ||||
|       'fetchItems', | ||||
| @ -297,19 +344,22 @@ export default { | ||||
|       'selectItem', | ||||
|       'deleteItem', | ||||
|       'deleteMultipleItems', | ||||
|       'setSelectAllState' | ||||
|       'setSelectAllState', | ||||
|       'fetchItemUnits', | ||||
|     ]), | ||||
|     refreshTable () { | ||||
|  | ||||
|     refreshTable() { | ||||
|       this.$refs.table.refresh() | ||||
|     }, | ||||
|     async fetchData ({ page, filter, sort }) { | ||||
|  | ||||
|     async fetchData({ page, filter, sort }) { | ||||
|       let data = { | ||||
|         search: this.filters.name !== null ? this.filters.name : '', | ||||
|         unit_id: this.filters.unit !== null ? this.filters.unit.id : '', | ||||
|         price: this.filters.price * 100, | ||||
|         orderByField: sort.fieldName || 'created_at', | ||||
|         orderBy: sort.order || 'desc', | ||||
|         page | ||||
|         page, | ||||
|       } | ||||
|  | ||||
|       this.isRequestOngoing = true | ||||
| @ -320,44 +370,43 @@ export default { | ||||
|         data: response.data.items.data, | ||||
|         pagination: { | ||||
|           totalPages: response.data.items.last_page, | ||||
|           currentPage: page | ||||
|         } | ||||
|           currentPage: page, | ||||
|         }, | ||||
|       } | ||||
|     }, | ||||
|     setFilters () { | ||||
|       this.filtersApplied = true | ||||
|  | ||||
|     setFilters() { | ||||
|       this.refreshTable() | ||||
|     }, | ||||
|     clearFilter () { | ||||
|  | ||||
|     clearFilter() { | ||||
|       this.filters = { | ||||
|         name: '', | ||||
|         unit: '', | ||||
|         price: '' | ||||
|         price: '', | ||||
|       } | ||||
|  | ||||
|       this.$nextTick(() => { | ||||
|         this.filtersApplied = false | ||||
|       }) | ||||
|     }, | ||||
|     toggleFilter () { | ||||
|       if (this.showFilters && this.filtersApplied) { | ||||
|  | ||||
|     toggleFilter() { | ||||
|       if (this.showFilters) { | ||||
|         this.clearFilter() | ||||
|         this.refreshTable() | ||||
|       } | ||||
|  | ||||
|       this.showFilters = !this.showFilters | ||||
|     }, | ||||
|     async removeItems (id) { | ||||
|  | ||||
|     async removeItems(id) { | ||||
|       this.id = id | ||||
|       swal({ | ||||
|         title: this.$t('general.are_you_sure'), | ||||
|         text: this.$tc('items.confirm_delete'), | ||||
|         icon: '/assets/icon/trash-solid.svg', | ||||
|         buttons: true, | ||||
|         dangerMode: true | ||||
|         dangerMode: true, | ||||
|       }).then(async (willDelete) => { | ||||
|         if (willDelete) { | ||||
|           let res = await this.deleteItem(this.id) | ||||
|           let res = await this.deleteItem({ ids: [id] }) | ||||
|  | ||||
|           if (res.data.success) { | ||||
|             window.toastr['success'](this.$tc('items.deleted_message', 1)) | ||||
|             this.$refs.table.refresh() | ||||
| @ -365,7 +414,10 @@ export default { | ||||
|           } | ||||
|  | ||||
|           if (res.data.error === 'item_attached') { | ||||
|             window.toastr['error'](this.$tc('items.item_attached_message'), this.$t('general.action_failed')) | ||||
|             window.toastr['error']( | ||||
|               this.$tc('items.item_attached_message'), | ||||
|               this.$t('general.action_failed') | ||||
|             ) | ||||
|             return true | ||||
|           } | ||||
|  | ||||
| @ -374,16 +426,18 @@ export default { | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     async removeMultipleItems () { | ||||
|  | ||||
|     async removeMultipleItems() { | ||||
|       swal({ | ||||
|         title: this.$t('general.are_you_sure'), | ||||
|         text: this.$tc('items.confirm_delete', 2), | ||||
|         icon: '/assets/icon/trash-solid.svg', | ||||
|         buttons: true, | ||||
|         dangerMode: true | ||||
|         dangerMode: true, | ||||
|       }).then(async (willDelete) => { | ||||
|         if (willDelete) { | ||||
|           let res = await this.deleteMultipleItems() | ||||
|  | ||||
|           if (res.data.success || res.data.items) { | ||||
|             window.toastr['success'](this.$tc('items.deleted_message', 2)) | ||||
|             this.$refs.table.refresh() | ||||
| @ -392,7 +446,7 @@ export default { | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
		Reference in New Issue
	
	Block a user