build version 400

This commit is contained in:
Mohit Panjwani
2020-12-02 17:54:08 +05:30
parent 326508e567
commit 89ee58590c
963 changed files with 62887 additions and 48868 deletions

View File

@@ -0,0 +1,179 @@
<template>
<div class="relative customer-modal">
<base-loader
v-if="isRequestOngoing"
class="h-130"
:show-bg-overlay="true"
/>
<form @submit.prevent="createNewBackup">
<div class="p-6">
<sw-input-group
:label="$t('settings.backup.select_backup_type')"
:error="optionError"
horizontal
required
class="py-2"
>
<sw-select
v-model="formData.option"
:options="options"
:searchable="true"
:show-labels="false"
:placeholder="$t('settings.backup.select_backup_type')"
:allow-empty="false"
:maxHeight="100"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.select_disk')"
:error="selectDiskError"
horizontal
required
class="py-2"
>
<sw-select
v-model="formData.selected_disk"
:options="getDisks"
:searchable="true"
:show-labels="false"
:placeholder="$t('settings.disk.select_disk')"
:allow-empty="false"
track-by="id"
:preselect-first="true"
:custom-label="getCustomLabel"
:maxHeight="100"
:loading="isLoading"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
>
<sw-button
class="mr-3"
variant="primary-outline"
type="button"
@click="cancelBackup"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isCreateLoading"
variant="primary"
type="submit"
:disabled="isCreateLoading"
>
<save-icon v-if="!isCreateLoading" class="mr-2" />
{{ $t('general.create') }}
</sw-button>
</div>
</form>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import AddressStub from '../../../stub/address'
import _ from 'lodash'
const { required } = require('vuelidate/lib/validators')
export default {
data() {
return {
isLoading: false,
isCreateLoading: false,
isRequestOngoing: false,
formData: {
option: 'full',
selected_disk: { driver: 'local' },
},
options: ['full', 'only-db', 'only-files'],
}
},
validations: {
formData: {
option: {
required,
},
selected_disk: {
required,
},
},
},
computed: {
...mapGetters('disks', ['getDisks']),
...mapGetters('modal', ['refreshData']),
optionError() {
if (!this.$v.formData.option.$error) {
return ''
}
if (!this.$v.formData.option) {
return this.$tc('validation.required')
}
},
selectDiskError() {
if (!this.$v.formData.selected_disk.$error) {
return ''
}
if (!this.$v.formData.selected_disk) {
return this.$tc('validation.required')
}
},
},
created() {
this.loadData()
},
methods: {
...mapActions('backup', ['createBackup']),
...mapActions('disks', ['fetchDisks']),
...mapActions('modal', ['closeModal']),
getCustomLabel({ driver, name }) {
return `${name} — [${driver}]`
},
async createNewBackup() {
let data = {
option: this.formData.option,
file_disk_id: this.formData.selected_disk.id,
}
try {
this.isCreateLoading = true
await this.createBackup(data)
this.isCreateLoading = false
window.toastr['success'](this.$t('settings.backup.created_message'))
this.refreshData ? this.refreshData() : ''
this.cancelBackup()
} catch (e) {
this.isCreateLoading = false
window.toastr['error'](e.response.data.message)
}
},
async loadData() {
this.isRequestOngoing = true
let res = await this.fetchDisks({ limit: 'all' })
this.formData.selected_disk = res.data.disks.data[0]
this.isRequestOngoing = false
},
cancelBackup() {
this.closeModal()
},
},
}
</script>

View File

@@ -1,19 +1,22 @@
<template>
<transition name="fade">
<div v-if="modalActive" :class="'size-' + modalSize" class="base-modal">
<div class="modal-body">
<div class="close-icon" @click="closeModal">
<font-awesome-icon icon="times" />
<div>
<sw-modal ref="baseModal" :variant="variant">
<template v-slot:header>
<div
class="absolute flex content-center justify-center w-5 cursor-pointer"
style="top: 20px; right: 15px"
@click="closeModal"
>
<x-icon />
</div>
<div class="modal-header p-3">
<h5 class="modal-heading">{{ modalTitle }}</h5>
</div>
<component :is="component" />
</div>
</div>
</transition>
<span>{{ modalTitle }}</span>
</template>
<component :is="component" />
</sw-modal>
</div>
</template>
<script>
import { XIcon } from '@vue-hero-icons/solid'
import { mapActions, mapGetters } from 'vuex'
import TaxTypeModal from './TaxTypeModal'
import ItemModal from './ItemModal'
@@ -21,9 +24,17 @@ import EstimateTemplate from './EstimateTemplate'
import InvoiceTemplate from './InvoiceTemplate'
import CustomerModal from './CustomerModal'
import CategoryModal from './CategoryModal'
import BackupModal from './BackupModal'
import PaymentMode from './PaymentModeModal'
import ItemUnit from './ItemUnitModal'
import MailTestModal from './MailTestModal'
import SendInvoiceModal from './SendInvoiceModal'
import SendEstimateModal from './SendEstimateModal'
import SendPaymentModal from './SendPaymentModal'
import FileDiskModal from './FileDiskModal'
import SetDefaultDiskModal from './SetDefaultDiskModal'
import CustomFieldModal from './CustomField/Index'
import NoteSelectModal from './NoteModal'
export default {
components: {
@@ -33,14 +44,22 @@ export default {
InvoiceTemplate,
CustomerModal,
CategoryModal,
BackupModal,
PaymentMode,
ItemUnit,
MailTestModal
MailTestModal,
SendInvoiceModal,
SendEstimateModal,
SendPaymentModal,
XIcon,
FileDiskModal,
SetDefaultDiskModal,
CustomFieldModal,
NoteSelectModal,
},
data () {
data() {
return {
component: '',
hasFocus: false
}
},
computed: {
@@ -49,31 +68,28 @@ export default {
'modalTitle',
'componentName',
'modalSize',
'modalData'
])
'modalData',
'variant',
]),
},
watch: {
componentName (component) {
componentName(component) {
if (!component) {
return
}
this.component = component
}
},
modalActive(status) {
if (status) {
this.$refs.baseModal.open()
return true
}
this.$refs.baseModal.close()
return false
},
},
methods: {
...mapActions('modal', [
'openModal',
'closeModal'
])
}
...mapActions('modal', ['openModal', 'closeModal']),
},
}
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
</style>

View File

@@ -1,98 +1,115 @@
<template>
<div class="category-modal">
<form action="" @submit.prevent="submitCategoryData">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('expenses.category') }}<span class="required text-danger">*</span></label>
<div class="col-sm-7">
<base-input
ref="name"
:invalid="$v.formData.name.$error"
v-model="formData.name"
type="text"
@input="$v.formData.name.$touch()"
/>
<form action="" @submit.prevent="submitCategoryData">
<div class="p-8 sm:p-6">
<sw-input-group
:label="$t('expenses.category')"
:error="nameError"
variant="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>
<div v-if="$v.formData.name.$error">
<span v-if="!$v.formData.name.required" class="text-danger">{{ $tc('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>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('expenses.description') }}</label>
<div class="col-sm-7">
<base-text-area
v-model="formData.description"
rows="4"
cols="50"
@input="$v.formData.description.$touch()"
/>
<div v-if="$v.formData.description.$error">
<span v-if="!$v.formData.name.maxLength" class="text-danger"> {{ $tc('validation.description_maxlength') }} </span>
</div>
</div>
</div>
</div>
<div class="card-footer">
<base-button
:outline="true"
class="mr-3"
color="theme"
@click="closeCategoryModal"
>
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
icon="save"
color="theme"
type="submit"
>
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</base-button>
</div>
</form>
</div>
<sw-input-group
:label="$t('expenses.description')"
:error="descriptionError"
class="mt-5"
variant="horizontal"
>
<sw-textarea
v-model="formData.description"
rows="4"
cols="50"
@input="$v.formData.description.$touch()"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid border-modal-bg"
>
<sw-button
type="button"
variant="primary-outline"
class="mr-3 text-sm"
@click="closeCategoryModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button variant="primary" type="submit" :loading="isLoading">
<save-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</sw-button>
</div>
</form>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { validationMixin } from 'vuelidate'
const { required, minLength, maxLength } = require('vuelidate/lib/validators')
export default {
mixins: [validationMixin],
data () {
data() {
return {
isEdit: false,
isLoading: false,
formData: {
id: null,
name: null,
description: null
}
description: null,
},
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive'
])
'modalActive',
'refreshData',
]),
nameError() {
if (!this.$v.formData.name.$error) {
return ''
}
if (!this.$v.formData.name.required) {
return this.$tc('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 }
)
}
},
descriptionError() {
if (!this.$v.formData.description.$error) {
return ''
}
if (!this.$v.formData.name.maxLength) {
return this.$tc('validation.description_maxlength')
}
},
},
validations: {
formData: {
name: {
required,
minLength: minLength(3)
minLength: minLength(3),
},
description: {
maxLength: maxLength(255)
}
}
maxLength: maxLength(255),
},
},
},
watch: {
'modalDataID' (val) {
modalDataID(val) {
if (val) {
this.isEdit = true
this.setData()
@@ -100,40 +117,33 @@ export default {
this.isEdit = false
}
},
'modalActive' (val) {
modalActive(val) {
if (!this.modalActive) {
this.resetFormData()
}
}
},
},
mounted () {
mounted() {
this.$refs.name.focus = true
if (this.modalDataID) {
this.isEdit = true
this.setData()
}
},
destroyed () {
},
methods: {
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
...mapActions('category', [
'addCategory',
'updateCategory'
]),
resetFormData () {
...mapActions('modal', ['closeModal']),
...mapActions('category', ['addCategory', 'updateCategory']),
resetFormData() {
this.formData = {
id: null,
name: null,
description: null
description: null,
}
this.$v.formData.$reset()
},
async submitCategoryData () {
async submitCategoryData() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
@@ -149,28 +159,33 @@ export default {
if (response.data) {
if (!this.isEdit) {
window.toastr['success'](this.$t('settings.expense_category.created_message'))
window.toastr['success'](
this.$t('settings.expense_category.created_message')
)
} else {
window.toastr['success'](this.$t('settings.expense_category.updated_message'))
window.toastr['success'](
this.$t('settings.expense_category.updated_message')
)
}
window.hub.$emit('newCategory', response.data.category)
this.refreshData ? this.refreshData() : ''
this.closeCategoryModal()
this.isLoading = false
return true
}
window.toastr['error'](response.data.error)
},
async setData () {
async setData() {
this.formData = {
id: this.modalData.id,
name: this.modalData.name,
description: this.modalData.description
description: this.modalData.description,
}
},
closeCategoryModal () {
closeCategoryModal() {
this.resetFormData()
this.closeModal()
}
}
},
},
}
</script>

View File

@@ -0,0 +1,500 @@
<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
:label="$t('settings.custom_fields.options')"
class="mt-5"
v-if="isDropdownSelected"
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
@click="removeOption(index)"
class="ml-1 cursor-pointer icon text-danger"
/>
</div>
</sw-input-group>
<sw-input-group
:label="$t('settings.custom_fields.default_value')"
horizontal
class="relative mt-5"
v-if="formData.type"
>
<component
:value="formData.default_answer"
:is="formData.type + 'Type'"
:options="formData.options"
:defaultDateTime="formData.dateTimeValue"
v-model="formData.default_answer"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.custom_fields.placeholder')"
v-if="!isSwitchTypeSelected"
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']),
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)
window.toastr['success'](
this.$tc('settings.custom_fields.updated_message')
)
this.refreshData()
this.closeCategoryModal()
return true
}
this.isLoading = true
response = await this.addCustomField(data)
window.toastr['success'](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>

View File

@@ -0,0 +1,35 @@
<template>
<base-date-picker
v-model="inputValue"
:enable-time="true"
@input="$emit('input', inputValue)"
/>
</template>
<script>
import moment from 'moment'
export default {
props: {
value: {
type: String,
default: null,
},
defaultDateTime: {
type: String,
default: null,
},
},
data() {
return {
inputValue: this.value ? this.value : moment().format('YYYY-MM-DD hh:mm'),
}
},
watch: {
value(data) {
this.inputValue = data
},
defaultDateTime(data) {
this.dateTimeValue = data
},
},
}
</script>

View File

@@ -0,0 +1,29 @@
<template>
<base-date-picker
:calendar-button="true"
v-model="inputValue"
calendar-button-icon="calendar"
@input="$emit('input', inputValue)"
/>
</template>
<script>
import moment from 'moment'
export default {
props: {
value: {
type: [String, Date],
default: null,
},
},
data() {
return {
inputValue: moment().format('YYYY-MM-DD'),
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,37 @@
<template>
<sw-select
v-model="inputValue"
:options="inputOptions"
:taggable="true"
:show-labels="false"
label="name"
track-by="name"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: [String, Object],
default: null,
},
options: {
type: Array,
default: () => [],
},
},
data() {
return {
inputValue: this.value,
inputOptions: this.options,
}
},
watch: {
options(data) {
this.inputOptions = data
this.inputValue = null
},
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<sw-input
v-model="inputValue"
type="text"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: String,
default: null,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<sw-input
v-model="inputValue"
type="number"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: [String, Number],
default: null,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,41 @@
<template>
<div class="flex items-center" style="margin-top: 5px">
<sw-input
v-model="option"
type="text"
style="width: 90%"
@handleEnter.stop="onAddOption"
/>
<plus-circle-icon
class="ml-1 cursor-pointer text-danger"
@click="onAddOption"
/>
</div>
</template>
<script>
import { PlusCircleIcon } from '@vue-hero-icons/solid'
export default {
components: {
PlusCircleIcon,
},
data() {
return {
option: null,
}
},
methods: {
onAddOption() {
if (
this.option == null ||
this.option == '' ||
this.option == undefined
) {
return true
}
this.$emit('onAdd', this.option)
this.option = null
},
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<sw-input
v-model="inputValue"
type="text"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: [String, Number],
default: null,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<sw-switch
class="-mt-3"
v-model="inputValue"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: [Boolean, Number],
default: false,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,28 @@
<template>
<sw-textarea
v-model="inputValue"
rows="2"
name="description"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: String,
default: null,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,28 @@
<template>
<base-time-picker
:value="inputValue"
v-model="inputValue"
hide-clear-button
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: [String, Object],
default: null,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<sw-input
v-model="inputValue"
type="url"
@input="$emit('input', inputValue)"
/>
</template>
<script>
export default {
props: {
value: {
type: String,
default: null,
},
},
data() {
return {
inputValue: this.value,
}
},
watch: {
value(data) {
this.inputValue = data
},
},
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +1,66 @@
<template>
<div class="template-modal">
<div class="card-body">
<div class="template-container">
<div class="px-8 py-8 sm:p-6">
<div class="flex flex-wrap justify-start p-1 overflow-x-auto sw-scroll">
<div
v-for="(template,index) in modalData"
v-for="(template, index) in modalData"
:key="index"
:class="{'selected-template': selectedTemplate === template.id}"
class="template-img"
:class="{
'border border-solid border-primary-500':
selectedTemplate === template.id,
}"
class="relative m-2 border border-gray-200 border-solid"
>
<img
:src="template.path"
alt="template-image"
height="200" width="140"
height="200"
width="140"
@click="selectedTemplate = template.id"
>
/>
<img
v-if="selectedTemplate === template.id"
class="check-icon"
class="absolute z-10 w-5 h-5 text-primary-500"
style="top: -6px; right: -5px"
src="/assets/img/tick.png"
>
/>
</div>
</div>
</div>
<div class="card-footer">
<base-button outline class="mr-3" color="theme" @click="closeEstimateModal">
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
color="theme"
@click="chooseTemplate()"
<div class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid">
<sw-button
class="mr-3"
variant="primary-outline"
@click="closeEstimateModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button variant="primary" @click="chooseTemplate()">
{{ $t('general.choose') }}
</base-button>
</sw-button>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data () {
data() {
return {
selectedTemplate: 1,
isLoading: false
isLoading: false,
}
},
computed: {
...mapGetters('modal', [
'modalData'
]),
...mapGetters('estimate', [
'getTemplateId'
])
...mapGetters('modal', ['modalData']),
...mapGetters('estimate', ['getTemplateId']),
},
mounted () {
mounted() {
this.selectedTemplate = this.getTemplateId
},
methods: {
...mapActions('estimate', [
'setTemplate'
]),
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
async chooseTemplate () {
...mapActions('estimate', ['setTemplate']),
...mapActions('modal', ['closeModal', 'resetModalData']),
async chooseTemplate() {
this.isLoading = true
let resp = await this.setTemplate(this.selectedTemplate)
if (resp) {
@@ -73,11 +69,11 @@ export default {
this.closeModal()
}
},
closeEstimateModal () {
closeEstimateModal() {
this.selectedTemplate = this.getTemplateId
this.closeModal()
this.resetModalData()
}
}
},
},
}
</script>

View File

@@ -0,0 +1,151 @@
<template>
<div class="file-disk-modal">
<div v-if="getDiskDrivers.length">
<component
:is="selected_disk"
:loading="isLoading"
:disks="getDiskDrivers"
@on-change-disk="(disk) => (selected_disk = disk.value)"
@submit="createNewDisk"
:is-edit="isEdit"
>
<template v-slot="slotProps">
<div
class="z-0 flex justify-end p-4 border-t border-solid border-gray-light"
>
<sw-button
class="mr-3 text-sm"
variant="primary-outline"
@click="closeDisk"
type="button"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isRequestFire(slotProps)"
variant="primary"
:disabled="isRequestFire(slotProps)"
type="submit"
>
<save-icon v-if="!isRequestFire(slotProps)" class="mr-2" />
{{ $t('general.save') }}
</sw-button>
</div>
</template>
</component>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import Dropbox from './disks/DropboxDisk'
import Local from './disks/LocalDisk'
import S3 from './disks/S3Disk'
import DoSpaces from './disks/DoSpacesDisk'
const {
required,
minLength,
email,
numeric,
url,
maxLength,
} = require('vuelidate/lib/validators')
export default {
components: {
Dropbox,
Local,
S3,
DoSpaces,
},
data() {
return {
isLoading: false,
isEdit: false,
set_as_default: false,
name: 'local',
formData: {},
selected_disk: 'local',
diskConfigData: {},
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive',
'refreshData',
]),
...mapGetters('disks', ['getDiskDrivers']),
},
created() {
if (this.modalDataID) {
this.isEdit = true
}
this.loadData()
},
methods: {
...mapActions('disks', ['fetchDiskDrivers', 'createDisk', 'updateDisk']),
...mapActions('modal', ['closeModal']),
isRequestFire(slotProps) {
return slotProps && (slotProps.diskData.isLoading || this.isLoading)
},
async loadData() {
this.isLoading = true
let res = await this.fetchDiskDrivers()
if (this.isEdit) {
this.selected_disk = this.modalData.driver
} else {
this.selected_disk = res.data.drivers[0].value
}
this.isLoading = false
},
async createNewDisk(data) {
this.isLoading = true
let formData = {
id: this.modalDataID,
...data,
}
let response
if (this.isEdit) {
response = await this.updateDisk(formData)
} else {
response = await this.createDisk(formData)
}
if (response.data.success) {
this.refreshData()
this.closeDisk()
if (this.isEdit) {
window.toastr['success'](this.$t('settings.disk.success_update'))
} else {
window.toastr['success'](this.$t('settings.disk.success_create'))
}
} else {
window.toastr['error'](
this.$t('settings.disk.invalid_disk_credentials')
)
}
this.isLoading = false
},
closeDisk() {
this.closeModal()
},
},
}
</script>

View File

@@ -1,71 +1,67 @@
<template>
<div class="template-modal">
<div class="card-body">
<div class="template-container">
<div class="px-8 py-8 sm:p-6">
<div class="flex flex-wrap justify-start p-1 overflow-x-auto sw-scroll">
<div
v-for="(template,index) in modalData"
v-for="(template, index) in modalData"
:key="index"
:class="{'selected-template': selectedTemplate === template.id}"
class="template-img"
:class="{
'border border-solid border-primary-500':
selectedTemplate === template.id,
}"
class="relative m-2 border border-gray-200 border-solid"
>
<img
:src="template.path"
alt="template-image"
height="200" width="140"
height="200"
width="140"
@click="selectedTemplate = template.id"
>
/>
<img
v-if="selectedTemplate === template.id"
class="check-icon"
class="absolute z-10 w-5 h-5 text-primary-500"
style="top: -6px; right: -5px"
src="/assets/img/tick.png"
>
/>
</div>
</div>
</div>
<div class="card-footer">
<base-button outline class="mr-3" color="theme" @click="closeInvoiceModal">
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
color="theme"
@click="chooseTemplate()"
<div class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid">
<sw-button
class="mr-3"
variant="primary-outline"
@click="closeInvoiceModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button variant="primary" @click="chooseTemplate()">
{{ $t('general.choose') }}
</base-button>
</sw-button>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data () {
data() {
return {
selectedTemplate: 1,
isLoading: false
isLoading: false,
}
},
computed: {
...mapGetters('modal', [
'modalData'
]),
...mapGetters('invoice', [
'getTemplateId'
])
...mapGetters('modal', ['modalData']),
...mapGetters('invoice', ['getTemplateId']),
},
mounted () {
mounted() {
this.selectedTemplate = this.getTemplateId
},
methods: {
...mapActions('invoice', [
'setTemplate'
]),
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
async chooseTemplate () {
this.isLoading = true;
...mapActions('invoice', ['setTemplate']),
...mapActions('modal', ['closeModal', 'resetModalData']),
async chooseTemplate() {
this.isLoading = true
let resp = await this.setTemplate(this.selectedTemplate)
if (resp) {
this.isLoading = false
@@ -73,11 +69,11 @@ export default {
this.closeModal()
}
},
closeInvoiceModal () {
closeInvoiceModal() {
this.selectedTemplate = this.getTemplateId
this.closeModal()
this.resetModalData()
}
}
},
},
}
</script>

View File

@@ -1,120 +1,104 @@
<template>
<div class="item-modal">
<form action="" @submit.prevent="submitItemData">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">
{{ $t('items.name') }}<span class="required">*</span>
</label>
<div class="col-sm-7">
<base-input
ref="name"
:invalid="$v.formData.name.$error"
v-model="formData.name"
type="text"
@input="$v.formData.name.$touch()"
/>
<div class="px-8 py-8 sm:p-6">
<sw-input-group
:label="$t('items.name')"
:error="nameError"
class="mb-4"
variant="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>
<div v-if="$v.formData.name.$error">
<span v-if="!$v.formData.name.required" class="text-danger">{{ $tc('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>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('items.price') }}<span class="required">*</span></label>
<div class="col-sm-7">
<div class="base-input">
<money
:class="{'invalid' : $v.formData.price.$error}"
v-model="price"
v-bind="defaultCurrencyForInput"
class="input-field"
/>
</div>
<div v-if="$v.formData.price.$error">
<span v-if="!$v.formData.price.required" class="text-danger">{{ $tc('validation.required') }}</span>
<span v-if="!$v.formData.price.numeric" class="text-danger">{{ $tc('validation.numbers_only') }}</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>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('items.unit') }}</label>
<div class="col-sm-7">
<base-select
v-model="formData.unit"
:options="itemUnits"
:searchable="true"
:show-labels="false"
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>
<div v-if="isTexPerItem" class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('items.taxes') }}</label>
<div class="col-sm-7">
<base-select
v-model="formData.taxes"
:options="getTaxTypes"
:searchable="true"
:show-labels="false"
:allow-empty="true"
:multiple="true"
label="tax_name"
/>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('items.description') }}</label>
<div class="col-sm-7">
<base-text-area
v-model="formData.description"
rows="4"
cols="50"
@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>
<sw-input-group
:label="$t('items.price')"
:error="priceError"
class="mb-4"
variant="horizontal"
required
>
<sw-money
v-model="price"
:currency="defaultCurrencyForInput"
:invalid="$v.formData.price.$error"
class="relative w-full focus:border focus:border-solid focus:border-primary"
@input="$v.formData.price.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('items.unit')"
class="mb-4"
variant="horizontal"
>
<sw-select
v-model="formData.unit"
:options="itemUnits"
:searchable="true"
:show-labels="false"
:maxHeight="200"
label="name"
>
</sw-select>
</sw-input-group>
<sw-input-group
v-if="isTexPerItem"
:label="$t('items.taxes')"
class="mb-4"
variant="horizontal"
>
<sw-select
v-model="formData.taxes"
:options="getTaxTypes"
:searchable="true"
:show-labels="false"
:allow-empty="true"
:multiple="true"
label="tax_name"
/>
</sw-input-group>
<sw-input-group
:label="$t('items.description')"
:error="descriptionError"
variant="horizontal"
>
<sw-textarea
v-model="formData.description"
rows="4"
cols="50"
@input="$v.formData.description.$touch()"
/>
</sw-input-group>
</div>
<div class="card-footer">
<base-button
:outline="true"
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
>
<sw-button
class="mr-3"
color="theme"
variant="primary-outline"
type="button"
@click="closeItemModal"
>
{{ $t('general.cancel') }}
</base-button>
<base-button
v-if="isEdit"
</sw-button>
<sw-button
:loading="isLoading"
color="theme"
@click="submitItemData"
>
{{ $t('general.update') }}
</base-button>
<base-button
v-else
:loading="isLoading"
icon="save"
color="theme"
:disabled="isLoading"
variant="primary"
type="submit"
>
{{ $t('general.save') }}
</base-button>
<save-icon v-if="!isLoading" class="mr-2" />
{{ isEdit ? $t('general.update') : $t('general.save') }}
</sw-button>
</div>
</form>
</div>
@@ -122,97 +106,131 @@
<script>
import { mapActions, mapGetters } from 'vuex'
import { validationMixin } from 'vuelidate'
const { required, minLength, numeric, maxLength, minValue } = require('vuelidate/lib/validators')
import { ShoppingCartIcon } from '@vue-hero-icons/solid'
const {
required,
minLength,
numeric,
maxLength,
minValue,
} = require('vuelidate/lib/validators')
export default {
mixins: [validationMixin],
data () {
components: {
ShoppingCartIcon,
},
data() {
return {
isEdit: false,
isLoading: false,
tempData: null,
units: [
{ name: 'box', value: 'box' },
{ name: 'cm', value: 'cm' },
{ name: 'dz', value: 'dz' },
{ name: 'ft', value: 'ft' },
{ name: 'g', value: 'g' },
{ name: 'in', value: 'in' },
{ name: 'kg', value: 'kg' },
{ name: 'km', value: 'km' },
{ name: 'lb', value: 'lb' },
{ name: 'mg', value: 'mg' },
{ name: 'pc', value: 'pc' }
],
taxes: [],
formData: {
name: null,
price: null,
description: null,
unit: null,
taxes: []
}
taxes: [],
},
}
},
validations: {
formData: {
name: {
required,
minLength: minLength(3)
minLength: minLength(3),
},
price: {
required,
numeric,
minValue: minValue(0.1),
maxLength: maxLength(20)
maxLength: maxLength(20),
},
description: {
maxLength: maxLength(255)
}
}
maxLength: maxLength(255),
},
},
},
computed: {
...mapGetters('currency', [
'defaultCurrencyForInput'
]),
...mapGetters('company', ['defaultCurrencyForInput']),
price: {
get: function () {
return this.formData.price / 100
},
set: function (newValue) {
this.formData.price = newValue * 100
}
},
},
// itemUnits () {
// return this.units
// },
...mapGetters('modal', [
'modalDataID',
'modalData'
]),
...mapGetters('item', [
'getItemById',
'itemUnits'
]),
...mapGetters('taxType', [
'taxTypes'
]),
isTexPerItem () {
...mapGetters('modal', ['modalDataID', 'modalData']),
...mapGetters('item', ['getItemById', 'itemUnits']),
...mapGetters('taxType', ['taxTypes']),
isTexPerItem() {
return this.modalData.taxPerItem === 'YES'
},
getTaxTypes () {
return this.taxTypes.map(tax => {
return {...tax, tax_name: tax.name + ' (' + tax.percent + '%)'}
getTaxTypes() {
return this.taxTypes.map((tax) => {
return { ...tax, tax_name: tax.name + ' (' + tax.percent + '%)' }
})
}
},
nameError() {
if (!this.$v.formData.name.$error) {
return ''
}
if (!this.$v.formData.name.required) {
return this.$tc('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.$tc('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')
}
},
},
watch: {
modalDataID () {
modalDataID() {
this.isEdit = true
this.fetchEditData()
}
},
},
created () {
created() {
if (this.modalDataID) {
this.isEdit = true
this.fetchEditData()
@@ -222,33 +240,30 @@ export default {
this.loadEditData()
}
},
mounted () {
mounted() {
this.$v.formData.$reset()
this.$refs.name.focus = true
this.fetchItemUnits({ limit: 'all' })
},
methods: {
...mapActions('modal', [
'openModal',
'closeModal',
'resetModalData'
]),
...mapActions('item', [
'addItem',
'updateItem'
]),
...mapActions('invoice', [
'setItem'
]),
resetFormData () {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('item', ['addItem', 'updateItem', 'fetchItemUnits']),
...mapActions('invoice', ['setItem']),
resetFormData() {
this.formData = {
name: null,
price: null,
description: null,
unit: null,
id: null
id: null,
}
this.$v.$reset()
},
fetchEditData () {
fetchEditData() {
this.tempData = this.getItemById(this.modalDataID)
if (this.tempData) {
this.formData.name = this.tempData.name
@@ -258,15 +273,17 @@ export default {
this.formData.id = this.tempData.id
}
},
async submitItemData () {
async submitItemData() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
if (this.formData.unit) {
this.formData.unit = this.formData.unit.name
this.formData.unit_id = this.formData.unit.id
}
this.isLoading = true
let response
if (this.isEdit) {
@@ -274,21 +291,22 @@ export default {
} 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) {
window.toastr['success'](this.$tc('items.created_message'))
this.setItem(response.data.item)
window.hub.$emit('newItem', response.data.item)
this.isLoading = false
this.resetModalData()
@@ -298,17 +316,12 @@ export default {
}
window.toastr['error'](response.data.error)
},
async addItemUnit () {
this.openModal({
'title': 'Add Item Unit',
'componentName': 'ItemUnit'
})
},
closeItemModal () {
closeItemModal() {
this.resetFormData()
this.closeModal()
this.resetModalData()
}
}
},
},
}
</script>

View File

@@ -1,78 +1,85 @@
<template>
<div class="item-unit-modal">
<form action="" @submit.prevent="submitItemUnit">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('settings.customization.items.unit_name') }} <span class="required"> *</span></label>
<div class="col-sm-7">
<base-input
ref="name"
:invalid="$v.formData.name.$error"
v-model="formData.name"
type="text"
@input="$v.formData.name.$touch()"
/>
<div v-if="$v.formData.name.$error">
<span v-if="!$v.formData.name.required" class="form-group__message text-danger">{{ $tc('validation.required') }}</span>
</div>
</div>
</div>
</div>
<div class="card-footer">
<base-button
:outline="true"
class="mr-3"
color="theme"
type="button"
@click="closePaymentModeModal"
>
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
color="theme"
icon="save"
type="submit"
>
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</base-button>
</div>
</form>
</div>
<form action="" @submit.prevent="submitItemUnit">
<div class="p-8 sm:p-6">
<sw-input-group
:label="$t('settings.customization.items.unit_name')"
:error="nameError"
variant="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>
</div>
<div class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid">
<sw-button
class="mr-3"
variant="primary-outline"
type="button"
@click="closeItemUnitModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isLoading"
variant="primary"
icon="save"
type="submit"
>
<save-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</sw-button>
</div>
</form>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { validationMixin } from 'vuelidate'
const { required, minLength } = require('vuelidate/lib/validators')
export default {
mixins: [validationMixin],
data () {
data() {
return {
isEdit: false,
isLoading: false,
formData: {
id: null,
name: null
}
name: null,
},
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive'
])
'modalActive',
'refreshData',
]),
nameError() {
if (!this.$v.formData.name.$error) {
return ''
}
if (!this.$v.formData.name.required) {
return this.$tc('validation.required')
}
},
},
validations: {
formData: {
name: {
required,
minLength: minLength(2)
}
}
minLength: minLength(2),
},
},
},
async mounted () {
async mounted() {
this.$refs.name.focus = true
if (this.modalDataID) {
this.isEdit = true
@@ -80,69 +87,62 @@ export default {
}
},
methods: {
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
...mapActions('item', [
'addItemUnit',
'updateItemUnit',
'fatchItemUnit'
]),
resetFormData () {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('item', ['addItemUnit', 'updateItemUnit', 'fatchItemUnit']),
resetFormData() {
this.formData = {
id: null,
name: null
name: null,
}
this.$v.formData.$reset()
},
async submitItemUnit () {
async submitItemUnit() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
this.isLoading = true
this.isLoading = true
let response
if (this.isEdit) {
response = await this.updateItemUnit(this.formData)
try {
if (!this.isEdit) {
response = await this.addItemUnit(this.formData)
} else {
response = await this.updateItemUnit(this.formData)
}
if (response.data) {
window.toastr['success'](this.$t('settings.customization.items.item_unit_updated'))
this.closePaymentModeModal()
this.isLoading = false
if (!this.isEdit) {
window.toastr['success'](
this.$t('settings.customization.items.item_unit_added')
)
} else {
window.toastr['success'](
this.$t('settings.customization.items.item_unit_updated')
)
}
this.refreshData ? this.refreshData() : ''
this.closeItemUnitModal()
return true
}
} catch (error) {
this.isLoading = false
window.toastr['error'](response.data.error)
} else {
try {
response = await this.addItemUnit(this.formData)
if (response.data) {
this.isLoading = false
window.toastr['success'](this.$t('settings.customization.items.item_unit_added'))
this.closePaymentModeModal()
return true
} window.toastr['error'](response.data.error)
} catch (err) {
if (err.response.data.errors.name) {
this.isLoading = true
window.toastr['error'](this.$t('validation.item_unit_already_taken'))
}
}
}
},
async setData () {
async setData() {
this.formData = {
id: this.modalData.id,
name: this.modalData.name
name: this.modalData.name,
}
},
closePaymentModeModal () {
closeItemUnitModal() {
this.resetModalData()
this.resetFormData()
this.closeModal()
}
}
},
},
}
</script>

View File

@@ -1,75 +1,66 @@
<template>
<div class="mail-test-modal">
<div class="mail-config-modal">
<form action="" @submit.prevent="onTestMailSend">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('general.to') }} <span class="required"> *</span></label>
<div class="col-sm-7">
<base-input
ref="to"
:invalid="$v.formData.to.$error"
v-model="formData.to"
type="text"
@input="$v.formData.to.$touch()"
/>
<div v-if="$v.formData.to.$error">
<span v-if="!$v.formData.to.required" class="form-group__message text-danger">{{ $tc('validation.required') }}</span>
<span v-if="!$v.formData.to.email" class="form-group__message text-danger"> {{ $t('validation.email_incorrect') }} </span>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('general.subject') }} <span class="required"> *</span></label>
<div class="col-sm-7">
<div class="base-input">
<base-input
:invalid="$v.formData.subject.$error"
v-model="formData.subject"
type="text"
@input="$v.formData.subject.$touch()"
/>
</div>
<div v-if="$v.formData.subject.$error">
<span v-if="!$v.formData.subject.required" class="text-danger">{{ $t('validation.required') }}</span>
<span v-if="!$v.formData.subject.maxLength" class="text-danger">{{ $t('validation.subject_maxlength') }}</span>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('general.message') }}<span class="required"> *</span></label>
<div class="col-sm-7">
<base-text-area
v-model="formData.message"
:invalid="$v.formData.message.$error"
rows="4"
cols="50"
@input="$v.formData.message.$touch()"
/>
<div v-if="$v.formData.message.$error">
<span v-if="!$v.formData.message.required" class="text-danger">{{ $t('validation.required') }}</span>
<span v-if="!$v.formData.message.maxLength" class="text-danger">{{ $t('validation.message_maxlength') }}</span>
</div>
</div>
</div>
<div class="p-4 md:p-8">
<sw-input-group
:label="$t('general.to')"
class="mt-3"
:error="emailError"
variant="horizontal"
required
>
<sw-input
ref="to"
:invalid="$v.formData.to.$error"
v-model="formData.to"
type="text"
@input="$v.formData.to.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.subject')"
class="mt-3"
:error="subjectError"
variant="horizontal"
required
>
<sw-input
:invalid="$v.formData.subject.$error"
v-model="formData.subject"
type="text"
@input="$v.formData.subject.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.message')"
class="mt-3"
:error="messageError"
variant="horizontal"
required
>
<sw-textarea
v-model="formData.message"
:invalid="$v.formData.message.$error"
rows="4"
cols="50"
@input="$v.formData.message.$touch()"
/>
</sw-input-group>
</div>
<div class="card-footer">
<base-button
:outline="true"
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
>
<sw-button
variant="primary-outline"
class="mr-3"
color="theme"
type="button"
@click="closeTaxModal"
>
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
color="theme"
icon="save"
type="submit"
>
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</base-button>
</sw-button>
<sw-button variant="primary" type="submit" :loading="isLoading">
<paper-airplane-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.send') : $t('general.update') }}
</sw-button>
</div>
</form>
</div>
@@ -77,61 +68,96 @@
<script>
import { mapActions, mapGetters } from 'vuex'
import { validationMixin } from 'vuelidate'
const { required, minLength, email, maxLength } = require('vuelidate/lib/validators')
import { PaperAirplaneIcon } from '@vue-hero-icons/outline'
const {
required,
minLength,
email,
maxLength,
} = require('vuelidate/lib/validators')
export default {
mixins: [validationMixin],
data () {
components: {
PaperAirplaneIcon,
},
data() {
return {
isEdit: false,
isLoading: false,
formData: {
to: null,
subject: null,
message: null
}
message: null,
},
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive'
])
...mapGetters('modal', ['modalDataID', 'modalData', 'modalActive']),
emailError() {
if (!this.$v.formData.to.$error) {
return ''
}
if (!this.$v.formData.to.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.to.email) {
return this.$tc('validation.email_incorrect')
}
},
subjectError() {
if (!this.$v.formData.subject.$error) {
return ''
}
if (!this.$v.formData.subject.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.subject.maxLength) {
return this.$tc('validation.subject_maxlength')
}
},
messageError() {
if (!this.$v.formData.message.$error) {
return ''
}
if (!this.$v.formData.message.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.message.maxLength) {
return this.$tc('validation.message_maxlength')
}
},
},
validations: {
formData: {
to: {
required,
email
email,
},
subject: {
required,
maxLength: maxLength(100)
maxLength: maxLength(100),
},
message: {
required,
maxLength: maxLength(255)
}
}
maxLength: maxLength(255),
},
},
},
async mounted () {
async mounted() {
this.$refs.to.focus = true
},
methods: {
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
resetFormData () {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('company', ['sendTestMail']),
resetFormData() {
this.formData = {
to: null,
subject: null,
message: null
message: null,
}
this.$v.formData.$reset()
},
async onTestMailSend () {
async onTestMailSend() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
@@ -139,9 +165,9 @@ export default {
}
this.isLoading = true
let response = await axios.post('/api/settings/test/mail', this.formData)
if (response.data) {
let response = await this.sendTestMail(this.formData)
if (response.data) {
if (response.data.success) {
window.toastr['success'](this.$tc('general.send_mail_successfully'))
this.closeTaxModal()
@@ -156,11 +182,11 @@ export default {
}
window.toastr['error'](response.data.error)
},
closeTaxModal () {
closeTaxModal() {
this.resetModalData()
this.resetFormData()
this.closeModal()
}
}
},
},
}
</script>

View File

@@ -0,0 +1,299 @@
<template>
<div class="note-modal">
<form action="" @submit.prevent="submitNote">
<div class="px-8 py-8 sm:p-6">
<sw-input-group
:label="$t('settings.customization.notes.name')"
:error="nameError"
class="mb-4"
variant="vertical"
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.customization.notes.type')"
:error="typeError"
class="mb-4"
variant="vertical"
required
>
<sw-select
v-model="noteType"
:options="types"
:allow-empty="false"
:show-labels="false"
class="mt-2"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.customization.notes.notes')"
:error="noteError"
variant="vertical"
required
>
<base-custom-input
v-model="formData.notes"
:fields="fields"
class="mt-2"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end px-4 py-4 border-t border-solid border-gray-light"
>
<sw-button
class="mr-2"
variant="primary-outline"
type="button"
@click="closeNoteModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isLoading"
variant="primary"
icon="save"
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'
const { required, minLength } = require('vuelidate/lib/validators')
export default {
data() {
return {
isEdit: false,
isLoading: false,
types: ['Invoice', 'Estimate', 'Payment'],
selectType: null,
formData: {
type: '',
name: '',
notes: '',
},
noteType: null,
fields: [],
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive',
'refreshData',
]),
nameError() {
if (!this.$v.formData.name.$error) {
return ''
}
if (!this.$v.formData.name.required) {
return this.$tc('validation.required')
}
},
noteError() {
if (!this.$v.formData.notes.$error) {
return ''
}
if (!this.$v.formData.notes.required) {
return this.$tc('validation.required')
}
},
typeError() {
if (!this.$v.noteType.$error) {
return ''
}
if (!this.$v.noteType.required) {
return this.$tc('validation.required')
}
},
},
validations: {
formData: {
name: {
required,
minLength: minLength(2),
},
notes: {
required,
},
},
noteType: {
required,
},
},
async mounted() {
this.setFields()
if (this.modalDataID) {
this.isEdit = true
this.setData()
} else {
this.modalData
? (this.noteType = this.modalData)
: (this.noteType = 'Invoice')
}
},
watch: {
noteType() {
this.setFields()
},
},
methods: {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('notes', ['addNote', 'updateNote']),
...mapActions('invoice', {
setInvoiceNote: 'selectNote',
}),
...mapActions('estimate', {
setEstimateNote: 'selectNote',
}),
...mapActions('payment', {
setPaymentNote: 'selectNote',
}),
setFields() {
this.fields = ['customer', 'customerCustom']
if (this.noteType === 'Invoice') {
this.fields.push('invoice', 'invoiceCustom')
}
if (this.noteType === 'Estimate') {
this.fields.push('estimate', 'estimateCustom')
}
if (this.noteType === 'Payment') {
this.fields.push('payment', 'paymentCustom')
}
return true
},
resetFormData() {
this.formData = {
name: null,
notes: null,
}
this.notetype = null
this.$v.formData.$reset()
},
async submitNote() {
this.$v.formData.$touch()
this.$v.noteType.$touch()
if (this.$v.$invalid) {
return true
}
this.isLoading = true
if (this.isEdit) {
let data = {
id: this.modalDataID,
type: this.noteType,
name: this.formData.name,
notes: this.formData.notes,
}
let res = await this.updateNote(data)
if (res.data) {
window.toastr['success'](
this.$t('settings.customization.notes.note_updated')
)
this.refreshData ? this.refreshData() : ''
this.closeNoteModal()
return true
}
window.toastr['error'](res.data.error)
} else {
try {
let data = {
type: this.noteType,
name: this.formData.name,
notes: this.formData.notes,
}
let response = await this.addNote(data)
if (response.data && response.data.note) {
this.isLoading = false
window.toastr['success'](
this.$t('settings.customization.notes.note_added')
)
if (
(this.$route.name === 'invoices.create' &&
response.data.note.type === 'Invoice') ||
(this.$route.name === 'invoices.edit' &&
response.data.note.type === 'Invoice')
) {
this.setInvoiceNote(response.data.note)
}
if (
(this.$route.name === 'estimates.create' &&
response.data.note.type === 'Estimate') ||
(this.$route.name === 'estimates.edit' &&
response.data.note.type === 'Estimate')
) {
this.setEstimateNote(response.data.note)
}
if (
(this.$route.name === 'payments.create' &&
response.data.note.type === 'Payment') ||
(this.$route.name === 'payments.edit' &&
response.data.note.type === 'Payment')
) {
this.setPaymentNote(response.data.note)
}
this.refreshData ? this.refreshData() : ''
this.closeNoteModal()
return true
}
window.toastr['error'](response.data.error)
} catch (err) {
if (err.response.data.errors.name) {
this.isLoading = true
}
}
}
},
async setData() {
this.noteType = this.modalData.type
this.formData.name = this.modalData.name
this.formData.notes = this.modalData.notes
},
closeNoteModal() {
this.closeModal()
this.resetFormData()
},
},
}
</script>
<style lang="scss">
.note-modal {
.header-editior .editor-menu-bar {
margin-left: 0.5px;
margin-right: 0px;
}
}
</style>

View File

@@ -1,79 +1,78 @@
<template>
<div class="payment-modes-modal">
<form action="" @submit.prevent="submitPaymentMode">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('settings.customization.payments.mode_name') }} <span class="required"> *</span></label>
<div class="col-sm-7">
<base-input
ref="name"
:invalid="$v.formData.name.$error"
v-model="formData.name"
type="text"
@input="$v.formData.name.$touch()"
/>
<div v-if="$v.formData.name.$error">
<span v-if="!$v.formData.name.required" class="form-group__message text-danger">{{ $tc('validation.required') }}</span>
</div>
</div>
</div>
</div>
<div class="card-footer">
<base-button
:outline="true"
class="mr-3"
color="theme"
type="button"
@click="closePaymentModeModal"
>
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
color="theme"
icon="save"
type="submit"
>
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</base-button>
</div>
</form>
</div>
<form action="" @submit.prevent="submitPaymentMode">
<div class="p-8 sm:p-6">
<sw-input-group
:label="$t('settings.customization.payments.mode_name')"
:error="nameError"
variant="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>
</div>
<div class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid">
<sw-button
class="mr-3"
variant="primary-outline"
type="button"
@click="closePaymentModeModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button :loading="isLoading" variant="primary" type="submit">
<save-icon class="mr-2" v-if="!isLoading" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</sw-button>
</div>
</form>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { validationMixin } from 'vuelidate'
const { required, minLength } = require('vuelidate/lib/validators')
export default {
mixins: [validationMixin],
data () {
data() {
return {
isEdit: false,
isLoading: false,
formData: {
id: null,
name: null
}
name: null,
},
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive'
])
'modalActive',
'refreshData',
]),
nameError() {
if (!this.$v.formData.name.$error) {
return ''
}
if (!this.$v.formData.name.required) {
return this.$tc('validation.required')
}
},
},
validations: {
formData: {
name: {
required,
minLength: minLength(2)
}
}
minLength: minLength(2),
},
},
},
async mounted () {
async mounted() {
this.$refs.name.focus = true
if (this.modalDataID) {
this.isEdit = true
@@ -81,63 +80,68 @@ export default {
}
},
methods: {
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
...mapActions('payment', [
'addPaymentMode',
'updatePaymentMode'
]),
resetFormData () {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('payment', ['addPaymentMode', 'updatePaymentMode']),
resetFormData() {
this.formData = {
id: null,
name: null
name: null,
}
this.$v.formData.$reset()
},
async submitPaymentMode () {
async submitPaymentMode() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
this.isLoading = true
let response
if (this.isEdit) {
response = await this.updatePaymentMode(this.formData)
if (response.data) {
window.toastr['success'](this.$t('settings.customization.payments.payment_mode_updated'))
window.toastr['success'](
this.$t('settings.customization.payments.payment_mode_updated')
)
this.refreshData ? this.refreshData() : ''
this.closePaymentModeModal()
return true
} window.toastr['error'](response.data.error)
}
window.toastr['error'](response.data.error)
} else {
try {
response = await this.addPaymentMode(this.formData)
if (response.data) {
this.isLoading = false
window.toastr['success'](this.$t('settings.customization.payments.payment_mode_added'))
window.toastr['success'](
this.$t('settings.customization.payments.payment_mode_added')
)
this.refreshData ? this.refreshData() : ''
this.closePaymentModeModal()
return true
} window.toastr['error'](response.data.error)
}
window.toastr['error'](response.data.error)
} catch (err) {
if (err.response.data.errors.name) {
this.isLoading = true
window.toastr['error'](this.$t('validation.payment_mode_already_taken'))
this.isLoading = false
window.toastr['error'](
this.$t('validation.payment_mode_already_taken')
)
}
}
}
},
async setData () {
async setData() {
this.formData = {
id: this.modalData.id,
name: this.modalData.name
name: this.modalData.name,
}
},
closePaymentModeModal () {
closePaymentModeModal() {
this.resetModalData()
this.resetFormData()
this.closeModal()
}
}
},
},
}
</script>

View File

@@ -0,0 +1,271 @@
<template>
<div>
<form action="" @submit.prevent="sendEstimateData">
<div class="px-8 py-8 sm:p-6">
<sw-input-group
:label="$t('general.from')"
:error="fromError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.from"
:invalid="$v.formData.from.$error"
type="text"
@input="$v.formData.from.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.to')"
:error="toError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.to"
:invalid="$v.formData.to.$error"
type="text"
@input="$v.formData.to.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.subject')"
:error="subjectError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.subject"
:invalid="$v.formData.subject.$error"
type="text"
@input="$v.formData.subject.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.body')"
:error="bodyError"
class="mb-4"
variant="vertical"
required
>
<!-- <sw-editor
v-model="formData.body"
:set-editor="formData.body"
:invalid="$v.formData.body.$error"
@input="$v.formData.body.$touch()"
/> -->
<base-custom-input
v-model="formData.body"
:fields="estimateMailFields"
:invalid="$v.formData.body.$error"
@input="$v.formData.body.$touch()"
class="mt-2"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
>
<sw-button
class="mr-3"
variant="primary-outline"
type="button"
@click="closeSendEstimateModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isLoading"
:disabled="isLoading"
variant="primary"
type="submit"
>
<paper-airplane-icon v-if="!isLoading" class="h-5 mr-2" />
{{ $t('general.send') }}
</sw-button>
</div>
</form>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { PaperAirplaneIcon } from '@vue-hero-icons/solid'
const { required, email } = require('vuelidate/lib/validators')
const _ = require('lodash')
export default {
components: {
PaperAirplaneIcon,
},
data() {
return {
isLoading: false,
estimateMailFields: [
'customer',
'customerCustom',
'estimate',
'estimateCustom',
'company',
],
formData: {
from: null,
to: null,
subject: 'New Estimate',
body: null,
},
}
},
validations: {
formData: {
from: {
required,
email,
},
to: {
required,
email,
},
subject: {
required,
},
body: {
required,
},
},
},
computed: {
...mapGetters('modal', ['modalDataID', 'modalData', 'modalActive']),
...mapGetters('user', ['currentUser']),
getEmailUrl() {
return this.url
},
fromError() {
if (!this.$v.formData.from.$error) {
return ''
}
if (!this.$v.formData.from.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.from.email) {
return this.$tc('validation.email_incorrect')
}
},
toError() {
if (!this.$v.formData.to.$error) {
return ''
}
if (!this.$v.formData.to.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.to.email) {
return this.$tc('validation.email_incorrect')
}
},
subjectError() {
if (!this.$v.formData.subject.$error) {
return ''
}
if (!this.$v.formData.subject.required) {
return this.$tc('validation.required')
}
},
bodyError() {
if (!this.$v.formData.body.$error) {
return ''
}
if (!this.$v.formData.body.required) {
return this.$tc('validation.required')
}
},
},
mounted() {
this.setInitialData()
},
methods: {
...mapActions('modal', ['closeModal']),
...mapActions('estimate', ['sendEmail']),
...mapActions('company', ['fetchCompanySettings', 'fetchMailConfig']),
async setInitialData() {
let admin = await this.fetchMailConfig()
if (this.modalData) {
this.formData.from = admin.data.from_mail
this.formData.to = this.modalData.user.email
}
let res = await this.fetchCompanySettings(['estimate_mail_body'])
this.formData.body = res.data.estimate_mail_body
},
resetFormData() {
this.formData = {
from: null,
to: null,
subject: null,
body: null,
}
},
async sendEstimateData() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_send_estimate'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
try {
if (value) {
let data = {
...this.formData,
id: this.modalDataID,
status: 'SENT',
}
this.isLoading = true
let res = await this.sendEmail(data)
this.closeModal()
if (res.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('estimates.send_estimate_successfully')
)
return true
}
if (res.data.error === 'estimates.user_email_does_not_exist') {
window.toastr['error'](
this.$tc('estimates.user_email_does_not_exist')
)
return false
}
}
} catch (error) {
this.isLoading = false
window.toastr['error'](this.$tc('estimates.something_went_wrong'))
}
})
},
closeSendEstimateModal() {
this.resetFormData()
this.closeModal()
},
},
}
</script>

View File

@@ -0,0 +1,266 @@
<template>
<div>
<form action="" @submit.prevent="sendInvoiceData">
<div class="gap-4 px-8 py-8 sm:p-6">
<sw-input-group
:label="$t('general.from')"
:error="fromError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.from"
:invalid="$v.formData.from.$error"
type="text"
@input="$v.formData.from.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.to')"
:error="toError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.to"
:invalid="$v.formData.to.$error"
type="text"
@input="$v.formData.to.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.subject')"
:error="subjectError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.subject"
:invalid="$v.formData.subject.$error"
type="text"
@input="$v.formData.subject.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.body')"
:error="bodyError"
class="mb-4"
variant="vertical"
required
>
<base-custom-input
v-model="formData.body"
:fields="InvoiceMailFields"
:invalid="$v.formData.body.$error"
@input="$v.formData.body.$touch()"
class="mt-2"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
>
<sw-button
class="mr-3"
variant="primary-outline"
type="button"
@click="closeSendInvoiceModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isLoading"
:disabled="isLoading"
variant="primary"
type="submit"
>
<paper-airplane-icon v-if="!isLoading" class="h-5 mr-2" />
{{ $t('general.send') }}
</sw-button>
</div>
</form>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { PaperAirplaneIcon } from '@vue-hero-icons/solid'
const { required, email } = require('vuelidate/lib/validators')
const _ = require('lodash')
export default {
components: {
PaperAirplaneIcon,
},
data() {
return {
isLoading: false,
InvoiceMailFields: [
'customer',
'customerCustom',
'invoice',
'invoiceCustom',
'company',
],
formData: {
from: null,
to: null,
subject: 'New Invoice',
body: null,
},
}
},
validations: {
formData: {
from: {
required,
email,
},
to: {
required,
email,
},
subject: {
required,
},
body: {
required,
},
},
},
computed: {
...mapGetters('modal', ['modalDataID', 'modalData', 'modalActive']),
...mapGetters('user', ['currentUser']),
fromError() {
if (!this.$v.formData.from.$error) {
return ''
}
if (!this.$v.formData.from.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.from.email) {
return this.$tc('validation.email_incorrect')
}
},
toError() {
if (!this.$v.formData.to.$error) {
return ''
}
if (!this.$v.formData.to.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.from.email) {
return this.$tc('validation.email_incorrect')
}
},
subjectError() {
if (!this.$v.formData.subject.$error) {
return ''
}
if (!this.$v.formData.subject.required) {
return this.$tc('validation.required')
}
},
bodyError() {
if (!this.$v.formData.body.$error) {
return ''
}
if (!this.$v.formData.body.required) {
return this.$tc('validation.required')
}
},
},
mounted() {
this.setInitialData()
},
methods: {
...mapActions('modal', ['closeModal']),
...mapActions('invoice', ['sendEmail']),
...mapActions('company', ['fetchCompanySettings', 'fetchMailConfig']),
async setInitialData() {
let admin = await this.fetchMailConfig()
if (this.modalData) {
this.formData.from = admin.data.from_mail
this.formData.to = this.modalData.user.email
}
let res = await this.fetchCompanySettings(['invoice_mail_body'])
this.formData.body = res.data.invoice_mail_body
},
resetFormData() {
this.formData = {
from: null,
to: null,
subject: null,
body: null,
}
},
async sendInvoiceData() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.confirm_send_invoice'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
try {
if (value) {
let data = {
...this.formData,
id: this.modalDataID,
status: 'SENT',
}
this.isLoading = true
let res = await this.sendEmail(data)
this.closeModal()
if (res.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('invoices.send_invoice_successfully')
)
return true
}
if (res.data.error === 'invoices.user_email_does_not_exist') {
window.toastr['error'](
this.$tc('invoices.user_email_does_not_exist')
)
return false
}
}
} catch (error) {
this.isLoading = false
window.toastr['error'](this.$tc('invoices.something_went_wrong'))
}
})
},
closeSendInvoiceModal() {
this.resetFormData()
this.closeModal()
},
},
}
</script>

View File

@@ -0,0 +1,263 @@
<template>
<div>
<form action="" @submit.prevent="sendPaymentData">
<div class="px-8 py-8 sm:p-6">
<sw-input-group
:label="$t('general.from')"
class="mb-4"
variant="vertical"
:error="fromError"
required
>
<sw-input
v-model="formData.from"
:invalid="$v.formData.from.$error"
type="text"
@input="$v.formData.from.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.to')"
:error="toError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.to"
type="text"
:invalid="$v.formData.to.$error"
@input="$v.formData.to.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.subject')"
:error="subjectError"
class="mb-4"
variant="vertical"
required
>
<sw-input
v-model="formData.subject"
:invalid="$v.formData.subject.$error"
type="text"
@input="$v.formData.subject.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('general.body')"
:error="bodyError"
class="mb-4"
variant="vertical"
required
>
<sw-editor
v-model="formData.body"
:set-editor="formData.body"
:invalid="$v.formData.body.$error"
@input="$v.formData.body.$touch()"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
>
<sw-button
class="mr-3"
variant="primary-outline"
type="button"
@click="closeSendPaymentModal"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isLoading"
:disabled="isLoading"
variant="primary"
type="submit"
>
<paper-airplane-icon v-if="!isLoading" class="h-5 mr-2" />
{{ $t('general.send') }}
</sw-button>
</div>
</form>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { PaperAirplaneIcon } from '@vue-hero-icons/solid'
const { required, email } = require('vuelidate/lib/validators')
const _ = require('lodash')
export default {
components: {
PaperAirplaneIcon,
},
data() {
return {
isLoading: false,
formData: {
from: null,
to: null,
subject: null,
body: null,
},
}
},
validations: {
formData: {
from: {
required,
email,
},
to: {
required,
email,
},
subject: {
required,
},
body: {
required,
},
},
},
computed: {
...mapGetters('modal', ['modalDataID', 'modalData', 'modalActive']),
fromError() {
if (!this.$v.formData.from.$error) {
return ''
}
if (!this.$v.formData.from.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.from.email) {
return this.$tc('validation.email_incorrect')
}
},
toError() {
if (!this.$v.formData.to.$error) {
return ''
}
if (!this.$v.formData.to.required) {
return this.$tc('validation.required')
}
if (!this.$v.formData.to.email) {
return this.$tc('validation.email_incorrect')
}
},
subjectError() {
if (!this.$v.formData.subject.$error) {
return ''
}
if (!this.$v.formData.subject.required) {
return this.$tc('validation.required')
}
},
bodyError() {
if (!this.$v.formData.body.$error) {
return ''
}
if (!this.$v.formData.body.required) {
return this.$tc('validation.required')
}
},
},
mounted() {
this.setInitialData()
},
methods: {
...mapActions('modal', ['closeModal']),
...mapActions('payment', ['sendEmail']),
...mapActions('company', ['fetchCompanySettings', 'fetchMailConfig']),
async setInitialData() {
let admin = await this.fetchMailConfig()
if (this.modalData) {
this.formData.from = admin.data.from_mail
this.formData.to = this.modalData.user.email
}
let res = await this.fetchCompanySettings(['payment_mail_body'])
this.formData.body = res.data.payment_mail_body
},
resetFormData() {
this.formData = {
from: null,
to: null,
subject: null,
body: null,
}
},
async sendPaymentData() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
swal({
title: this.$t('general.are_you_sure'),
text: this.$t('payments.confirm_send_payment'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
try {
if (value) {
let data = {
...this.formData,
id: this.modalDataID,
status: 'SENT',
}
this.isLoading = true
let res = await this.sendEmail(data)
this.closeModal()
if (res.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('payments.send_payment_successfully')
)
return true
}
if (res.data.error === 'payments.user_email_does_not_exist') {
window.toastr['error'](
this.$tc('payments.user_email_does_not_exist')
)
return false
}
}
} catch (error) {
this.isLoading = false
window.toastr['error'](this.$tc('payments.something_went_wrong'))
}
})
},
closeSendPaymentModal() {
this.resetFormData()
this.closeModal()
},
},
}
</script>

View File

@@ -0,0 +1,124 @@
<template>
<div class="file-disk-modal">
<form @submit.prevent="submitData">
<div class="px-8 py-6">
<sw-input-group :label="$t('settings.disk.driver')" required>
<sw-select
v-model="selected_disk"
:options="getDisks"
:searchable="true"
:allow-empty="false"
:show-labels="false"
class="mt-2"
track-by="id"
:custom-label="getCustomLabel"
/>
</sw-input-group>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-solid border-gray-light"
>
<sw-button
class="mr-3 text-sm"
type="button"
variant="primary-outline"
@click="closeDisk"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isLoading"
icon="save"
type="submit"
variant="primary"
class="text-sm"
>
{{ $t('general.save') }}
</sw-button>
</div>
</form>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import Dropbox from './disks/DropboxDisk'
import Local from './disks/LocalDisk'
import S3 from './disks/S3Disk'
import DoSpaces from './disks/DoSpacesDisk'
const {
required,
minLength,
email,
numeric,
url,
maxLength,
} = require('vuelidate/lib/validators')
export default {
components: {
Dropbox,
Local,
S3,
DoSpaces,
},
data() {
return {
isLoading: false,
set_as_default: false,
name: 'local',
formData: null,
selected_disk: null,
diskConfigData: {},
}
},
computed: {
...mapGetters('modal', ['modalData', 'refreshData']),
...mapGetters('disks', ['getDisks']),
},
created() {
this.loadData()
},
methods: {
...mapActions('disks', ['fetchDisks', 'setDefaultDisk']),
...mapActions('modal', ['closeModal']),
async loadData() {
this.loading = true
let res = await this.fetchDisks()
this.selected_disk = res.data.disks.find((v) => v.set_as_default == true)
this.loading = false
},
async submitData() {
this.isLoading = true
let response = await this.setDefaultDisk(this.selected_disk)
if (response.data.success) {
this.refreshData()
this.closeDisk()
window.toastr['success'](this.$t('settings.disk.success'))
}
this.isLoading = true
},
closeDisk() {
this.closeModal()
},
getCustomLabel({ driver, name }) {
return `${name} — [${driver}]`
},
},
}
</script>

View File

@@ -1,186 +1,194 @@
<template>
<div class="tax-type-modal">
<form action="" @submit.prevent="submitTaxTypeData">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('tax_types.name') }} <span class="required"> *</span></label>
<div class="col-sm-7">
<base-input
ref="name"
:invalid="$v.formData.name.$error"
v-model="formData.name"
type="text"
@input="$v.formData.name.$touch()"
/>
<div v-if="$v.formData.name.$error">
<span v-if="!$v.formData.name.required" class="form-group__message text-danger">{{ $tc('validation.required') }}</span>
<span v-if="!$v.formData.name.minLength" class="form-group__message text-danger"> {{ $tc('validation.name_min_length', $v.formData.name.$params.minLength.min, { count: $v.formData.name.$params.minLength.min }) }} </span>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('tax_types.percent') }} <span class="required"> *</span></label>
<div class="col-sm-7">
<div class="base-input">
<money
:class="{'invalid' : $v.formData.percent.$error}"
v-model="formData.percent"
v-bind="defaultInput"
class="input-field"
/>
</div>
<div v-if="$v.formData.percent.$error">
<span v-if="!$v.formData.percent.required" class="text-danger">{{ $t('validation.required') }}</span>
<span v-if="!$v.formData.percent.between" class="form-group__message text-danger">{{ $t('validation.enter_valid_tax_rate') }}</span>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('tax_types.description') }}</label>
<div class="col-sm-7">
<base-text-area
v-model="formData.description"
rows="4"
cols="50"
@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>
<div class="form-group row">
<label class="col-sm-4 col-form-label input-label">{{ $t('tax_types.compound_tax') }}</label>
<div class="col-sm-7 mr-4">
<base-switch
v-model="formData.compound_tax"
class="btn-switch compound-tax-toggle"
/>
</div>
</div>
<div class="p-8 sm:p-6">
<sw-input-group
:label="$t('tax_types.name')"
:error="nameError"
class="mt-3"
variant="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('tax_types.percent')"
:error="percentError"
class="mt-3"
variant="horizontal"
required
>
<sw-money
v-model="formData.percent"
:currency="defaultInput"
:invalid="$v.formData.percent.$error"
class="relative w-full focus:border focus:border-solid focus:border-primary"
@input="$v.formData.percent.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('tax_types.description')"
:error="descriptionError"
class="mt-3"
variant="horizontal"
>
<sw-textarea
v-model="formData.description"
rows="4"
cols="50"
@input="$v.formData.description.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('tax_types.compound_tax')"
class="mt-3"
variant="horizontal"
>
<sw-switch
v-model="formData.compound_tax"
class="flex items-center mt-1"
/>
</sw-input-group>
</div>
<div class="card-footer">
<base-button
:outline="true"
class="mr-3"
color="theme"
<div
class="z-0 flex justify-end p-4 border-t border-solid border--200 border-modal-bg"
>
<sw-button
class="mr-3 text-sm"
variant="primary-outline"
type="button"
@click="closeTaxModal"
>
{{ $t('general.cancel') }}
</base-button>
<base-button
:loading="isLoading"
color="theme"
icon="save"
type="submit"
>
</sw-button>
<sw-button :loading="isLoading" variant="primary" type="submit">
<save-icon class="mr-2" v-if="!isLoading" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</base-button>
</sw-button>
</div>
</form>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { validationMixin } from 'vuelidate'
const { required, minLength, between, maxLength } = require('vuelidate/lib/validators')
const {
required,
minLength,
between,
maxLength,
} = require('vuelidate/lib/validators')
export default {
mixins: [validationMixin],
data () {
data() {
return {
isEdit: false,
isLoading: false,
formData: {
id: null,
name: null,
percent: '',
percent: 0,
description: null,
compound_tax: false,
collective_tax: 0
collective_tax: 0,
},
defaultInput: {
decimal: '.',
thousands: ',',
prefix: '% ',
precision: 2,
masked: false
}
masked: false,
},
}
},
computed: {
...mapGetters('modal', [
'modalDataID',
'modalData',
'modalActive'
])
'modalActive',
'refreshData',
]),
descriptionError() {
if (!this.$v.formData.description.$error) {
return ''
}
if (!this.$v.formData.description.maxLength) {
return this.$t('validation.description_maxlength')
}
},
nameError() {
if (!this.$v.formData.name.$error) {
return ''
}
if (!this.$v.formData.name.required) {
return this.$tc('validation.required')
} else {
return this.$tc(
'validation.name_min_length',
this.$v.formData.name.$params.minLength.min,
{ count: this.$v.formData.name.$params.minLength.min }
)
}
},
percentError() {
if (!this.$v.formData.percent.$error) {
return ''
}
if (!this.$v.formData.percent.required) {
return this.$t('validation.required')
} else {
return this.$t('validation.enter_valid_tax_rate')
}
},
},
validations: {
formData: {
name: {
required,
minLength: minLength(3)
minLength: minLength(3),
},
percent: {
required,
between: between(0, 100)
between: between(0, 100),
},
description: {
maxLength: maxLength(255)
}
}
maxLength: maxLength(255),
},
},
},
// watch: {
// 'modalDataID' (val) {
// if (val) {
// this.isEdit = true
// this.setData()
// } else {
// this.isEdit = false
// }
// },
// 'modalActive' (val) {
// if (!this.modalActive) {
// this.resetFormData()
// }
// }
// },
async mounted () {
async mounted() {
this.$refs.name.focus = true
if (this.modalDataID) {
this.isEdit = true
this.setData()
// this.resetFormData()
}
},
methods: {
...mapActions('modal', [
'closeModal',
'resetModalData'
]),
...mapActions('taxType', [
'addTaxType',
'updateTaxType',
'fetchTaxType'
]),
resetFormData () {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('taxType', ['addTaxType', 'updateTaxType', 'fetchTaxType']),
resetFormData() {
this.formData = {
id: null,
name: null,
percent: null,
percent: 0,
description: null,
collective_tax: 0
collective_tax: 0,
}
this.$v.formData.$reset()
},
async submitTaxTypeData () {
async submitTaxTypeData() {
this.$v.formData.$touch()
if (this.$v.$invalid) {
return true
}
this.isLoading = true
let response
if (!this.isEdit) {
@@ -190,31 +198,36 @@ export default {
}
if (response.data) {
if (!this.isEdit) {
window.toastr['success'](this.$t('settings.tax_types.created_message'))
window.toastr['success'](
this.$t('settings.tax_types.created_message')
)
} else {
window.toastr['success'](this.$t('settings.tax_types.updated_message'))
window.toastr['success'](
this.$t('settings.tax_types.updated_message')
)
}
window.hub.$emit('newTax', response.data.taxType)
this.refreshData ? this.refreshData() : ''
this.closeTaxModal()
this.isLoading = false
return true
}
window.toastr['error'](response.data.error)
},
async setData () {
async setData() {
this.formData = {
id: this.modalData.id,
name: this.modalData.name,
percent: this.modalData.percent,
description: this.modalData.description,
compound_tax: this.modalData.compound_tax ? true : false
compound_tax: this.modalData.compound_tax ? true : false,
}
},
closeTaxModal () {
closeTaxModal() {
this.resetModalData()
this.resetFormData()
this.closeModal()
}
}
},
},
}
</script>

View File

@@ -0,0 +1,345 @@
<template>
<form @submit.prevent="submitData">
<div class="px-8 py-6">
<div class="grid gap-6 grid-col-1 md:grid-cols-2">
<sw-input-group
:label="$t('settings.disk.name')"
:error="nameError"
required
>
<sw-input
v-model="name"
type="text"
name="name"
class="mt-2"
:invalid="$v.name.$error"
@input="$v.name.$touch()"
/>
</sw-input-group>
<sw-input-group :label="$t('settings.disk.driver')" required>
<sw-select
v-model="selected_disk"
:invalid="$v.selected_disk.$error"
:options="disks"
:searchable="true"
:allow-empty="false"
:show-labels="false"
class="mt-2"
track-by="value"
label="name"
@input="onChangeDriver"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.do_spaces_root')"
:error="rootError"
required
>
<sw-input
v-model.trim="diskConfigData.root"
:invalid="$v.diskConfigData.root.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. /user/root/"
@input="$v.diskConfigData.root.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.do_spaces_key')"
:error="keyError"
required
>
<sw-input
v-model.trim="diskConfigData.key"
:invalid="$v.diskConfigData.key.$error"
type="text"
name="name"
placeholder="Ex. KEIS4S39SERSDS"
class="mt-2"
@input="$v.diskConfigData.key.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.do_spaces_secret')"
:error="secretError"
required
>
<sw-input
v-model.trim="diskConfigData.secret"
:invalid="$v.diskConfigData.secret.$error"
type="text"
name="name"
placeholder="Ex. ********"
class="mt-2"
@input="$v.diskConfigData.secret.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.do_spaces_region')"
:error="regionError"
required
>
<sw-input
v-model.trim="diskConfigData.region"
:invalid="$v.diskConfigData.region.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. nyc3"
@input="$v.diskConfigData.region.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.do_spaces_endpoint')"
:error="endpointError"
required
>
<sw-input
v-model.trim="diskConfigData.endpoint"
:invalid="$v.diskConfigData.endpoint.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. https://nyc3.digitaloceanspaces.com"
@input="$v.diskConfigData.endpoint.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.do_spaces_bucket')"
:error="bucketError"
required
>
<sw-input
v-model.trim="diskConfigData.bucket"
:invalid="$v.diskConfigData.bucket.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. my-new-space"
@input="$v.diskConfigData.bucket.$touch()"
/>
</sw-input-group>
</div>
<div class="flex items-center mt-6" v-if="!isDisabled">
<div class="relative flex items-center w-12">
<sw-switch class="flex" v-model="set_as_default"/>
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>
</div>
</div>
<slot :disk-data="{ isLoading, submitData }" />
</form>
</template>
<script>
const { required, url } = require('vuelidate/lib/validators')
import { mapActions, mapGetters } from 'vuex'
export default {
props: {
isEdit: {
type: Boolean,
require: true,
default: false,
},
loading: {
type: Boolean,
require: true,
default: false,
},
disks: {
type: Array,
require: true,
default: Array,
},
},
data() {
return {
diskConfigData: {
selected_driver: 'doSpaces',
key: '',
secret: '',
region: '',
bucket: '',
endpoint: '',
root: '',
},
name: '',
isLoading: false,
set_as_default: false,
selected_disk: null,
is_current_disk: null,
}
},
validations: {
diskConfigData: {
key: {
required,
},
secret: {
required,
},
region: {
required,
},
bucket: {
required,
},
root: {
required,
},
endpoint: {
required,
url
},
},
name: {
required,
},
selected_disk: {
required,
},
},
computed: {
...mapGetters('modal', ['modalData']),
nameError() {
if (!this.$v.name.$error) {
return ''
}
if (!this.$v.name.required) {
return this.$tc('validation.required')
}
},
typeError() {
if (!this.$v.diskConfigData.type.$error) {
return ''
}
if (!this.$v.diskConfigData.type.required) {
return this.$tc('validation.required')
}
},
keyError() {
if (!this.$v.diskConfigData.key.$error) {
return ''
}
if (!this.$v.diskConfigData.key.required) {
return this.$tc('validation.required')
}
},
secretError() {
if (!this.$v.diskConfigData.secret.$error) {
return ''
}
if (!this.$v.diskConfigData.secret.required) {
return this.$tc('validation.required')
}
},
regionError() {
if (!this.$v.diskConfigData.region.$error) {
return ''
}
if (!this.$v.diskConfigData.region.required) {
return this.$tc('validation.required')
}
},
bucketError() {
if (!this.$v.diskConfigData.bucket.$error) {
return ''
}
if (!this.$v.diskConfigData.bucket.required) {
return this.$tc('validation.required')
}
},
rootError() {
if (!this.$v.diskConfigData.root.$error) {
return ''
}
if (!this.$v.diskConfigData.root.required) {
return this.$tc('validation.required')
}
},
endpointError() {
if (!this.$v.diskConfigData.endpoint.$error) {
return ''
}
if (!this.$v.diskConfigData.endpoint.required) {
return this.$tc('validation.required')
}
if (!this.$v.diskConfigData.endpoint.url) {
return this.$tc('validation.invalid_url')
}
},
isDisabled() {
return (this.isEdit && this.set_as_default && this.is_current_disk) ? true : false
}
},
created() {
this.loadData()
},
methods: {
...mapActions('disks', ['fetchDiskEnv', 'updateDisk']),
async loadData() {
this.isLoading = true
let data = {
disk: 'doSpaces',
}
if(this.isEdit) {
this.diskConfigData = JSON.parse(this.modalData.credentials)
this.set_as_default = this.modalData.set_as_default
if(this.set_as_default) {
this.is_current_disk = true
}
this.name = this.modalData.name
} else {
let diskData = await this.fetchDiskEnv(data)
this.diskConfigData = diskData.data
}
this.selected_disk = this.disks.find((v) => v.value == 'doSpaces')
this.isLoading = false
},
async submitData() {
this.$v.$touch()
if (this.$v.$invalid) {
return
}
let data = {
credentials: this.diskConfigData,
name: this.name,
driver: this.selected_disk.value,
set_as_default: this.set_as_default,
}
this.$emit('submit', data)
return false
},
onChangeDriver() {
this.$emit('on-change-disk', this.selected_disk)
},
},
}
</script>

View File

@@ -0,0 +1,311 @@
<template>
<form @submit.prevent="submitData">
<div class="px-8 py-6">
<div class="grid gap-6 grid-col-1 md:grid-cols-2">
<sw-input-group
:label="$t('settings.disk.name')"
:error="nameError"
required
>
<sw-input
v-model="name"
type="text"
name="name"
class="mt-2"
:invalid="$v.name.$error"
@input="$v.name.$touch()"
/>
</sw-input-group>
<sw-input-group :label="$t('settings.disk.driver')" required>
<sw-select
v-model="selected_disk"
:invalid="$v.selected_disk.$error"
:options="disks"
:searchable="true"
:allow-empty="false"
:show-labels="false"
track-by="value"
label="name"
class="mt-2"
@input="onChangeDriver"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.dropbox_root')"
:error="rootError"
required
>
<sw-input
v-model.trim="diskConfigData.root"
:invalid="$v.diskConfigData.root.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. /user/root/"
@input="$v.diskConfigData.root.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.dropbox_token')"
:error="tokenError"
required
>
<sw-input
v-model.trim="diskConfigData.token"
:invalid="$v.diskConfigData.token.$error"
type="text"
name="name"
class="mt-2"
@input="$v.diskConfigData.token.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.dropbox_key')"
:error="keyError"
required
>
<sw-input
v-model.trim="diskConfigData.key"
:invalid="$v.diskConfigData.key.$error"
type="text"
name="name"
placeholder="Ex. KEIS4S39SERSDS"
class="mt-2"
@input="$v.diskConfigData.key.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.dropbox_secret')"
:error="secretError"
required
>
<sw-input
v-model.trim="diskConfigData.secret"
:invalid="$v.diskConfigData.secret.$error"
type="text"
name="name"
placeholder="Ex. ********"
class="mt-2"
@input="$v.diskConfigData.secret.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.dropbox_app')"
:error="appError"
required
>
<sw-input
v-model.trim="diskConfigData.app"
:invalid="$v.diskConfigData.app.$error"
type="text"
name="name"
class="mt-2"
@input="$v.diskConfigData.app.$touch()"
/>
</sw-input-group>
</div>
<div class="flex items-center mt-6" v-if="!isDisabled">
<div class="relative flex items-center w-12">
<sw-switch class="flex" v-model="set_as_default"/>
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>
</div>
</div>
<slot :disk-data="{ isLoading, submitData }" />
</form>
</template>
<script>
const { required, edisk } = require('vuelidate/lib/validators')
import { mapActions, mapGetters } from 'vuex'
export default {
props: {
isEdit: {
type: Boolean,
require: true,
default: false,
},
loading: {
type: Boolean,
require: true,
default: false,
},
disks: {
type: Array,
require: true,
default: Array,
},
},
data() {
return {
diskConfigData: {
selected_driver: 'dropbox',
token: '',
key: '',
secret: '',
app: '',
},
name: '',
set_as_default: false,
isLoading: false,
is_current_disk: null,
selected_disk: 'dropbox',
}
},
validations: {
diskConfigData: {
token: {
required,
},
key: {
required,
},
secret: {
required,
},
app: {
required,
},
root: {
required,
},
},
name: {
required,
},
selected_disk: {
required,
},
},
computed: {
...mapGetters('modal', ['modalData']),
nameError() {
if (!this.$v.name.$error) {
return ''
}
if (!this.$v.name.required) {
return this.$tc('validation.required')
}
},
typeError() {
if (!this.$v.diskConfigData.type.$error) {
return ''
}
if (!this.$v.diskConfigData.type.required) {
return this.$tc('validation.required')
}
},
tokenError() {
if (!this.$v.diskConfigData.token.$error) {
return ''
}
if (!this.$v.diskConfigData.token.required) {
return this.$tc('validation.required')
}
},
keyError() {
if (!this.$v.diskConfigData.key.$error) {
return ''
}
if (!this.$v.diskConfigData.key.required) {
return this.$tc('validation.required')
}
},
secretError() {
if (!this.$v.diskConfigData.secret.$error) {
return ''
}
if (!this.$v.diskConfigData.secret.required) {
return this.$tc('validation.required')
}
},
appError() {
if (!this.$v.diskConfigData.app.$error) {
return ''
}
if (!this.$v.diskConfigData.app.required) {
return this.$tc('validation.required')
}
},
rootError() {
if (!this.$v.diskConfigData.root.$error) {
return ''
}
if (!this.$v.diskConfigData.root.required) {
return this.$tc('validation.required')
}
},
isDisabled() {
return (this.isEdit && this.set_as_default && this.is_current_disk) ? true : false
}
},
created() {
this.loadData()
},
methods: {
...mapActions('disks', ['fetchDiskEnv', 'updateDisk']),
async loadData() {
this.isLoading = true
let data = {
disk: this.selected_disk,
}
if(this.isEdit) {
this.diskConfigData = JSON.parse(this.modalData.credentials)
this.set_as_default = this.modalData.set_as_default
if(this.set_as_default) {
this.is_current_disk = true
}
this.name = this.modalData.name
} else {
let diskData = await this.fetchDiskEnv(data)
this.diskConfigData = diskData.data
}
this.selected_disk = this.disks.find((v) => v.value == 'dropbox')
this.isLoading = false
},
async submitData() {
this.$v.$touch()
if (this.$v.$invalid) {
return
}
let data = {
credentials: this.diskConfigData,
name: this.name,
driver: this.selected_disk.value,
set_as_default: this.set_as_default,
}
this.$emit('submit', data)
return false
},
onChangeDriver() {
this.$emit('on-change-disk', this.selected_disk)
},
},
}
</script>

View File

@@ -0,0 +1,177 @@
<template>
<form @submit.prevent="submitData">
<div class="px-8 py-6">
<div class="grid gap-6 grid-col-1 md:grid-cols-2">
<sw-input-group
:label="$t('settings.disk.name')"
:error="nameError"
required
>
<sw-input
v-model="name"
type="text"
name="name"
class="mt-2"
:invalid="$v.name.$error"
@input="$v.name.$touch()"
/>
</sw-input-group>
<sw-input-group :label="$tc('settings.disk.driver')" required>
<sw-select
v-model="selected_disk"
:invalid="$v.selected_disk.$error"
:options="disks"
:searchable="true"
:allow-empty="false"
:show-labels="false"
track-by="value"
label="name"
class="mt-2"
@input="onChangeDriver"
/>
</sw-input-group>
<sw-input-group :label="$t('settings.disk.local_root')" required>
<sw-input
v-model.trim="diskConfigData.root"
type="text"
name="name"
placeholder="Ex. /user/root/"
class="mt-2"
/>
</sw-input-group>
</div>
<div class="flex items-center mt-6" v-if="!isDisabled">
<div class="relative flex items-center w-12">
<sw-switch class="flex" v-model="set_as_default"/>
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>
</div>
</div>
<slot :disk-data="{ isLoading, submitData }" />
</form>
</template>
<script>
const { required, edisk } = require('vuelidate/lib/validators')
import { mapActions, mapGetters } from 'vuex'
export default {
props: {
isEdit: {
type: Boolean,
require: true,
default: false,
},
loading: {
type: Boolean,
require: true,
default: false,
},
disks: {
type: Array,
require: true,
default: Array,
},
},
data() {
return {
diskConfigData: {
selected_driver: 'local',
root: '',
},
name: '',
isLoading: false,
set_as_default: false,
selected_disk: null,
is_current_disk: null,
is_current_disk: null,
}
},
validations: {
diskConfigData: {
root: {
required,
},
},
name: {
required,
},
selected_disk: {
required,
},
},
computed: {
...mapGetters('modal', ['modalData']),
nameError() {
if (!this.$v.name.$error) {
return ''
}
if (!this.$v.name.required) {
return this.$tc('validation.required')
}
},
isDisabled() {
return (this.isEdit && this.set_as_default && this.is_current_disk) ? true : false
}
},
created() {
this.loadData()
},
methods: {
...mapActions('disks', ['fetchDiskEnv', 'updateDisk']),
async loadData() {
this.isLoading = true
let data = {
disk: 'local',
}
if(this.isEdit) {
this.diskConfigData = JSON.parse(this.modalData.credentials)
this.set_as_default = this.modalData.set_as_default
if(this.set_as_default) {
this.is_current_disk = true
}
this.name = this.modalData.name
} else {
let diskData = await this.fetchDiskEnv(data)
this.diskConfigData = diskData.data
}
this.selected_disk = this.disks.find((v) => v.value == 'local')
this.isLoading = false
},
async submitData() {
this.$v.$touch()
if (this.$v.$invalid) {
return
}
let data = {
credentials: this.diskConfigData,
name: this.name,
driver: this.selected_disk.value,
set_as_default: this.set_as_default,
}
this.$emit('submit', data)
return false
},
onChangeDriver() {
this.$emit('on-change-disk', this.selected_disk)
},
},
}
</script>

View File

@@ -0,0 +1,313 @@
<template>
<form @submit.prevent="submitData">
<div class="px-8 py-6">
<div class="grid gap-6 grid-col-1 md:grid-cols-2">
<sw-input-group
:label="$t('settings.disk.name')"
:error="nameError"
required
>
<sw-input
v-model="name"
type="text"
name="name"
class="mt-2"
:invalid="$v.name.$error"
@input="$v.name.$touch()"
/>
</sw-input-group>
<sw-input-group :label="$tc('settings.disk.driver')" required>
<sw-select
v-model="selected_disk"
:invalid="$v.selected_disk.$error"
:options="disks"
:searchable="true"
:allow-empty="false"
:show-labels="false"
track-by="value"
label="name"
class="mt-2"
@input="onChangeDriver"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.aws_root')"
:error="rootError"
required
>
<sw-input
v-model.trim="diskConfigData.root"
:invalid="$v.diskConfigData.root.$error"
type="text"
name="name"
placeholder="Ex. /user/root/"
class="mt-2"
@input="$v.diskConfigData.root.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.aws_key')"
:error="keyError"
required
>
<sw-input
v-model.trim="diskConfigData.key"
:invalid="$v.diskConfigData.key.$error"
type="text"
name="name"
placeholder="Ex. KEIS4S39SERSDS"
class="mt-2"
@input="$v.diskConfigData.key.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.aws_secret')"
:error="secretError"
required
>
<sw-input
v-model.trim="diskConfigData.secret"
:invalid="$v.diskConfigData.secret.$error"
type="text"
name="name"
placeholder="Ex. ********"
class="mt-2"
@input="$v.diskConfigData.secret.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.aws_region')"
:error="regionError"
required
>
<sw-input
v-model.trim="diskConfigData.region"
:invalid="$v.diskConfigData.region.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. us-west"
@input="$v.diskConfigData.region.$touch()"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.disk.aws_bucket')"
:error="bucketError"
required
>
<sw-input
v-model.trim="diskConfigData.bucket"
:invalid="$v.diskConfigData.bucket.$error"
type="text"
name="name"
class="mt-2"
placeholder="Ex. AppName"
@input="$v.diskConfigData.bucket.$touch()"
/>
</sw-input-group>
</div>
<div class="flex items-center mt-6" v-if="!isDisabled">
<div class="relative flex items-center w-12">
<sw-switch class="flex" v-model="set_as_default"/>
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>
</div>
</div>
<slot :disk-data="{ isLoading, submitData }" />
</form>
</template>
<script>
const { required, edisk } = require('vuelidate/lib/validators')
import { mapActions, mapGetters } from 'vuex'
export default {
props: {
isEdit: {
type: Boolean,
require: true,
default: false,
},
loading: {
type: Boolean,
require: true,
default: false,
},
disks: {
type: Array,
require: true,
default: Array,
},
},
data() {
return {
diskConfigData: {
selected_driver: 's3',
key: '',
secret: '',
region: '',
bucket: '',
root: '',
},
name: '',
set_as_default: false,
isLoading: false,
selected_disk: null,
is_current_disk: null,
}
},
validations: {
diskConfigData: {
key: {
required,
},
secret: {
required,
},
region: {
required,
},
bucket: {
required,
},
root: {
required,
},
},
name: {
required,
},
selected_disk: {
required,
},
},
computed: {
...mapGetters('modal', ['modalData']),
nameError() {
if (!this.$v.name.$error) {
return ''
}
if (!this.$v.name.required) {
return this.$tc('validation.required')
}
},
driverError() {
if (!this.$v.diskConfigData.driver.$error) {
return ''
}
if (!this.$v.diskConfigData.driver.required) {
return this.$tc('validation.required')
}
},
keyError() {
if (!this.$v.diskConfigData.key.$error) {
return ''
}
if (!this.$v.diskConfigData.key.required) {
return this.$tc('validation.required')
}
},
secretError() {
if (!this.$v.diskConfigData.secret.$error) {
return ''
}
if (!this.$v.diskConfigData.secret.required) {
return this.$tc('validation.required')
}
},
regionError() {
if (!this.$v.diskConfigData.region.$error) {
return ''
}
if (!this.$v.diskConfigData.region.required) {
return this.$tc('validation.required')
}
},
bucketError() {
if (!this.$v.diskConfigData.bucket.$error) {
return ''
}
if (!this.$v.diskConfigData.bucket.required) {
return this.$tc('validation.required')
}
},
rootError() {
if (!this.$v.diskConfigData.root.$error) {
return ''
}
if (!this.$v.diskConfigData.root.required) {
return this.$tc('validation.required')
}
},
isDisabled() {
return (this.isEdit && this.set_as_default && this.is_current_disk) ? true : false
}
},
created() {
this.loadData()
},
methods: {
...mapActions('disks', ['fetchDiskEnv', 'updateDisk']),
async loadData() {
this.isLoading = true
let data = {
disk: 's3',
}
if(this.isEdit) {
this.diskConfigData = JSON.parse(this.modalData.credentials)
this.set_as_default = this.modalData.set_as_default
if(this.set_as_default) {
this.is_current_disk = true
}
this.name = this.modalData.name
} else {
let diskData = await this.fetchDiskEnv(data)
this.diskConfigData = diskData.data
}
this.selected_disk = this.disks.find((v) => v.value == 's3')
this.isLoading = false
},
async submitData() {
this.$v.$touch()
if (this.$v.$invalid) {
return
}
let data = {
credentials: this.diskConfigData,
name: this.name,
driver: this.selected_disk.value,
set_as_default: this.set_as_default,
}
this.$emit('submit', data)
return false
},
onChangeDriver() {
this.$emit('on-change-disk', this.selected_disk)
},
},
}
</script>