mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 19:51:09 -04:00
506 lines
14 KiB
Vue
506 lines
14 KiB
Vue
<template>
|
|
<div class="custom-field-modal">
|
|
<form action="" @submit.prevent="submitCustomFieldData">
|
|
<div
|
|
class="px-8 py-8 overflow-y-auto sw-scroll sm:p-6"
|
|
style="max-height: 600px"
|
|
>
|
|
<sw-input-group
|
|
:label="$t('settings.custom_fields.name')"
|
|
:error="nameError"
|
|
horizontal
|
|
required
|
|
>
|
|
<sw-input
|
|
ref="name"
|
|
:invalid="$v.formData.name.$error"
|
|
v-model="formData.name"
|
|
type="text"
|
|
@input="$v.formData.name.$touch()"
|
|
/>
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
:label="$t('settings.custom_fields.model')"
|
|
:error="modalTypeError"
|
|
class="mt-5"
|
|
horizontal
|
|
required
|
|
>
|
|
<sw-select
|
|
v-model="formData.model_type"
|
|
:options="modelTypes"
|
|
:invalid="$v.formData.model_type.$error"
|
|
:searchable="true"
|
|
:show-labels="false"
|
|
:allow-empty="false"
|
|
@input="$v.formData.model_type.$touch()"
|
|
/>
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
:label="$t('settings.custom_fields.required')"
|
|
class="mt-5"
|
|
horizontal
|
|
>
|
|
<sw-switch v-model="formData.is_required" style="margin-top: -20px" />
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
:label="$t('settings.custom_fields.type')"
|
|
:error="dataTypeError"
|
|
class="mt-5"
|
|
horizontal
|
|
required
|
|
>
|
|
<sw-select
|
|
v-model="selectType"
|
|
:options="dataTypes"
|
|
:invalid="$v.selectType.$error"
|
|
:searchable="true"
|
|
:show-labels="false"
|
|
:allow-empty="false"
|
|
track-by="label"
|
|
label="label"
|
|
@input="onSelectTypeChange"
|
|
/>
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
:label="$t('settings.custom_fields.label')"
|
|
:error="labelError"
|
|
class="mt-5"
|
|
horizontal
|
|
required
|
|
>
|
|
<sw-input
|
|
ref="name"
|
|
:invalid="$v.formData.label.$error"
|
|
v-model="formData.label"
|
|
type="text"
|
|
@input="$v.formData.label.$touch()"
|
|
/>
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
v-if="isDropdownSelected"
|
|
:label="$t('settings.custom_fields.options')"
|
|
class="mt-5"
|
|
horizontal
|
|
>
|
|
<option-create @onAdd="addNewOptions" />
|
|
<div
|
|
v-for="(option, index) in formData.options"
|
|
:key="index"
|
|
class="flex items-center"
|
|
style="margin-top: 5px"
|
|
>
|
|
<sw-input v-model="option.name" type="text" style="width: 90%" />
|
|
<minus-circle-icon
|
|
class="ml-1 cursor-pointer icon text-danger"
|
|
@click="removeOption(index)"
|
|
/>
|
|
</div>
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
v-if="formData.type"
|
|
:label="$t('settings.custom_fields.default_value')"
|
|
horizontal
|
|
class="relative mt-5"
|
|
>
|
|
<component
|
|
:value="formData.default_answer"
|
|
:is="formData.type + 'Type'"
|
|
:options="formData.options"
|
|
:default-date-time="formData.dateTimeValue"
|
|
v-model="formData.default_answer"
|
|
/>
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
v-if="!isSwitchTypeSelected"
|
|
:label="$t('settings.custom_fields.placeholder')"
|
|
class="mt-5"
|
|
horizontal
|
|
>
|
|
<sw-input v-model="formData.placeholder" type="text" />
|
|
</sw-input-group>
|
|
<sw-input-group
|
|
:label="$t('settings.custom_fields.order')"
|
|
:error="orderError"
|
|
class="mt-5"
|
|
horizontal
|
|
>
|
|
<sw-input
|
|
v-model="formData.order"
|
|
:invalid="$v.formData.order.$error"
|
|
type="number"
|
|
@input="$v.formData.order.$touch()"
|
|
/>
|
|
</sw-input-group>
|
|
</div>
|
|
<div
|
|
class="z-0 flex justify-end p-4 border-t border-solid border-gray-light border-modal-bg"
|
|
>
|
|
<sw-button
|
|
class="mr-3"
|
|
type="button"
|
|
variant="primary-outline"
|
|
@click="closeCategoryModal"
|
|
>
|
|
{{ $t('general.cancel') }}
|
|
</sw-button>
|
|
<sw-button
|
|
:loading="isLoading"
|
|
:disabled="isLoading"
|
|
variant="primary"
|
|
type="submit"
|
|
>
|
|
<save-icon v-if="!isLoading" class="mr-2" />
|
|
{{ !isEdit ? $t('general.save') : $t('general.update') }}
|
|
</sw-button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapActions, mapGetters } from 'vuex'
|
|
import { MinusCircleIcon } from '@vue-hero-icons/solid'
|
|
import InputType from './types/InputType'
|
|
import NumberType from './types/NumberType'
|
|
import SwitchType from './types/SwitchType'
|
|
import TextAreaType from './types/TextAreaType'
|
|
import TimeType from './types/TimeType'
|
|
import UrlType from './types/UrlType'
|
|
import PhoneType from './types/PhoneType'
|
|
import DateTimeType from './types/DateTimeType'
|
|
import DateType from './types/DateType'
|
|
import DropdownType from './types/DropdownType'
|
|
|
|
import OptionCreate from './types/OptionsCreate'
|
|
import moment from 'moment'
|
|
|
|
const {
|
|
required,
|
|
requiredIf,
|
|
numeric,
|
|
minLength,
|
|
} = require('vuelidate/lib/validators')
|
|
|
|
export default {
|
|
components: {
|
|
MinusCircleIcon,
|
|
OptionCreate,
|
|
InputType,
|
|
NumberType,
|
|
SwitchType,
|
|
TextAreaType,
|
|
TimeType,
|
|
UrlType,
|
|
PhoneType,
|
|
DateTimeType,
|
|
DateType,
|
|
DropdownType,
|
|
},
|
|
data() {
|
|
return {
|
|
isEdit: false,
|
|
dateType: 'custom',
|
|
isLoading: false,
|
|
relativeDateTypes: [
|
|
'Today',
|
|
'Tomorrow',
|
|
'Yesterday',
|
|
'Starting Date of Week',
|
|
'Ending Date of Week',
|
|
'Starting Date of Next Week',
|
|
'Ending Date of Next Week',
|
|
'Starting Date of Previous Week',
|
|
'Ending Date of Previous Week',
|
|
'Starting Date of Month',
|
|
'Ending Date of Month',
|
|
'Starting Date of Next Month',
|
|
'Ending Date of Next Month',
|
|
'Starting Date of Previous Month',
|
|
'Ending Date of Previous Month',
|
|
'Starting Date of Fiscal Month',
|
|
'Ending Date of Fiscal Month',
|
|
],
|
|
dataTypes: [
|
|
{ label: 'Text', value: 'Input' },
|
|
{ label: 'Textarea', value: 'TextArea' },
|
|
{ label: 'Phone', value: 'Phone' },
|
|
{ label: 'URL', value: 'Url' },
|
|
{ label: 'Number', value: 'Number' },
|
|
{ label: 'Select Field', value: 'Dropdown' },
|
|
{ label: 'Switch Toggle', value: 'Switch' },
|
|
{ label: 'Date', value: 'Date' },
|
|
{ label: 'Time', value: 'Time' },
|
|
{ label: 'Date & Time', value: 'DateTime' },
|
|
],
|
|
modelTypes: ['Customer', 'Invoice', 'Estimate', 'Expense', 'Payment'],
|
|
selectType: null,
|
|
formData: {
|
|
label: null,
|
|
type: null,
|
|
name: null,
|
|
default_answer: null,
|
|
is_required: false,
|
|
placeholder: null,
|
|
model_type: null,
|
|
order: 1,
|
|
options: [],
|
|
},
|
|
}
|
|
},
|
|
validations: {
|
|
selectType: {
|
|
required,
|
|
},
|
|
formData: {
|
|
name: {
|
|
required,
|
|
},
|
|
label: {
|
|
required,
|
|
},
|
|
model_type: {
|
|
required,
|
|
},
|
|
order: {
|
|
required,
|
|
numeric,
|
|
},
|
|
options: {
|
|
required: requiredIf('isDropdownSelected'),
|
|
minLength: minLength(1),
|
|
$each: {
|
|
name: {
|
|
required: requiredIf('isDropdownSelected'),
|
|
minLength: minLength(1),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
computed: {
|
|
...mapGetters('modal', ['modalData', 'modalDataID', 'refreshData']),
|
|
isDropdownSelected() {
|
|
if (this.selectType && this.selectType.label === 'Select Field') {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
isSwitchTypeSelected() {
|
|
if (this.selectType && this.selectType.label === 'Switch Toggle') {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
nameError() {
|
|
if (!this.$v.formData.name.$error) {
|
|
return ''
|
|
}
|
|
|
|
if (!this.$v.formData.name.required) {
|
|
return this.$tc('validation.required')
|
|
}
|
|
},
|
|
labelError() {
|
|
if (!this.$v.formData.label.$error) {
|
|
return ''
|
|
}
|
|
|
|
if (!this.$v.formData.label.required) {
|
|
return this.$tc('validation.required')
|
|
}
|
|
},
|
|
modalTypeError() {
|
|
if (!this.$v.formData.model_type.$error) {
|
|
return ''
|
|
}
|
|
|
|
if (!this.$v.formData.model_type.required) {
|
|
return this.$tc('validation.required')
|
|
}
|
|
},
|
|
dataTypeError() {
|
|
if (!this.$v.selectType.$error) {
|
|
return ''
|
|
}
|
|
|
|
if (!this.$v.selectType.required) {
|
|
return this.$tc('validation.required')
|
|
}
|
|
},
|
|
hasPlaceHolder() {
|
|
if (this.selectType.label == 'Switch Toggle') {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
orderError() {
|
|
if (!this.$v.formData.order.$error) {
|
|
return ''
|
|
}
|
|
|
|
if (!this.$v.formData.order.required) {
|
|
return this.$tc('validation.required')
|
|
}
|
|
|
|
if (!this.$v.formData.order.numeric) {
|
|
return this.$tc('validation.numbers_only')
|
|
}
|
|
},
|
|
},
|
|
watch: {
|
|
'formData.type'(newValue, oldvalue) {
|
|
if (oldvalue != null || oldvalue != undefined) {
|
|
this.onChangeReset()
|
|
}
|
|
},
|
|
dateType(newValue, oldvalue) {
|
|
if (oldvalue != null || oldvalue != undefined) {
|
|
this.onChangeReset()
|
|
}
|
|
},
|
|
},
|
|
mounted() {
|
|
if (this.modalDataID) {
|
|
this.setData()
|
|
return true
|
|
}
|
|
this.formData.model_type = this.modelTypes[0]
|
|
this.selectType = this.dataTypes[0]
|
|
this.formData.type = this.dataTypes[0].value
|
|
},
|
|
methods: {
|
|
...mapActions('customFields', [
|
|
'addCustomField',
|
|
'updateCustomField',
|
|
'fetchCustomField',
|
|
]),
|
|
...mapActions('modal', ['closeModal']),
|
|
...mapActions('notification', ['showNotification']),
|
|
resetFormData() {
|
|
this.formData = {
|
|
label: null,
|
|
label: null,
|
|
type: null,
|
|
dateTimeValue: null,
|
|
default_answer: null,
|
|
is_required: false,
|
|
placeholder: null,
|
|
model_type: null,
|
|
options: [{ name: '' }],
|
|
}
|
|
this.$v.$reset()
|
|
},
|
|
async submitCustomFieldData() {
|
|
this.$v.selectType.$touch()
|
|
this.$v.formData.$touch()
|
|
|
|
if (this.$v.$invalid) {
|
|
return false
|
|
}
|
|
|
|
let data = {
|
|
...this.formData,
|
|
options: this.formData.options.map((option) => option.name),
|
|
default_answer:
|
|
this.isDropdownSelected && this.formData.default_answer
|
|
? this.formData.default_answer.name
|
|
: this.formData.default_answer,
|
|
}
|
|
if (this.isSwitchTypeSelected && this.formData.default_answer == null) {
|
|
data.default_answer = false
|
|
}
|
|
if (data.type == 'Date') {
|
|
data.default_answer = data.default_answer
|
|
? moment(data.default_answer).format('YYYY-MM-DD')
|
|
: null
|
|
}
|
|
if (data.type == 'Time' && typeof data.default_answer == 'object') {
|
|
let HH =
|
|
data && data.default_answer && data.default_answer.HH
|
|
? data.default_answer.HH
|
|
: null
|
|
let mm =
|
|
data && data.default_answer && data.default_answer.mm
|
|
? data.default_answer.mm
|
|
: null
|
|
let ss =
|
|
data && data.default_answer && data.default_answer.ss
|
|
? data.default_answer.ss
|
|
: null
|
|
data.default_answer = `${HH}:${mm}:${ss}`
|
|
}
|
|
let response = null
|
|
if (this.isEdit) {
|
|
this.isLoading = true
|
|
response = await this.updateCustomField(data)
|
|
this.showNotification({
|
|
type: 'success',
|
|
message: this.$tc('settings.custom_fields.updated_message'),
|
|
})
|
|
this.refreshData()
|
|
this.closeCategoryModal()
|
|
return true
|
|
}
|
|
|
|
this.isLoading = true
|
|
response = await this.addCustomField(data)
|
|
|
|
this.showNotification({
|
|
type: 'success',
|
|
message: this.$tc('settings.custom_fields.added_message'),
|
|
})
|
|
this.refreshData()
|
|
this.closeCategoryModal()
|
|
return true
|
|
},
|
|
addNewOptions(option) {
|
|
this.formData.options = [{ name: option }, ...this.formData.options]
|
|
},
|
|
removeOption(index) {
|
|
this.formData.options.splice(index, 1)
|
|
},
|
|
async setData() {
|
|
let response = await this.fetchCustomField(this.modalDataID)
|
|
let fieldData = response.data.customField
|
|
this.isEdit = true
|
|
let data = {
|
|
...fieldData,
|
|
options: [],
|
|
dateTimeValue: fieldData.defaultAnswer,
|
|
default_answer: fieldData.defaultAnswer,
|
|
options: fieldData.options
|
|
? fieldData.options.map((option) => {
|
|
return { name: option ? option : '' }
|
|
})
|
|
: [],
|
|
}
|
|
this.selectType = this.dataTypes.find(
|
|
(type) => type.value == fieldData.type
|
|
)
|
|
if (data.type == 'Dropdown') {
|
|
data.default_answer = { name: fieldData.defaultAnswer }
|
|
}
|
|
this.formData = { ...data }
|
|
},
|
|
onChangeReset() {
|
|
this.formData = {
|
|
...this.formData,
|
|
default_answer: null,
|
|
is_required: false,
|
|
placeholder: null,
|
|
options: [],
|
|
}
|
|
},
|
|
onSelectTypeChange(data) {
|
|
this.formData.type = data.value
|
|
this.$v.selectType.$touch()
|
|
},
|
|
closeCategoryModal() {
|
|
this.resetFormData()
|
|
this.closeModal()
|
|
},
|
|
},
|
|
}
|
|
</script>
|