Add New SweetAlert & Notification Components

This commit is contained in:
Mohit Panjwani
2021-04-09 12:35:50 +00:00
78 changed files with 2295 additions and 984 deletions

View File

@ -34,14 +34,13 @@
"lodash": "^4.17.13",
"moment": "^2.29.1",
"sweet-modal-vue": "^2.0.0",
"sweetalert": "^2.1.2",
"tailwindcss": "^2.0.1",
"toastr": "^2.1.4",
"v-tooltip": "^2.0.2",
"vue": "^2.6.10",
"vue-i18n": "^8.22.0",
"vue-loader": "^15.9.3",
"vue-router": "2.7.0",
"vue-sweetalert2": "^4.2.1",
"vue2-transitions": "^0.3.0",
"vuedraggable": "^2.24.2",
"vuelidate": "^0.6.2",

View File

@ -8,7 +8,6 @@ import router from './router.js'
import store from './store/index'
import utils from './helpers/utilities'
import i18n from './plugins/i18n'
import swal from 'sweetalert'
require('./bootstrap')
@ -27,5 +26,4 @@ new Vue({
router,
store,
i18n,
swal,
}).$mount('#app')

View File

@ -8,6 +8,8 @@ import money from 'v-money'
import VTooltip from 'v-tooltip'
import Transitions from 'vue2-transitions'
import SpaceWind from '@bytefury/spacewind'
import swal from 'vue-sweetalert2'
import 'sweetalert2/dist/sweetalert2.min.css'
/**
* Theme
@ -22,6 +24,33 @@ Vue.use(SpaceWind, { theme })
Vue.use(Vuelidate)
Vue.use(swal, {
customClass: {
container:
'fixed z-50 inset-0 overflow-y-auto bg-black bg-opacity-25 flex justify-center min-h-screen items-center sm:p-0 swal2-container',
popup:
'flex items-center flex-col justify-center align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6',
header: 'swal2-header',
title: 'swal2-title',
closeButton: '',
icon: 'swal2-icon',
image: '',
content: 'swal2-content',
input: '',
inputLabel: '',
validationMessage: '',
actions: 'swal2-actions',
confirmButton:
'w-full inline-flex py-2 px-4 text-sm leading-5 rounded items-center justify-center text-white font-normal transition duration-150 ease-in-out border border-transparent focus:outline-none bg-primary-500 hover:bg-opacity-75 whitespace-nowrap',
denyButton: '',
cancelButton:
'w-full inline-flex py-2 px-4 text-sm leading-5 rounded justify-center items-center focus:outline-none font-normal transition ease-in-out duration-150 border border-transparent border border-solid border-primary-500 text-primary-500 hover:bg-primary-200 shadow-inner whitespace-nowrap',
loader: '',
footer: '',
},
buttonsStyling: false,
})
Vue.use(Transitions)
window._ = require('lodash')
@ -89,10 +118,11 @@ global.axios.interceptors.response.use(undefined, function (err) {
return true
}
if (!err.response) {
window.toastr['error'](
'Please check your internet connection or wait until servers are back online',
'Network Error'
)
store.dispatch('notification/showNotification', {
type: 'error',
message:
'Please check your internet connection or wait until servers are back online.',
})
} else {
if (
err.response.data &&
@ -100,24 +130,30 @@ global.axios.interceptors.response.use(undefined, function (err) {
err.response.data === ' Unauthorized.')
) {
// Unauthorized and log out
window.toastr['error'](
err.response.data.message ? err.response.data.message : 'Unauthorized'
)
store.dispatch('notification/showNotification', {
type: 'error',
message: err.response.data.message
? err.response.data.message
: 'Unauthorized',
})
store.dispatch('auth/logout', true)
} else if (err.response.data.errors) {
// Show a notification per error
const errors = JSON.parse(JSON.stringify(err.response.data.errors))
for (const i in errors) {
window.toastr['error'](errors[i])
store.dispatch('notification/showNotification', {
type: 'error',
message: errors[i],
})
}
} else {
// Unknown error
window.toastr['error'](
err.response.data.message
store.dispatch('notification/showNotification', {
type: 'error',
message: err.response.data.message
? err.response.data.message
: err.response.data || 'Unknown error occurred',
'Error'
)
})
}
}
return Promise.reject(err)
@ -126,8 +162,6 @@ global.axios.interceptors.response.use(undefined, function (err) {
/**
* Global plugins
*/
window.toastr = require('toastr')
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(VTooltip)

View File

@ -0,0 +1,147 @@
<template>
<transition
enter-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2 "
enter-active-class="transition duration-300 ease-out transform"
enter-to-class="duration-300 translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition duration-100 ease-in"
leave-class="duration-200 opacity-100"
leave-to-class="duration-200 opacity-0"
>
<div
v-if="notificationActive"
class="fixed inset-0 z-50 flex items-end justify-center px-4 py-6 pointer-events-none sm:p-6 sm:items-start sm:justify-end"
>
<div
:class="success || info ? 'bg-white' : 'bg-red-50'"
class="w-full max-w-sm rounded-lg shadow-lg cursor-pointer pointer-events-auto"
@click="hideNotification"
>
<div class="overflow-hidden rounded-lg shadow-xs">
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<svg
v-if="success"
class="w-6 h-6 text-green-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<exclamation-circle-icon
v-if="info"
class="w-6 h-6 text-blue-400"
/>
<svg
v-if="error"
class="w-6 h-6 text-red-400"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class="flex-1 w-0 ml-3">
<p
:class="`text-sm leading-5 font-medium ${
success || info ? 'text-gray-900' : 'text-red-800'
}`"
>
{{
notificationTitle ? notificationTitle : success ? 'Success!' : 'Error'
}}
</p>
<p
:class="`mt-1 text-sm leading-5 ${
success || info ? 'text-gray-500' : 'text-red-700'
}`"
>
{{
notificationMessage
? notificationMessage
: success
? 'Successful'
: 'Somthing went wrong'
}}
</p>
</div>
<div class="flex flex-shrink-0">
<button
:class="
success || info
? ' text-gray-400 focus:text-gray-500'
: 'text-red-400 focus:text-red-500'
"
class="inline-flex w-5 h-5 transition duration-150 ease-in-out focus:outline-none"
@click="hideNotification"
>
<x-icon />
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { XIcon } from '@vue-hero-icons/outline'
import { ExclamationCircleIcon } from '@vue-hero-icons/solid'
export default {
components: {
XIcon,
ExclamationCircleIcon,
},
data() {
return {
hasFocus: false,
}
},
computed: {
...mapGetters('notification', [
'notificationActive',
'notificationTitle',
'notificationType',
'notificationAutoHide',
'notificationMessage',
]),
success() {
return this.notificationType.toLowerCase() === 'success'
},
error() {
return this.notificationType.toLowerCase() === 'error'
},
info() {
return this.notificationType.toLowerCase() === 'info'
},
},
watch: {
notificationActive(val) {
if (val && this.notificationAutoHide) {
window.setTimeout(this.hideNotification, 5000)
}
},
},
mounted() {
if (this.notificationActive && this.notificationAutoHide) {
window.setTimeout(this.hideNotification, 5000)
}
},
methods: {
...mapActions('notification', ['showNotification', 'hideNotification']),
},
}
</script>

View File

@ -13,6 +13,7 @@ import NoteSelectPopup from './popup/NoteSelectPopup.vue'
import BaseDatePicker from '../base/BaseDatePicker.vue'
import BaseTimePicker from './BaseTimePicker.vue'
import BasePage from './BasePage.vue'
import BaseNotification from './BaseNotification.vue'
import GlobalSearch from '../GlobalSearch.vue'
@ -39,6 +40,7 @@ Vue.component('tax-select-popup', TaxSelectPopup)
Vue.component('note-select-popup', NoteSelectPopup)
Vue.component('base-time-picker', BaseTimePicker)
Vue.component('base-notification', BaseNotification)
Vue.component('dot-icon', DotIcon)
Vue.component('save-icon', SaveIcon)

View File

@ -2,8 +2,8 @@
<div class="relative customer-modal">
<base-loader
v-if="isRequestOngoing"
class="h-130"
:show-bg-overlay="true"
class="h-130"
/>
<form @submit.prevent="createNewBackup">
<div class="p-6">
@ -21,7 +21,7 @@
:show-labels="false"
:placeholder="$t('settings.backup.select_backup_type')"
:allow-empty="false"
:maxHeight="100"
:max-height="100"
/>
</sw-input-group>
<sw-input-group
@ -38,11 +38,11 @@
:show-labels="false"
:placeholder="$t('settings.disk.select_disk')"
:allow-empty="false"
track-by="id"
:preselect-first="true"
:custom-label="getCustomLabel"
:maxHeight="100"
:max-height="100"
:loading="isLoading"
track-by="id"
/>
</sw-input-group>
</div>
@ -59,9 +59,9 @@
</sw-button>
<sw-button
:loading="isCreateLoading"
:disabled="isCreateLoading"
variant="primary"
type="submit"
:disabled="isCreateLoading"
>
<save-icon v-if="!isCreateLoading" class="mr-2" />
{{ $t('general.create') }}
@ -140,6 +140,8 @@ export default {
...mapActions('modal', ['closeModal']),
...mapActions('notification', ['showNotification']),
getCustomLabel({ driver, name }) {
return `${name} — [${driver}]`
},
@ -154,12 +156,18 @@ export default {
this.isCreateLoading = true
await this.createBackup(data)
this.isCreateLoading = false
window.toastr['success'](this.$t('settings.backup.created_message'))
this.showNotification({
type: 'success',
message: this.$t('settings.backup.created_message'),
})
this.refreshData ? this.refreshData() : ''
this.cancelBackup()
} catch (e) {
this.isCreateLoading = false
window.toastr['error'](e.response.data.message)
this.showNotification({
type: 'error',
message: e.response.data.message,
})
}
},

View File

@ -41,7 +41,7 @@
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button variant="primary" type="submit" :loading="isLoading">
<sw-button :loading="isLoading" variant="primary" type="submit">
<save-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</sw-button>
@ -135,6 +135,8 @@ export default {
methods: {
...mapActions('modal', ['closeModal']),
...mapActions('category', ['addCategory', 'updateCategory']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
id: null,
@ -159,13 +161,15 @@ export default {
if (response.data) {
if (!this.isEdit) {
window.toastr['success'](
this.$t('settings.expense_category.created_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.expense_category.created_message'),
})
} else {
window.toastr['success'](
this.$t('settings.expense_category.updated_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.expense_category.updated_message'),
})
}
window.hub.$emit('newCategory', response.data.category)
this.refreshData ? this.refreshData() : ''
@ -173,7 +177,10 @@ export default {
this.isLoading = false
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
},
async setData() {
this.formData = {

View File

@ -78,9 +78,9 @@
/>
</sw-input-group>
<sw-input-group
v-if="isDropdownSelected"
:label="$t('settings.custom_fields.options')"
class="mt-5"
v-if="isDropdownSelected"
horizontal
>
<option-create @onAdd="addNewOptions" />
@ -92,28 +92,28 @@
>
<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"
@click="removeOption(index)"
/>
</div>
</sw-input-group>
<sw-input-group
v-if="formData.type"
:label="$t('settings.custom_fields.default_value')"
horizontal
class="relative mt-5"
v-if="formData.type"
>
<component
:value="formData.default_answer"
:is="formData.type + 'Type'"
:options="formData.options"
:defaultDateTime="formData.dateTimeValue"
:default-date-time="formData.dateTimeValue"
v-model="formData.default_answer"
/>
</sw-input-group>
<sw-input-group
:label="$t('settings.custom_fields.placeholder')"
v-if="!isSwitchTypeSelected"
:label="$t('settings.custom_fields.placeholder')"
class="mt-5"
horizontal
>
@ -376,6 +376,7 @@ export default {
'fetchCustomField',
]),
...mapActions('modal', ['closeModal']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
label: null,
@ -433,9 +434,10 @@ export default {
if (this.isEdit) {
this.isLoading = true
response = await this.updateCustomField(data)
window.toastr['success'](
this.$tc('settings.custom_fields.updated_message')
)
this.showNotification({
type: 'success',
message: this.$tc('settings.custom_fields.updated_message'),
})
this.refreshData()
this.closeCategoryModal()
return true
@ -444,7 +446,10 @@ export default {
this.isLoading = true
response = await this.addCustomField(data)
window.toastr['success'](this.$tc('settings.custom_fields.added_message'))
this.showNotification({
type: 'success',
message: this.$tc('settings.custom_fields.added_message'),
})
this.refreshData()
this.closeCategoryModal()
return true

View File

@ -62,7 +62,7 @@
:allow-empty="false"
:show-labels="false"
:placeholder="$t('customers.select_currency')"
:maxHeight="200"
:max-height="200"
label="name"
class="mt-1 md:mt-0"
track-by="id"
@ -343,7 +343,7 @@
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button variant="primary" type="submit" :loading="isLoading">
<sw-button :loading="isLoading" variant="primary" type="submit">
<save-icon v-if="!isLoading" class="mr-2" />
{{ $t('general.save') }}
</sw-button>
@ -444,6 +444,8 @@ export default {
...mapGetters(['currencies', 'countries']),
...mapGetters('company', ['defaultCurrency']),
...mapGetters('modal', ['modalDataID', 'modalData', 'modalActive']),
...mapActions('notification', ['showNotification']),
nameError() {
if (!this.$v.formData.name.$error) {
return ''
@ -586,6 +588,7 @@ export default {
'updateCustomer',
]),
...mapActions('modal', ['closeModal']),
...mapActions('notification', ['showNotification']),
resetData() {
this.formData = {
name: null,
@ -700,9 +703,15 @@ export default {
}
if (response.data) {
if (this.modalDataID) {
window.toastr['success'](this.$tc('customers.updated_message'))
this.showNotification({
type: 'success',
message: this.$tc('customers.updated_message'),
})
} else {
window.toastr['success'](this.$tc('customers.created_message'))
this.showNotification({
type: 'success',
message: this.$tc('customers.created_message'),
})
}
this.isLoading = false

View File

@ -5,9 +5,9 @@
:is="selected_disk"
:loading="isLoading"
:disks="getDiskDrivers"
:is-edit="isEdit"
@on-change-disk="(disk) => (selected_disk = disk.value)"
@submit="createNewDisk"
:is-edit="isEdit"
>
<template v-slot="slotProps">
<div
@ -16,15 +16,15 @@
<sw-button
class="mr-3 text-sm"
variant="primary-outline"
@click="closeDisk"
type="button"
@click="closeDisk"
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button
:loading="isRequestFire(slotProps)"
variant="primary"
:disabled="isRequestFire(slotProps)"
variant="primary"
type="submit"
>
<save-icon v-if="!isRequestFire(slotProps)" class="mr-2" />
@ -96,6 +96,8 @@ export default {
...mapActions('modal', ['closeModal']),
...mapActions('notification', ['showNotification']),
isRequestFire(slotProps) {
return slotProps && (slotProps.diskData.isLoading || this.isLoading)
},
@ -131,14 +133,21 @@ export default {
this.refreshData()
this.closeDisk()
if (this.isEdit) {
window.toastr['success'](this.$t('settings.disk.success_update'))
this.showNotification({
type: 'success',
message: this.$t('settings.disk.success_update'),
})
} else {
window.toastr['success'](this.$t('settings.disk.success_create'))
this.showNotification({
type: 'success',
message: this.$t('settings.disk.success_create'),
})
}
} else {
window.toastr['error'](
this.$t('settings.disk.invalid_disk_credentials')
)
this.showNotification({
type: 'error',
message: this.$t('settings.disk.invalid_disk_credentials'),
})
}
this.isLoading = false
},

View File

@ -251,6 +251,7 @@ export default {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('item', ['addItem', 'updateItem', 'fetchItemUnits']),
...mapActions('invoice', ['setItem']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
@ -304,7 +305,10 @@ export default {
response = await this.addItem(data)
}
if (response.data) {
window.toastr['success'](this.$tc('items.created_message'))
this.showNotification({
type: 'success',
message: this.$tc('items.created_message'),
})
this.setItem(response.data.item)
window.hub.$emit('newItem', response.data.item)
@ -314,7 +318,10 @@ export default {
this.closeModal()
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
},
closeItemModal() {

View File

@ -97,6 +97,7 @@ export default {
methods: {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('item', ['addItemUnit', 'updateItemUnit', 'fatchItemUnit']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
id: null,
@ -123,13 +124,17 @@ export default {
if (response.data) {
this.isLoading = false
if (!this.isEdit) {
window.toastr['success'](
this.$t('settings.customization.items.item_unit_added')
)
this.showNotification({
type: 'success',
message: this.$t('settings.customization.items.item_unit_added'),
})
} else {
window.toastr['success'](
this.$t('settings.customization.items.item_unit_updated')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.items.item_unit_updated'
),
})
}
this.refreshData ? this.refreshData() : ''
this.closeItemUnitModal()
@ -137,7 +142,10 @@ export default {
}
} catch (error) {
this.isLoading = false
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
}
},
async setData() {

View File

@ -4,8 +4,8 @@
<div class="p-4 md:p-8">
<sw-input-group
:label="$t('general.to')"
class="mt-3"
:error="emailError"
class="mt-3"
variant="horizontal"
required
>
@ -19,8 +19,8 @@
</sw-input-group>
<sw-input-group
:label="$t('general.subject')"
class="mt-3"
:error="subjectError"
class="mt-3"
variant="horizontal"
required
>
@ -33,8 +33,8 @@
</sw-input-group>
<sw-input-group
:label="$t('general.message')"
class="mt-3"
:error="messageError"
class="mt-3"
variant="horizontal"
required
>
@ -57,7 +57,7 @@
>
{{ $t('general.cancel') }}
</sw-button>
<sw-button variant="primary" type="submit" :loading="isLoading">
<sw-button :loading="isLoading" variant="primary" type="submit">
<paper-airplane-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.send') : $t('general.update') }}
</sw-button>
@ -149,6 +149,7 @@ export default {
methods: {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('company', ['sendTestMail']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
to: null,
@ -169,18 +170,26 @@ export default {
if (response.data) {
if (response.data.success) {
window.toastr['success'](this.$tc('general.send_mail_successfully'))
this.showNotification({
type: 'success',
message: this.$tc('general.send_mail_successfully'),
})
this.closeTaxModal()
this.isLoading = false
return true
}
window.toastr['error'](this.$tc('validation.something_went_wrong'))
this.showNotification({
type: 'error',
message: this.$tc('validation.something_went_wrong'),
})
this.closeTaxModal()
this.isLoading = false
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
},
closeTaxModal() {
this.resetModalData()

View File

@ -147,6 +147,11 @@ export default {
required,
},
},
watch: {
noteType() {
this.setFields()
},
},
async mounted() {
this.setFields()
if (this.modalDataID) {
@ -158,14 +163,10 @@ export default {
: (this.noteType = 'Invoice')
}
},
watch: {
noteType() {
this.setFields()
},
},
methods: {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('notes', ['addNote', 'updateNote']),
...mapActions('notification', ['showNotification']),
...mapActions('invoice', {
setInvoiceNote: 'selectNote',
}),
@ -222,15 +223,19 @@ export default {
let res = await this.updateNote(data)
if (res.data) {
window.toastr['success'](
this.$t('settings.customization.notes.note_updated')
)
this.showNotification({
type: 'success',
message: this.$t('settings.customization.notes.note_updated'),
})
this.refreshData ? this.refreshData() : ''
this.closeNoteModal()
return true
}
window.toastr['error'](res.data.error)
this.showNotification({
type: 'error',
message: res.data.error,
})
} else {
try {
let data = {
@ -243,9 +248,10 @@ export default {
if (response.data && response.data.note) {
this.isLoading = false
window.toastr['success'](
this.$t('settings.customization.notes.note_added')
)
this.showNotification({
type: 'success',
message: this.$t('settings.customization.notes.note_added'),
})
if (
(this.$route.name === 'invoices.create' &&
response.data.note.type === 'Invoice') ||
@ -277,7 +283,10 @@ export default {
this.closeNoteModal()
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
} catch (err) {
if (err.response.data.errors.name) {
this.isLoading = true

View File

@ -26,7 +26,7 @@
{{ $t('general.cancel') }}
</sw-button>
<sw-button :loading="isLoading" variant="primary" type="submit">
<save-icon class="mr-2" v-if="!isLoading" />
<save-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</sw-button>
</div>
@ -90,6 +90,7 @@ export default {
methods: {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('payment', ['addPaymentMode', 'updatePaymentMode']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
id: null,
@ -108,27 +109,39 @@ export default {
if (this.isEdit) {
response = await this.updatePaymentMode(this.formData)
if (response.data) {
window.toastr['success'](
this.$t('settings.customization.payments.payment_mode_updated')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.payments.payment_mode_updated'
),
})
this.refreshData ? this.refreshData() : ''
this.closePaymentModeModal()
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: 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')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.payments.payment_mode_added'
),
})
this.refreshData ? this.refreshData() : ''
this.closePaymentModeModal()
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
} catch (err) {
this.isLoading = false
}

View File

@ -61,8 +61,8 @@
v-model="formData.body"
:fields="estimateMailFields"
:invalid="$v.formData.body.$error"
@input="$v.formData.body.$touch()"
class="mt-2"
@input="$v.formData.body.$touch()"
/>
</sw-input-group>
</div>
@ -140,6 +140,7 @@ export default {
computed: {
...mapGetters('modal', ['modalDataID', 'modalData', 'modalActive']),
...mapGetters('user', ['currentUser']),
...mapActions('notification', ['showNotification']),
getEmailUrl() {
return this.url
},
@ -224,15 +225,31 @@ export default {
if (this.$v.$invalid) {
return true
}
swal({
this.$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) => {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
try {
if (value) {
if (result.value) {
let data = {
...this.formData,
id: this.modalDataID,
@ -244,21 +261,26 @@ export default {
this.closeModal()
if (res.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('estimates.send_estimate_successfully')
)
this.showNotification({
type: 'success',
message: 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')
)
this.showNotification({
type: 'error',
message: this.$tc('estimates.user_email_does_not_exist'),
})
return false
}
}
} catch (error) {
this.isLoading = false
window.toastr['error'](this.$tc('estimates.something_went_wrong'))
this.showNotification({
type: 'error',
message: this.$tc('estimates.something_went_wrong'),
})
}
})
},

View File

@ -55,8 +55,8 @@
v-model="formData.body"
:fields="InvoiceMailFields"
:invalid="$v.formData.body.$error"
@input="$v.formData.body.$touch()"
class="mt-2"
@input="$v.formData.body.$touch()"
/>
</sw-input-group>
</div>
@ -194,6 +194,8 @@ export default {
...mapActions('company', ['fetchCompanySettings', 'fetchMailConfig']),
...mapActions('notification', ['showNotification']),
async setInitialData() {
let admin = await this.fetchMailConfig()
@ -220,15 +222,31 @@ export default {
if (this.$v.$invalid) {
return true
}
swal({
this.$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) => {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
try {
if (value) {
if (result.value) {
let data = {
...this.formData,
id: this.modalDataID,
@ -239,21 +257,26 @@ export default {
this.closeModal()
if (res.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('invoices.send_invoice_successfully')
)
this.showNotification({
type: 'success',
message: 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')
)
this.showNotification({
type: 'error',
message: this.$tc('invoices.user_email_does_not_exist'),
})
return false
}
}
} catch (error) {
this.isLoading = false
window.toastr['error'](this.$tc('invoices.something_went_wrong'))
this.showNotification({
type: 'error',
message: this.$tc('invoices.something_went_wrong'),
})
}
})
},

View File

@ -4,9 +4,9 @@
<div class="px-8 py-8 sm:p-6">
<sw-input-group
:label="$t('general.from')"
:error="fromError"
class="mb-4"
variant="vertical"
:error="fromError"
required
>
<sw-input
@ -25,8 +25,8 @@
>
<sw-input
v-model="formData.to"
type="text"
:invalid="$v.formData.to.$error"
type="text"
@input="$v.formData.to.$touch()"
/>
</sw-input-group>
@ -188,6 +188,8 @@ export default {
...mapActions('company', ['fetchCompanySettings', 'fetchMailConfig']),
...mapActions('notification', ['showNotification']),
async setInitialData() {
let admin = await this.fetchMailConfig()
@ -216,15 +218,31 @@ export default {
if (this.$v.$invalid) {
return true
}
swal({
this.$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) => {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
try {
if (value) {
if (result.value) {
let data = {
...this.formData,
id: this.modalDataID,
@ -236,21 +254,26 @@ export default {
this.closeModal()
if (res.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('payments.send_payment_successfully')
)
this.showNotification({
type: 'success',
message: 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')
)
this.showNotification({
type: 'error',
message: this.$tc('payments.user_email_does_not_exist'),
})
return false
}
}
} catch (error) {
this.isLoading = false
window.toastr['error'](this.$tc('payments.something_went_wrong'))
this.showNotification({
type: 'error',
message: this.$tc('payments.something_went_wrong'),
})
}
})
},

View File

@ -9,9 +9,9 @@
:searchable="true"
:allow-empty="false"
:show-labels="false"
:custom-label="getCustomLabel"
class="mt-2"
track-by="id"
:custom-label="getCustomLabel"
/>
</sw-input-group>
</div>
@ -90,6 +90,8 @@ export default {
...mapActions('modal', ['closeModal']),
...mapActions('notification', ['showNotification']),
async loadData() {
this.loading = true
@ -107,7 +109,10 @@ export default {
if (response.data.success) {
this.refreshData()
this.closeDisk()
window.toastr['success'](this.$t('settings.disk.success'))
this.showNotification({
type: 'success',
message: this.$t('settings.disk.success'),
})
}
this.isLoading = true
},

View File

@ -69,7 +69,7 @@
{{ $t('general.cancel') }}
</sw-button>
<sw-button :loading="isLoading" variant="primary" type="submit">
<save-icon class="mr-2" v-if="!isLoading" />
<save-icon v-if="!isLoading" class="mr-2" />
{{ !isEdit ? $t('general.save') : $t('general.update') }}
</sw-button>
</div>
@ -174,6 +174,7 @@ export default {
methods: {
...mapActions('modal', ['closeModal', 'resetModalData']),
...mapActions('taxType', ['addTaxType', 'updateTaxType', 'fetchTaxType']),
...mapActions('notification', ['showNotification']),
resetFormData() {
this.formData = {
id: null,
@ -198,13 +199,15 @@ export default {
}
if (response.data) {
if (!this.isEdit) {
window.toastr['success'](
this.$t('settings.tax_types.created_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.tax_types.created_message'),
})
} else {
window.toastr['success'](
this.$t('settings.tax_types.updated_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.tax_types.updated_message'),
})
}
window.hub.$emit('newTax', response.data.taxType)
this.refreshData ? this.refreshData() : ''
@ -212,7 +215,10 @@ export default {
this.isLoading = false
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
},
async setData() {
this.formData = {

View File

@ -26,6 +26,7 @@ import estimateTemplate from './modules/estimate-template'
import invoiceTemplate from './modules/invoice-template'
import search from './modules/search'
import notes from './modules/notes'
import notification from './modules/notification'
Vue.use(Vuex)
@ -76,5 +77,6 @@ export default new Vuex.Store({
invoiceTemplate,
search,
notes,
notification,
},
})

View File

@ -13,7 +13,6 @@ export const login = ({ commit }, data) => {
commit('user/' + userTypes.RESET_CURRENT_USER, null, { root: true })
commit(rootTypes.UPDATE_APP_LOADING_STATUS, false, { root: true })
window.toastr['success']('Login Successful')
resolve(response)
})
.catch((err) => {
@ -28,7 +27,7 @@ export const setLogoutFalse = ({ state, commit }) => {
commit(types.SET_LOGOUT, false)
}
export const logout = ({ state, commit }) => {
export const logout = ({ state, commit, dispatch }) => {
return new Promise((resolve, reject) => {
if (state.isLoggedOut) {
resolve()
@ -40,7 +39,14 @@ export const logout = ({ state, commit }) => {
.get('/auth/logout')
.then(() => {
router.push('/login')
window.toastr['success']('Logged out!', 'Success')
dispatch(
'notification/showNotification',
{
type: 'success',
message: 'Logged out successfully.',
},
{ root: true }
)
})
.catch((err) => {
router.push('/login')

View File

@ -0,0 +1,27 @@
import * as types from './mutation-types'
export const showNotification = ({ commit, dispatch, state }, payload) => {
commit(types.SHOW_NOTIFICATION, true)
if (payload.type) {
commit(types.SET_NOTIFICATION_TYPE, payload.type)
}
if (payload.title) {
commit(types.SET_TITLE, payload.title)
}
if (payload.autoHide) {
commit(types.SET_AUTO_HIDE, payload.autoHide)
}
if (payload.message) {
commit(types.SET_MESSAGE, payload.message)
}
}
export const hideNotification = ({ commit, dispatch, state }) => {
commit(types.RESET_DATA)
commit(types.HIDE_NOTIFICATION, false)
}
export const resetNotificationData = ({ commit, dispatch, state }) => {
commit(types.RESET_DATA)
}

View File

@ -0,0 +1,5 @@
export const notificationActive = (state) => state.active
export const notificationTitle = (state) => state.title
export const notificationMessage = (state) => state.message
export const notificationAutoHide = (state) => state.autoHide
export const notificationType = (state) => state.type

View File

@ -0,0 +1,23 @@
import mutations from './mutations'
import * as actions from './actions'
import * as getters from './getters'
const initialState = {
active: false,
autoHide: true,
title: '',
message: '',
type: '',
}
export default {
namespaced: true,
state: initialState,
getters: getters,
actions: actions,
mutations: mutations,
}

View File

@ -0,0 +1,7 @@
export const SHOW_NOTIFICATION = 'SHOW_NOTIFICATION'
export const HIDE_NOTIFICATION = 'HIDE_NOTIFICATION'
export const SET_TITLE = 'SET_TITLE'
export const SET_AUTO_HIDE = 'SET_AUTO_HIDE'
export const SET_MESSAGE = 'SET_MESSAGE'
export const SET_NOTIFICATION_TYPE = 'SET_NOTIFICATION_TYPE'
export const RESET_DATA = 'RESET_DATA'

View File

@ -0,0 +1,33 @@
import * as types from './mutation-types'
export default {
[types.SHOW_NOTIFICATION](state, data) {
state.active = data
},
[types.HIDE_NOTIFICATION](state, data) {
state.active = data
},
[types.SET_TITLE](state, data) {
state.title = data
},
[types.SET_AUTO_HIDE](state, data) {
state.autoHide = data
},
[types.SET_MESSAGE](state, data) {
state.message = data
},
[types.SET_NOTIFICATION_TYPE](state, data) {
state.type = data
},
[types.RESET_DATA](state) {
state.active = false
state.description = ''
state.title = ''
},
}

View File

@ -45,9 +45,8 @@
</template>
<script type="text/babel">
import { async } from 'q'
const { required, email } = require('vuelidate/lib/validators')
import { mapActions } from 'vuex'
export default {
data() {
return {
@ -67,6 +66,7 @@ export default {
},
},
methods: {
...mapActions('notification', ['showNotification']),
async validateBeforeSubmit(e) {
this.$v.formData.$touch()
if (!this.$v.formData.$invalid) {
@ -78,7 +78,10 @@ export default {
)
if (res.data) {
toastr['success']('Mail sent successfuly!', 'Success')
this.showNotification({
type: 'success',
message: 'Mail sent successfuly!',
})
}
this.isSent = true

View File

@ -145,6 +145,7 @@ export default {
},
methods: {
...mapActions('auth', ['login']),
...mapActions('notification', ['showNotification']),
async validateBeforeSubmit() {
axios.defaults.withCredentials = true
@ -158,6 +159,10 @@ export default {
try {
await this.login(this.loginData)
this.$router.push('/admin/dashboard')
this.showNotification({
type: 'success',
message: 'Logged in successfully.',
})
this.isLoading = false
} catch (error) {
this.isLoading = false

View File

@ -92,6 +92,7 @@ const {
sameAs,
minLength,
} = require('vuelidate/lib/validators')
import { mapActions } from 'vuex'
export default {
data() {
@ -120,6 +121,7 @@ export default {
},
},
methods: {
...mapActions('notification', ['showNotification']),
async validateBeforeSubmit(e) {
this.$v.formData.$touch()
@ -135,18 +137,18 @@ export default {
let res = await axios.post('/api/v1/auth/reset/password', data)
this.isLoading = false
if (res.data) {
toastr['success'](
this.$t('login.password_reset_successfully'),
'Success'
)
this.showNotification({
type: 'success',
message: this.$t('login.password_reset_successfully'),
})
this.$router.push('/login')
}
} catch (err) {
if (err.response && err.response.status === 403) {
toastr['error'](
err.response.data,
this.$t('validation.email_incorrect')
)
this.showNotification({
type: 'error',
message: this.$t('validation.email_incorrect'),
})
this.isLoading = false
}
}

View File

@ -1,26 +1,26 @@
<template>
<base-page class="customer-create">
<form v-if="!initLoad" @submit.prevent="submitCustomerData">
<sw-page-header class="mb-5" :title="pageTitle">
<sw-page-header :title="pageTitle" class="mb-5">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item
to="/admin/dashboard"
:title="$t('general.home')"
to="/admin/dashboard"
/>
<sw-breadcrumb-item
to="/admin/customers"
:title="$tc('customers.customer', 2)"
to="/admin/customers"
/>
<sw-breadcrumb-item
v-if="$route.name === 'customers.edit'"
to="#"
:title="$t('customers.edit_customer')"
to="#"
active
/>
<sw-breadcrumb-item
v-else
to="#"
:title="$t('customers.new_customer')"
to="#"
active
/>
</sw-breadcrumb>
@ -56,8 +56,8 @@
>
<sw-input-group
:label="$t('customers.display_name')"
class="md:col-span-3"
:error="displayNameError"
class="md:col-span-3"
required
>
<sw-input
@ -85,8 +85,8 @@
<sw-input-group
:label="$t('customers.email')"
class="md:col-span-3"
:error="emailError"
class="md:col-span-3"
>
<sw-input
:invalid="$v.formData.email.$error"
@ -243,8 +243,8 @@
<sw-input-group :label="$t('customers.zip_code')">
<sw-input
tabindex="14"
v-model.trim="billing.zip"
tabindex="14"
type="text"
name="zip"
/>
@ -406,16 +406,16 @@
class="grid col-span-5 lg:col-span-4 gap-y-6 gap-x-4 md:grid-cols-6"
>
<sw-input-group
class="md:col-span-3"
v-for="(field, index) in customFields"
:label="field.label"
:required="field.is_required ? true : false"
:key="index"
class="md:col-span-3"
>
<component
:type="field.type.label"
:field="field"
:isEdit="isEdit"
:is-edit="isEdit"
:is="field.type + 'Field'"
:invalid-fields="invalidFields"
:tabindex="23 + index"
@ -694,7 +694,7 @@ export default {
'updateCustomer',
'fetchViewCustomer',
]),
...mapActions('notification', ['showNotification']),
...mapActions('customFields', ['fetchCustomFields']),
currencyNameWithCode({ name, code }) {
@ -786,10 +786,16 @@ export default {
this.$router.push(
`/admin/customers/${response.data.customer.id}/view`
)
window.toastr['success'](this.$t('customers.updated_message'))
this.showNotification({
type: 'success',
message: this.$t('customers.updated_message'),
})
}
if (response.data.error) {
window.toastr['error'](this.$t('validation.email_already_taken'))
this.showNotification({
type: 'error',
message: this.$t('validation.email_already_taken'),
})
}
} else {
response = await this.addCustomer(this.formData)
@ -797,7 +803,10 @@ export default {
this.$router.push(
`/admin/customers/${response.data.customer.id}/view`
)
window.toastr['success'](this.$t('customers.created_message'))
this.showNotification({
type: 'success',
message: this.$t('customers.created_message'),
})
}
}
@ -806,7 +815,10 @@ export default {
} catch (error) {
this.isLoading = false
if (err.response.data.errors.email) {
window.toastr['error'](this.$t('validation.email_already_taken'))
this.showNotification({
type: 'error',
message: this.$t('validation.email_already_taken'),
})
}
}
},

View File

@ -195,7 +195,11 @@
<template slot-scope="row">
<span>{{ $t('customers.contact_name') }}</span>
<span>
{{ row.contact_name ? row.contact_name : $t('customers.no_contact_name') }}
{{
row.contact_name
? row.contact_name
: $t('customers.no_contact_name')
}}
</span>
</template>
</sw-table-column>
@ -354,6 +358,7 @@ export default {
'deleteMultipleCustomers',
'setSelectAllState',
]),
...mapActions('notification', ['showNotification']),
refreshTable() {
this.$refs.table.refresh()
},
@ -398,43 +403,60 @@ export default {
},
async removeCustomer(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('customers.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteCustomer({ ids: [id] })
if (res.data.success) {
window.toastr['success'](this.$tc('customers.deleted_message', 1))
this.showNotification({
type: 'success',
message: this.$tc('customers.deleted_message', 1),
})
this.$refs.table.refresh()
return true
}
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: this.$tc(res.data.message),
})
return true
}
})
},
async removeMultipleCustomers() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('customers.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let request = await this.deleteMultipleCustomers()
if (request.data.success) {
window.toastr['success'](this.$tc('customers.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('customers.deleted_message', 2),
})
this.refreshTable()
} else if (request.data.error) {
window.toastr['error'](request.data.message)
this.showNotification({
type: 'error',
message: request.data.message,
})
}
}
})

View File

@ -3,8 +3,8 @@
<sw-page-header :title="pageTitle">
<template slot="actions">
<sw-button
tag-name="router-link"
:to="`/admin/customers/${$route.params.id}/edit`"
tag-name="router-link"
class="mr-3"
variant="primary-outline"
>
@ -15,29 +15,29 @@
{{ $t('customers.new_transaction') }}
</sw-button>
<sw-dropdown-item
tag-name="router-link"
:to="`/admin/estimates/create?customer=${$route.params.id}`"
tag-name="router-link"
>
<document-icon class="h-5 mr-3 text-gray-600" />
{{ $t('estimates.new_estimate') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`/admin/invoices/create?customer=${$route.params.id}`"
tag-name="router-link"
>
<document-text-icon class="h-5 mr-3 text-gray-600" />
{{ $t('invoices.new_invoice') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`/admin/payments/create?customer=${$route.params.id}`"
tag-name="router-link"
>
<credit-card-icon class="h-5 mr-3 text-gray-600" />
{{ $t('payments.new_payment') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`/admin/expenses/create?customer=${$route.params.id}`"
tag-name="router-link"
>
<calculator-icon class="h-5 mr-3 text-gray-600" />
{{ $t('expenses.new_expense') }}
@ -112,25 +112,48 @@ export default {
'selectCustomer',
'deleteMultipleCustomers',
]),
...mapActions('notification', ['showNotification']),
async removeCustomer(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('customers.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="trash"
class="svg-inline--fa fa-trash fa-w-14"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
<path
fill="#55547A"
d="M432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zM53.2 467a48 48 0 0 0 47.9 45h245.8a48 48 0 0 0 47.9-45L416 128H32z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let data = [id]
this.selectCustomer(data)
let res = await this.deleteMultipleCustomers()
if (res.data.success) {
window.toastr['success'](this.$tc('customers.deleted_message'))
this.showNotification({
type: 'success',
message: this.$tc('customers.deleted_message'),
})
this.$router.push('/admin/customers')
return true
} else if (request.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})

View File

@ -98,16 +98,16 @@
<sw-dropdown slot-scope="row">
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`invoices/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`invoices/${row.id}/view`"
tag-name="router-link"
>
<eye-icon class="h-5 mr-3 text-gray-600" />
{{ $t('invoices.view') }}
@ -227,16 +227,16 @@
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`estimates/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`estimates/${row.id}/view`"
tag-name="router-link"
>
<eye-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.view') }}
@ -350,6 +350,8 @@ export default {
'convertToInvoice',
]),
...mapActions('notification', ['showNotification']),
...mapActions('estimate', {
sendEstimateEmail: 'sendEmail',
markEstimateAsSent: 'markAsSent',
@ -362,25 +364,32 @@ export default {
async removeEstimate(id) {
this.id = id
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('estimates.confirm_delete', 1),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willDelete) => {
if (willDelete) {
let res = await this.deleteEstimate({ ids: [this.id] })
if (res.data.success) {
window.toastr['success'](this.$tc('estimates.deleted_message', 1))
this.refreshEstTable()
} else if (res.data.error) {
window.toastr['error'](res.data.message)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('estimates.confirm_delete', 1),
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteEstimate({ ids: [this.id] })
if (res.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('estimates.deleted_message', 1),
})
this.refreshEstTable()
} else if (res.data.error) {
this.showToaster({
type: 'error',
message: res.data.message,
})
}
})
}
})
},
refreshInvTable() {
@ -392,229 +401,355 @@ export default {
},
async convertInToinvoice(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_conversion'),
icon: '/assets/icon/file-alt-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willDelete) => {
if (willDelete) {
let res = await this.convertToInvoice(id)
this.selectAllField = false
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_conversion'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
viewBox="0 0 384 512"
class="w-6 h-6"
data-prefix="fas"
data-icon="file-alt"
class="svg-inline--fa fa-file-alt fa-w-12"
role="img"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="#55547A"
d="M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm64 236c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12v8zm0-64c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12v8zm0-72v8c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12zm96-114.1v6.1H256V0h6.1c6.4 0 12.5 2.5 17 7l97.9 98c4.5 4.5 7 10.6 7 16.9z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.convertToInvoice(id)
this.selectAllField = false
if (res.data) {
window.toastr['success'](this.$t('estimates.conversion_message'))
this.$router.push(`invoices/${res.data.invoice.id}/edit`)
} else if (res.data.error) {
window.toastr['error'](res.data.message)
}
if (res.data) {
this.showToaster({
type: 'success',
message: this.$t('estimates.conversion_message'),
})
this.$router.push(`invoices/${res.data.invoice.id}/edit`)
} else if (res.data.error) {
this.showToaster({
type: 'error',
message: res.data.message,
})
}
})
}
})
},
async onMarkAsSent(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_sent'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willMarkAsSent) => {
if (willMarkAsSent) {
const data = {
id: id,
status: 'SENT',
}
let response = await this.markEstimateAsSent(data)
this.refreshEstTable()
if (response.data) {
window.toastr['success'](
this.$tc('estimates.mark_as_sent_successfully')
)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_sent'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
status: 'SENT',
}
})
let response = await this.markEstimateAsSent(data)
this.refreshEstTable()
if (response.data) {
this.showToaster({
type: 'success',
message: this.$tc('estimates.mark_as_sent_successfully'),
})
}
}
})
},
async removeInvoice(id) {
this.id = id
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('invoices.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willDelete) => {
if (willDelete) {
let res = await this.deleteInvoice({ ids: [this.id] })
if (res.data.success) {
window.toastr['success'](this.$tc('invoices.deleted_message'))
this.refreshInvTable()
} else if (res.data.error) {
window.toastr['error'](res.data.message)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('invoices.confirm_delete'),
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteInvoice({ ids: [this.id] })
if (res.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('invoices.deleted_message'),
})
this.refreshInvTable()
} else if (res.data.error) {
this.showToaster({
type: 'error',
message: res.data.message,
})
}
})
}
})
},
async sendInvoice(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.confirm_send'),
icon: '/assets/icon/paper-plane-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willSendInvoice) => {
if (willSendInvoice) {
const data = {
id: id,
}
let response = await this.sendEmail(data)
this.refreshInvTable()
if (response.data.success) {
window.toastr['success'](
this.$tc('invoices.send_invoice_successfully')
)
return true
}
if (response.data.error === 'user_email_does_not_exist') {
window.toastr['error'](
this.$tc('invoices.user_email_does_not_exist')
)
return false
}
window.toastr['error'](this.$tc('invoices.something_went_wrong'))
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.confirm_send'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
focusable="false"
class="w-6 h-6"
data-prefix="fas"
data-icon="paper-plane"
class="svg-inline--fa fa-paper-plane fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M476 3.2L12.5 270.6c-18.1 10.4-15.8 35.6 2.2 43.2L121 358.4l287.3-253.2c5.5-4.9 13.3 2.6 8.6 8.3L176 407v80.5c0 23.6 28.5 32.9 42.5 15.8L282 426l124.6 52.2c14.2 6 30.4-2.9 33-18.2l72-432C515 7.8 493.3-6.8 476 3.2z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
}
})
let response = await this.sendEmail(data)
this.refreshInvTable()
if (response.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('invoices.send_invoice_successfully'),
})
return true
}
if (response.data.error === 'user_email_does_not_exist') {
this.showToaster({
type: 'error',
message: this.$tc('invoices.user_email_does_not_exist'),
})
return false
}
this.showToaster({
type: 'error',
message: this.$tc('invoices.something_went_wrong'),
})
}
})
},
async sentInvoice(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.invoice_mark_as_sent'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willMarkAsSend) => {
if (willMarkAsSend) {
const data = {
id: id,
status: 'SENT',
}
let response = await this.markAsSent(data)
this.refreshInvTable()
if (response.data) {
window.toastr['success'](
this.$tc('invoices.mark_as_sent_successfully')
)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.invoice_mark_as_sent'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
status: 'SENT',
}
})
let response = await this.markAsSent(data)
this.refreshInvTable()
if (response.data) {
this.showToaster({
type: 'success',
message: this.$tc('invoices.mark_as_sent_successfully'),
})
}
}
})
},
async onMarkAsAccepted(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_accepted'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (markedAsRejected) => {
if (markedAsRejected) {
const data = {
id: id,
}
let response = await this.markAsAccepted(data)
this.refreshEstTable()
if (response.data) {
this.refreshEstTable()
window.toastr['success'](
this.$tc('estimates.marked_as_accepted_message')
)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_accepted'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
}
})
let response = await this.markAsAccepted(data)
this.refreshEstTable()
if (response.data) {
this.refreshEstTable()
this.showToaster({
type: 'success',
message: this.$tc('estimates.marked_as_accepted_message'),
})
}
}
})
},
async onMarkAsRejected(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_rejected'),
icon: '/assets/icon/times-circle-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (markedAsRejected) => {
if (markedAsRejected) {
const data = {
id: id,
}
let response = await this.markAsRejected(data)
this.refreshEstTable()
if (response.data) {
this.refreshEstTable()
window.toastr['success'](
this.$tc('estimates.marked_as_rejected_message')
)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_rejected'),
icon: 'error',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#DC2626"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
}
})
let response = await this.markAsRejected(data)
this.refreshEstTable()
if (response.data) {
this.refreshEstTable()
this.showToaster({
type: 'success',
message: this.$tc('estimates.marked_as_rejected_message'),
})
}
}
})
},
async sendEstimate(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_send_estimate'),
icon: '/assets/icon/paper-plane-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (willSendEstimate) => {
if (willSendEstimate) {
const data = {
id: id,
}
let response = await this.sendEstimateEmail(data)
this.refreshEstTable()
if (response.data.success) {
window.toastr['success'](
this.$tc('estimates.send_estimate_successfully')
)
return true
}
if (response.data.error === 'user_email_does_not_exist') {
window.toastr['error'](
this.$tc('estimates.user_email_does_not_exist')
)
return true
}
window.toastr['error'](this.$tc('estimates.something_went_wrong'))
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_send_estimate'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
focusable="false"
class="w-6 h-6"
data-prefix="fas"
data-icon="paper-plane"
class="svg-inline--fa fa-paper-plane fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M476 3.2L12.5 270.6c-18.1 10.4-15.8 35.6 2.2 43.2L121 358.4l287.3-253.2c5.5-4.9 13.3 2.6 8.6 8.3L176 407v80.5c0 23.6 28.5 32.9 42.5 15.8L282 426l124.6 52.2c14.2 6 30.4-2.9 33-18.2l72-432C515 7.8 493.3-6.8 476 3.2z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
}
})
let response = await this.sendEstimateEmail(data)
this.refreshEstTable()
if (response.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('estimates.send_estimate_successfully'),
})
return true
}
if (response.data.error === 'user_email_does_not_exist') {
this.showToaster({
type: 'error',
message: this.$tc('estimates.user_email_does_not_exist'),
})
return true
}
this.showToaster({
type: 'error',
message: this.$tc('estimates.something_went_wrong'),
})
}
})
},
},
}

View File

@ -720,6 +720,8 @@ export default {
...mapActions('customFields', ['fetchCustomFields']),
...mapActions('notification', ['showNotification']),
selectFixed() {
if (this.newEstimate.discount_type === 'fixed') {
return
@ -921,7 +923,10 @@ export default {
.then((res) => {
if (res.data) {
this.$router.push(`/admin/estimates/${res.data.estimate.id}/view`)
window.toastr['success'](this.$t('estimates.created_message'))
this.showNotification({
type: 'success',
message: this.$t('estimates.created_message'),
})
}
this.isLoading = false
@ -937,7 +942,10 @@ export default {
this.isLoading = false
if (res.data) {
this.$router.push(`/admin/estimates/${res.data.estimate.id}/view`)
window.toastr['success'](this.$t('estimates.updated_message'))
this.showNotification({
type: 'success',
message: this.$t('estimates.updated_message'),
})
}
})
.catch((err) => {

View File

@ -2,11 +2,11 @@
<base-page>
<sw-page-header :title="$t('estimates.title')">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item to="dashboard" :title="$t('general.home')" />
<sw-breadcrumb-item :title="$t('general.home')" to="dashboard" />
<sw-breadcrumb-item
to="#"
:title="$tc('estimates.estimate', 2)"
to="#"
active
/>
</sw-breadcrumb>
@ -277,12 +277,12 @@
>
<template slot-scope="row">
<span> {{ $t('estimates.action') }} </span>
<sw-dropdown containerClass="w-56">
<sw-dropdown container-class="w-56">
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`estimates/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
@ -294,8 +294,8 @@
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`estimates/${row.id}/view`"
tag-name="router-link"
>
<eye-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.view') }}
@ -481,6 +481,8 @@ export default {
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
refreshTable() {
this.$refs.table.refresh()
},
@ -536,14 +538,30 @@ export default {
},
async onMarkAsAccepted(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_accepted'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (markedAsRejected) => {
if (markedAsRejected) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
status: 'ACCEPTED',
@ -553,23 +571,40 @@ export default {
if (response.data) {
this.$refs.table.refresh()
window.toastr['success'](
this.$tc('estimates.marked_as_accepted_message')
)
this.showNotification({
type: 'success',
message: this.$tc('estimates.marked_as_accepted_message'),
})
}
}
})
},
async onMarkAsRejected(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_rejected'),
icon: '/assets/icon/times-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (markedAsRejected) => {
if (markedAsRejected) {
icon: 'error',
iconHtml: `<svg
aria-hidden="true"
focusable="false"
class="w-6 h-6"
data-prefix="fas"
data-icon="times-circle"
class="svg-inline--fa fa-times-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#DC2626"
d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
status: 'REJECTED',
@ -579,9 +614,10 @@ export default {
if (response.data) {
this.$refs.table.refresh()
window.toastr['success'](
this.$tc('estimates.marked_as_rejected_message')
)
this.showNotification({
type: 'success',
message: this.$tc('estimates.marked_as_rejected_message'),
})
}
}
})
@ -624,65 +660,104 @@ export default {
async removeEstimate(id) {
this.id = id
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('estimates.confirm_delete', 1),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteEstimate({ ids: [this.id] })
if (res.data.success) {
this.$refs.table.refresh()
this.resetSelectedEstimates()
window.toastr['success'](this.$tc('estimates.deleted_message', 1))
this.showNotification({
type: 'success',
message: this.$tc('estimates.deleted_message', 1),
})
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})
},
async convertInToinvoice(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_conversion'),
icon: '/assets/icon/file-alt-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willConvertInToinvoice) => {
if (willConvertInToinvoice) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
viewBox="0 0 384 512"
class="w-6 h-6"
data-prefix="fas"
data-icon="file-alt"
class="svg-inline--fa fa-file-alt fa-w-12"
role="img"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="#55547A"
d="M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm64 236c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12v8zm0-64c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12v8zm0-72v8c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12zm96-114.1v6.1H256V0h6.1c6.4 0 12.5 2.5 17 7l97.9 98c4.5 4.5 7 10.6 7 16.9z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.convertToInvoice(id)
if (res.data) {
window.toastr['success'](this.$t('estimates.conversion_message'))
this.showNotification({
type: 'success',
message: this.$t('estimates.conversion_message'),
})
this.$router.push(`invoices/${res.data.invoice.id}/edit`)
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})
},
async removeMultipleEstimates() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('estimates.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteMultipleEstimates()
if (res.data.success) {
this.$refs.table.refresh()
this.resetSelectedEstimates()
window.toastr['success'](this.$tc('estimates.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('estimates.deleted_message', 2),
})
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})
@ -699,14 +774,30 @@ export default {
},
async onMarkAsSent(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_sent'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willMarkAsSent) => {
if (willMarkAsSent) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
status: 'SENT',
@ -716,9 +807,10 @@ export default {
this.refreshTable()
if (response.data) {
window.toastr['success'](
this.$tc('estimates.mark_as_sent_successfully')
)
this.showNotification({
type: 'success',
message: this.$tc('estimates.mark_as_sent_successfully'),
})
}
}
})

View File

@ -310,6 +310,8 @@ export default {
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
hasActiveUrl(id) {
return this.$route.params.id == id
},
@ -342,8 +344,10 @@ export default {
let pdfUrl = `${window.location.origin}/estimates/pdf/${this.estimate.unique_hash}`
let response = this.$utils.copyTextToClipboard(pdfUrl)
window.toastr['success'](this.$tc('general.copied_pdf_url_clipboard'))
this.showNotification({
type: 'success',
message: this.$tc('general.copied_pdf_url_clipboard'),
})
},
async onSearched() {
let data = ''
@ -386,30 +390,45 @@ export default {
return true
},
async onMarkAsSent() {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_sent'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (value) => {
if (value) {
this.isMarkAsSent = true
let response = await this.markAsSent({
id: this.estimate.id,
status: 'SENT',
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('estimates.confirm_mark_as_sent'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
this.isMarkAsSent = true
let response = await this.markAsSent({
id: this.estimate.id,
status: 'SENT',
})
this.isMarkAsSent = false
if (response.data) {
this.estimate.status = 'SENT'
this.showToaster({
type: 'success',
message: this.$tc('estimates.mark_as_sent_successfully'),
})
this.isMarkAsSent = false
if (response.data) {
this.estimate.status = 'SENT'
window.toastr['success'](
this.$tc('estimates.mark_as_sent_successfully')
)
}
}
})
}
})
},
async onSendEstimate(id) {
this.openModal({
@ -423,29 +442,38 @@ export default {
let pdfUrl = `${window.location.origin}/estimates/pdf/${this.estimate.unique_hash}`
let response = this.$utils.copyTextToClipboard(pdfUrl)
window.toastr['success'](this.$tc('general.copied_pdf_url_clipboard'))
this.showNotification({
type: 'success',
message: this.$tc('general.copied_pdf_url_clipboard'),
})
},
async removeEstimate(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: 'you will not be able to recover this estimate!',
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (value) => {
if (value) {
let request = await this.deleteEstimate({ ids: [id] })
if (request.data.success) {
window.toastr['success'](this.$tc('estimates.deleted_message', 1))
this.$router.push('/admin/estimates')
} else if (request.data.error) {
window.toastr['error'](request.data.message)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: 'you will not be able to recover this estimate!',
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let request = await this.deleteEstimate({ ids: [id] })
if (request.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('estimates.deleted_message', 1),
})
this.$router.push('/admin/estimates')
} else if (request.data.error) {
this.showToaster({
type: 'error',
message: request.data.message,
})
}
})
}
})
},
},
}

View File

@ -417,6 +417,8 @@ export default {
...mapActions('customer', ['fetchCustomers']),
...mapActions('notification', ['showNotification']),
openCategoryModal() {
this.openModal({
title: this.$t('settings.expense_category.add_category'),
@ -532,22 +534,34 @@ export default {
if (response.data.success) {
this.isLoading = false
window.toastr['success'](this.$t('expenses.updated_message'))
this.showNotification({
type: 'success',
message: this.$t('expenses.updated_message'),
})
this.$router.push('/admin/expenses')
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
} else {
this.isLoading = true
let response = await this.addExpense(data)
this.isLoading = false
if (response.data.success) {
window.toastr['success'](this.$t('expenses.created_message'))
this.showNotification({
type: 'success',
message: this.$t('expenses.created_message'),
})
this.$router.push('/admin/expenses')
return true
}
window.toastr['success'](response.data.success)
this.showNotification({
type: 'success',
message: response.data.success,
})
}
},
},

View File

@ -3,9 +3,9 @@
<!-- Page Header -->
<sw-page-header :title="$t('expenses.title')">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item to="dashboard" :title="$t('general.home')" />
<sw-breadcrumb-item :title="$t('general.home')" to="dashboard" />
<sw-breadcrumb-item to="#" :title="$tc('expenses.expense', 2)" active />
<sw-breadcrumb-item :title="$tc('expenses.expense', 2)" to="#" active />
</sw-breadcrumb>
<template slot="actions">
@ -208,7 +208,9 @@
>
<template slot-scope="row">
<span>{{ $t('expenses.customer') }}</span>
<span> {{ row.user_name ? row.user_name : $t('expenses.not_selected') }} </span>
<span>
{{ row.user_name ? row.user_name : $t('expenses.not_selected') }}
</span>
</template>
</sw-table-column>
@ -248,8 +250,8 @@
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`expenses/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
@ -374,6 +376,8 @@ export default {
...mapActions('category', ['fetchCategories']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
user_id: this.filters.user ? this.filters.user.id : null,
@ -454,43 +458,61 @@ export default {
},
async removeExpense(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('expenses.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteExpense({ ids: [id] })
if (res.data.success) {
window.toastr['success'](this.$tc('expenses.deleted_message', 1))
this.showNotification({
type: 'success',
message: this.$tc('expenses.deleted_message', 1),
})
this.refreshTable()
return true
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'success',
message: res.data.message,
})
}
}
})
},
async removeMultipleExpenses() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('expenses.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let request = await this.deleteMultipleExpenses()
if (request.data.success) {
window.toastr['success'](this.$tc('expenses.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('expenses.deleted_message', 2),
})
this.$refs.table.refresh()
} else if (request.data.error) {
window.toastr['error'](request.data.message)
this.showNotification({
type: 'error',
message: request.data.message,
})
}
}
})

View File

@ -722,6 +722,8 @@ export default {
...mapActions('customFields', ['fetchCustomFields']),
...mapActions('notification', ['showNotification']),
selectFixed() {
if (this.newInvoice.discount_type === 'fixed') {
return
@ -918,8 +920,10 @@ export default {
.then((res) => {
if (res.data) {
this.$router.push(`/admin/invoices/${res.data.invoice.id}/view`)
window.toastr['success'](this.$t('invoices.created_message'))
this.showNotification({
type: 'success',
message: this.$t('invoices.created_message'),
})
}
this.isLoading = false
@ -935,13 +939,17 @@ export default {
this.isLoading = false
if (res.data.success) {
this.$router.push(`/admin/invoices/${res.data.invoice.id}/view`)
window.toastr['success'](this.$t('invoices.updated_message'))
this.showNotification({
type: 'success',
message: this.$t('invoices.updated_message'),
})
}
if (res.data.error === 'invalid_due_amount') {
window.toastr['error'](
this.$t('invoices.invalid_due_amount_message')
)
this.showNotification({
type: 'error',
message: this.$t('invoices.invalid_due_amount_message'),
})
}
})
.catch((err) => {

View File

@ -2,8 +2,8 @@
<base-page>
<sw-page-header :title="$t('invoices.title')">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item to="dashboard" :title="$t('general.home')" />
<sw-breadcrumb-item to="#" :title="$tc('invoices.invoice', 2)" active />
<sw-breadcrumb-item :title="$t('general.home')" to="dashboard" />
<sw-breadcrumb-item :title="$tc('invoices.invoice', 2)" to="#" active />
</sw-breadcrumb>
<template slot="actions">
@ -93,8 +93,8 @@
<label
class="absolute text-sm leading-snug text-black cursor-pointer"
@click="clearFilter"
style="top: 10px; right: 15px"
@click="clearFilter"
>{{ $t('general.clear_all') }}</label
>
</sw-filter-wrapper>
@ -258,7 +258,9 @@
:bg-color="$utils.getBadgeStatusColor(row.status).bgColor"
:color="$utils.getBadgeStatusColor(row.status).color"
>
{{ $utils.getStatusTranslation(row.paid_status.replace('_', ' ')) }}
{{
$utils.getStatusTranslation(row.paid_status.replace('_', ' '))
}}
</sw-badge>
</template>
</sw-table-column>
@ -289,16 +291,16 @@
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`invoices/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`invoices/${row.id}/view`"
tag-name="router-link"
>
<eye-icon class="h-5 mr-3 text-gray-600" />
{{ $t('invoices.view') }}
@ -334,8 +336,8 @@
row.status === 'VIEWED' ||
row.status === 'OVERDUE'
"
tag-name="router-link"
:to="`/admin/payments/${row.id}/create`"
tag-name="router-link"
>
<credit-card-icon class="h-5 mr-3 text-gray-600" />
{{ $t('payments.record_payment') }}
@ -510,6 +512,8 @@ export default {
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
async sendInvoice(invoice) {
this.openModal({
title: this.$t('invoices.send_invoice'),
@ -521,14 +525,30 @@ export default {
},
async markInvoiceAsSent(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.invoice_mark_as_sent'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
const data = {
id: id,
status: 'SENT',
@ -536,29 +556,49 @@ export default {
let response = await this.markAsSent(data)
this.refreshTable()
if (response.data) {
window.toastr['success'](
this.$tc('invoices.mark_as_sent_successfully')
)
this.showNotification({
type: 'success',
message: this.$tc('invoices.mark_as_sent_successfully'),
})
}
}
})
},
async onCloneInvoice(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.confirm_clone'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.cloneInvoice({ id })
this.refreshTable()
if (response.data) {
window.toastr['success'](this.$tc('invoices.cloned_successfully'))
this.showNotification({
type: 'success',
message: this.$tc('invoices.cloned_successfully'),
})
this.$router.push(
`/admin/invoices/${response.data.invoice.id}/edit`
)
@ -665,31 +705,43 @@ export default {
async removeInvoice(id) {
this.id = id
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('invoices.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteInvoice({ ids: [id] })
if (res.data.success) {
window.toastr['success'](this.$tc('invoices.deleted_message'))
this.showNotification({
type: 'success',
message: this.$tc('invoices.deleted_message'),
})
this.$refs.table.refresh()
return true
}
if (res.data.error === 'payment_attached') {
window.toastr['error'](
this.$t('invoices.payment_attached_message'),
this.$t('general.action_failed')
)
this.showNotification({
type: 'error',
message:
(this.$t('invoices.payment_attached_message'),
this.$t('general.action_failed')),
})
return true
}
window.toastr['error'](res.data.error)
this.showNotification({
type: 'error',
message: res.data.error,
})
return true
}
this.resetSelectedInvoices()
@ -697,30 +749,41 @@ export default {
},
async removeMultipleInvoices() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('invoices.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteMultipleInvoices()
if (res.data.error === 'payment_attached') {
window.toastr['error'](
this.$t('invoices.payment_attached_message'),
this.$t('general.action_failed')
)
this.showNotification({
type: 'error',
message:
(this.$t('invoices.payment_attached_message'),
this.$t('general.action_failed')),
})
return true
}
if (res.data) {
this.$refs.table.refresh()
this.resetSelectedInvoices()
window.toastr['success'](this.$tc('invoices.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('invoices.deleted_message', 2),
})
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})

View File

@ -323,6 +323,7 @@ export default {
]),
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
hasActiveUrl(id) {
return this.$route.params.id == id
@ -393,30 +394,45 @@ export default {
return true
},
async onMarkAsSent() {
window
.swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.invoice_mark_as_sent'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (value) => {
if (value) {
this.isMarkingAsSent = true
let response = await this.markAsSent({
id: this.invoice.id,
status: 'SENT',
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('invoices.invoice_mark_as_sent'),
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
this.isMarkingAsSent = true
let response = await this.markAsSent({
id: this.invoice.id,
status: 'SENT',
})
this.isMarkingAsSent = false
if (response.data) {
this.invoice.status = 'SENT'
this.showToaster({
type: 'success',
message: this.$tc('invoices.marked_as_sent_message'),
})
this.isMarkingAsSent = false
if (response.data) {
this.invoice.status = 'SENT'
window.toastr['success'](
this.$tc('invoices.marked_as_sent_message')
)
}
}
})
}
})
},
async onSendInvoice() {
this.openModal({
@ -430,29 +446,38 @@ export default {
let pdfUrl = `${window.location.origin}/invoices/pdf/${this.invoice.unique_hash}`
let response = this.$utils.copyTextToClipboard(pdfUrl)
window.toastr['success'](this.$t('general.copied_pdf_url_clipboard'))
this.showNotification({
type: 'success',
message: this.$t('general.copied_pdf_url_clipboard'),
})
},
async removeInvoice(id) {
window
.swal({
title: this.$t('general.are_you_sure'),
text: 'you will not be able to recover this invoice!',
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (value) => {
if (value) {
let request = await this.deleteInvoice({ ids: [id] })
if (request.data.success) {
window.toastr['success'](this.$tc('invoices.deleted_message', 1))
this.$router.push('/admin/invoices')
} else if (request.data.error) {
window.toastr['error'](request.data.message)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: 'you will not be able to recover this invoice!',
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let request = await this.deleteInvoice({ ids: [id] })
if (request.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('invoices.deleted_message', 1),
})
this.$router.push('/admin/invoices')
} else if (request.data.error) {
this.showToaster({
type: 'error',
message: request.data.message,
})
}
})
}
})
},
},
}

View File

@ -312,6 +312,8 @@ export default {
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
async setTaxPerItem() {
let response = await this.fetchCompanySettings(['tax_per_item'])
@ -385,15 +387,24 @@ export default {
this.isLoading = false
if (!this.isEdit) {
window.toastr['success'](this.$tc('items.created_message'))
this.showNotification({
type: 'success',
message: this.$tc('items.created_message'),
})
this.$router.push('/admin/items')
return true
} else {
window.toastr['success'](this.$tc('items.updated_message'))
this.showNotification({
type: 'success',
message: this.$tc('items.updated_message'),
})
this.$router.push('/admin/items')
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
}
},

View File

@ -348,6 +348,8 @@ export default {
'fetchItemUnits',
]),
...mapActions('notification', ['showNotification']),
refreshTable() {
this.$refs.table.refresh()
},
@ -397,52 +399,71 @@ export default {
async removeItems(id) {
this.id = id
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('items.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteItem({ ids: [id] })
if (res.data.success) {
window.toastr['success'](this.$tc('items.deleted_message', 1))
this.showNotification({
type: 'success',
message: this.$tc('items.deleted_message'),
})
this.$refs.table.refresh()
return true
}
if (res.data.error === 'item_attached') {
window.toastr['error'](
this.$tc('items.item_attached_message'),
this.$t('general.action_failed')
)
this.showNotification({
type: 'error',
message:
(this.$tc('items.item_attached_message'),
this.$t('general.action_failed')),
})
return true
}
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
return true
}
})
},
async removeMultipleItems() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('items.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteMultipleItems()
if (res.data.success || res.data.items) {
window.toastr['success'](this.$tc('items.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('items.deleted_message', 2),
})
this.$refs.table.refresh()
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})

View File

@ -1,6 +1,7 @@
<template>
<div v-if="isAppLoaded" class="h-full">
<base-modal />
<base-notification />
<site-header />
<div class="flex h-screen pt-16 pb-10 overflow-hidden">
<site-sidebar />
@ -20,6 +21,7 @@ import SiteSidebar from './partials/TheSiteSidebar.vue'
import BaseModal from '../../components/base/modal/BaseModal'
import { RefreshIcon } from '@vue-hero-icons/solid'
import { mapActions, mapGetters } from 'vuex'
import BaseNotification from '../../components/base/BaseNotification.vue'
export default {
components: {
@ -28,6 +30,7 @@ export default {
SiteFooter,
BaseModal,
RefreshIcon,
BaseNotification,
},
computed: {

View File

@ -1,5 +1,6 @@
<template>
<div class="grid h-full grid-cols-12 overflow-y-hidden bg-gray-100">
<base-notification />
<div
class="flex items-center justify-center w-full max-w-sm col-span-12 p-4 mx-auto text-gray-900 md:p-8 md:col-span-6 lg:col-span-4 flex-2 md:pb-48 md:pt-40"
>

View File

@ -423,6 +423,8 @@ export default {
...mapActions('customer', ['fetchCustomers']),
...mapActions('notification', ['showNotification']),
invoiceWithAmount({ invoice_number, due_amount }) {
return `${invoice_number} (${this.$utils.formatGraphMoney(
due_amount,
@ -561,25 +563,38 @@ export default {
this.$router.push(
`/admin/payments/${response.data.payment.id}/view`
)
window.toastr['success'](this.$t('payments.updated_message'))
this.showNotification({
type: 'success',
message: this.$t('payments.updated_message'),
})
return true
}
if (response.data.error === 'invalid_amount') {
window.toastr['error'](this.$t('invalid_amount_message'))
this.showNotification({
type: 'error',
message: this.$t('invalid_amount_message'),
})
return false
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
} catch (err) {
this.isLoading = false
if (err.response.data.errors.payment_number) {
window.toastr['error'](err.response.data.errors.payment_number)
this.showNotification({
type: 'error',
message: err.response.data.errors.payment_number,
})
return true
}
window.toastr['error'](err.response.data.message)
this.showNotification({
type: 'error',
message: err.response.data.message,
})
}
} else {
let data = {
@ -599,26 +614,39 @@ export default {
this.$router.push(
`/admin/payments/${response.data.payment.id}/view`
)
window.toastr['success'](this.$t('payments.created_message'))
this.showNotification({
type: 'success',
message: this.$t('payments.created_message'),
})
this.isLoading = true
return true
}
if (response.data.error === 'invalid_amount') {
window.toastr['error'](this.$t('invalid_amount_message'))
this.showNotification({
type: 'error',
message: this.$t('invalid_amount_message'),
})
return false
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
} catch (err) {
this.isLoading = false
if (err.response.data.errors.payment_number) {
window.toastr['error'](err.response.data.errors.payment_number)
this.showNotification({
type: 'error',
message: err.response.data.errors.payment_number,
})
return true
}
window.toastr['error'](err.response.data.message)
this.showNotification({
type: 'error',
message: err.response.data.message,
})
}
}
},

View File

@ -2,8 +2,8 @@
<base-page class="payments">
<sw-page-header :title="$t('payments.title')">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item to="dashboard" :title="$t('general.home')" />
<sw-breadcrumb-item to="#" :title="$tc('payments.payment', 2)" active />
<sw-breadcrumb-item :title="$t('general.home')" to="dashboard" />
<sw-breadcrumb-item :title="$tc('payments.payment', 2)" to="#" active />
</sw-breadcrumb>
<template slot="actions">
@ -202,7 +202,11 @@
<template slot-scope="row">
<span>{{ $t('payments.payment_mode') }}</span>
<span>
{{ row.payment_mode ? row.payment_mode : $t('payments.not_selected') }}
{{
row.payment_mode
? row.payment_mode
: $t('payments.not_selected')
}}
</span>
</template>
</sw-table-column>
@ -216,7 +220,11 @@
<template slot-scope="row">
<span>{{ $t('invoices.invoice_number') }}</span>
<span>
{{ row.invoice_number ? row.invoice_number : $t('payments.no_invoice') }}
{{
row.invoice_number
? row.invoice_number
: $t('payments.no_invoice')
}}
</span>
</template>
</sw-table-column>
@ -239,16 +247,16 @@
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`payments/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
</sw-dropdown-item>
<sw-dropdown-item
tag-name="router-link"
:to="`payments/${row.id}/view`"
tag-name="router-link"
>
<eye-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.view') }}
@ -371,6 +379,8 @@ export default {
'fetchPaymentModes',
]),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
customer_id: this.filters.customer ? this.filters.customer.id : '',
@ -433,43 +443,60 @@ export default {
},
async removePayment(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('payments.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deletePayment({ ids: [id] })
if (res.data.success) {
window.toastr['success'](this.$tc('payments.deleted_message', 1))
this.showNotification({
type: 'success',
message: this.$tc('payments.deleted_message', 1),
})
this.$refs.table.refresh()
return true
}
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
return true
}
})
},
async removeMultiplePayments() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('payments.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let request = await this.deleteMultiplePayments()
if (request.data.success) {
window.toastr['success'](this.$tc('payments.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('payments.deleted_message', 2),
})
this.$refs.table.refresh()
} else if (request.data.error) {
window.toastr['error'](request.data.message)
this.showNotification({
type: 'error',
message: request.data.message,
})
}
}
})

View File

@ -282,6 +282,7 @@ export default {
'searchPayment',
]),
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
hasActiveUrl(id) {
return this.$route.params.id == id
@ -364,30 +365,39 @@ export default {
let pdfUrl = `${window.location.origin}/payments/pdf/${this.payment.unique_hash}`
let response = this.$utils.copyTextToClipboard(pdfUrl)
window.toastr['success'](this.$t('general.copied_pdf_url_clipboard'))
this.showNotification({
type: 'success',
message: this.$t('general.copied_pdf_url_clipboard'),
})
},
async removePayment(id) {
this.id = id
window
.swal({
title: this.$t('general.are_you_sure'),
text: 'you will not be able to recover this payment!',
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
})
.then(async (value) => {
if (value) {
let request = await this.deletePayment({ ids: [id] })
if (request.data.success) {
window.toastr['success'](this.$tc('payments.deleted_message', 1))
this.$router.push('/admin/payments')
} else if (request.data.error) {
window.toastr['error'](request.data.message)
}
this.$swal({
title: this.$t('general.are_you_sure'),
text: 'you will not be able to recover this payment!',
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let request = await this.deletePayment({ ids: [id] })
if (request.data.success) {
this.showToaster({
type: 'success',
message: this.$tc('payments.deleted_message', 1),
})
this.$router.push('/admin/payments')
} else if (request.data.error) {
this.showToaster({
type: 'error',
message: request.data.message,
})
}
})
}
})
},
},
}

View File

@ -58,8 +58,8 @@
:options="reportTypes"
:allow-empty="false"
:show-labels="false"
class="mt-2"
:placeholder="$t('reports.sales.report_type')"
class="mt-2"
@input="getInitialReport"
/>
</sw-input-group>
@ -79,7 +79,7 @@
class="hidden w-full h-screen border-gray-100 border-solid rounded md:flex"
/>
<a
class="flex items-center justify-center h-10 px-5 py-1 text-sm font-medium leading-none text-center text-white whitespace-nowrap rounded md:hidden bg-primary-500"
class="flex items-center justify-center h-10 px-5 py-1 text-sm font-medium leading-none text-center text-white rounded whitespace-nowrap md:hidden bg-primary-500"
@click="viewReportsPDF"
>
<document-text-icon />

View File

@ -33,18 +33,18 @@
:show-labels="false"
:placeholder="$t('settings.disk.select_disk')"
:allow-empty="false"
:custom-label="getCustomLabel"
track-by="id"
label="name"
:custom-label="getCustomLabel"
@select="refreshTable"
/>
</sw-input-group>
</div>
<sw-table-component
ref="table"
variant="gray"
:show-filter="false"
:data="fetchBackupsData"
variant="gray"
>
<sw-table-column :label="$t('settings.backup.path')" show="path">
<template slot-scope="row">
@ -119,6 +119,8 @@ export default {
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
getCustomLabel({ driver, name }) {
if (!name) {
return
@ -127,14 +129,17 @@ export default {
},
async onRemoveBackup(backup) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.backup.backup_confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'question',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let data = {
disk: this.filters.selected_disk.driver,
file_disk_id: this.filters.selected_disk.id,
@ -142,7 +147,10 @@ export default {
}
let response = await this.removeBackup(data)
if (response.data.success) {
window.toastr['success'](this.$t('settings.backup.deleted_message'))
this.showNotification({
type: 'success',
message: this.$t('settings.backup.deleted_message'),
})
this.$refs.table.refresh()
return true
}
@ -170,9 +178,10 @@ export default {
let response = await this.fetchBackups(data)
if (response.data.error) {
window.toastr['error'](
this.$t('settings.backup.' + response.data.error)
)
this.showNotification({
type: 'error',
message: this.$t('settings.backup.' + response.data.error),
})
}
this.isRequestOngoing = false
@ -225,7 +234,10 @@ export default {
})
.catch((e) => {
this.isRequestOngoing = false
window.toastr['error'](e.response.data.message)
this.showNotification({
type: 'error',
message: e.response.data.message,
})
})
},
},

View File

@ -1,5 +1,5 @@
<template>
<form @submit.prevent="updateCompanyData" class="relative h-full">
<form class="relative h-full" @submit.prevent="updateCompanyData">
<base-loader v-if="isRequestOnGoing" :show-bg-overlay="true" />
<sw-card variant="setting-card">
<template slot="header">
@ -41,8 +41,8 @@
</div>
<sw-avatar
trigger="#logo-box"
:preview-avatar="previewLogo"
trigger="#logo-box"
@changed="onChange"
@uploadHandler="onUploadHandler"
@handleUploadError="onHandleUploadError"
@ -74,8 +74,8 @@
<sw-input-group :label="$tc('settings.company_info.phone')">
<sw-input
v-model="formData.phone"
class="mt-2"
:placeholder="$t('settings.company_info.phone')"
class="mt-2"
/>
</sw-input-group>
@ -153,9 +153,9 @@
</div>
<sw-button
class="mt-4"
:loading="isLoading"
:disabled="isLoading"
class="mt-4"
variant="primary"
>
<save-icon v-if="!isLoading" class="mr-2 -ml-1" />
@ -269,13 +269,17 @@ export default {
methods: {
...mapActions('company', ['updateCompany', 'updateCompanyLogo']),
...mapActions('user', ['fetchCurrentUser']),
...mapActions('notification', ['showNotification']),
onUploadHandler(cropper) {
this.previewLogo = cropper
.getCroppedCanvas()
.toDataURL(this.cropperOutputMime)
},
onHandleUploadError() {
window.toastr['error']('Oops! Something went wrong...')
this.showNotification({
type: 'error',
message: 'Oops! Something went wrong...',
})
},
onChange(file) {
this.cropperOutputMime = file.type
@ -322,13 +326,17 @@ export default {
await this.updateCompanyLogo(logoData)
}
this.isLoading = false
window.toastr['success'](
this.$t('settings.company_info.updated_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.company_info.updated_message'),
})
return true
}
this.isLoading = false
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
return true
},
},

View File

@ -23,9 +23,9 @@
<sw-table-component
ref="table"
variant="gray"
:show-filter="false"
:data="fetchData"
variant="gray"
>
<sw-table-column
:sortable="true"
@ -118,6 +118,8 @@ export default {
...mapActions('modal', ['openModal']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -138,26 +140,31 @@ export default {
},
async removeCustomField(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.custom_fields.custom_field_confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deleteCustomFields(id)
if (response.data.success) {
window.toastr['success'](
this.$t('settings.custom_fields.deleted_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.custom_fields.deleted_message'),
})
this.id = null
this.$refs.table.refresh()
return true
}
window.toastr['error'](
this.$t('settings.custom_fields.already_in_use')
)
this.showNotification({
type: 'error',
message: this.$t('settings.custom_fields.already_in_use'),
})
}
})
},

View File

@ -110,6 +110,8 @@ export default {
'deleteCategory',
]),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -132,26 +134,31 @@ export default {
},
async removeExpenseCategory(id, index) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.expense_category.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'question',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>s`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deleteCategory(id)
if (response.data.success) {
window.toastr['success'](
this.$tc('settings.expense_category.deleted_message')
)
this.showNotification({
type: 'success',
message: this.$tc('settings.expense_category.deleted_message'),
})
this.id = null
this.$refs.table.refresh()
return true
}
window.toastr['error'](
this.$t('settings.expense_category.already_in_use')
)
this.showNotification({
type: 'error',
message: this.$t('settings.expense_category.already_in_use'),
})
}
})
},

View File

@ -27,9 +27,9 @@
<sw-table-component
ref="table"
variant="gray"
:show-filter="false"
:data="fetchData"
variant="gray"
table-class="table tax-table"
class="mt-0 mb-3"
>
@ -173,6 +173,8 @@ export default {
...mapActions('company', ['updateCompanySettings', 'fetchCompanySettings']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -243,20 +245,39 @@ export default {
let response = await this.updateCompanySettings(data)
if (response.data.success) {
window.toastr['success'](this.$t('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$t('general.setting_updated'),
})
}
this.$refs.table.refresh()
},
async setDefaultDiskData(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.disk.set_default_disk_confirm'),
icon: '/assets/icon/check-circle-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'question',
iconHtml: `<svg
aria-hidden="true"
class="w-6 h-6"
focusable="false"
data-prefix="fas"
data-icon="check-circle"
class="svg-inline--fa fa-check-circle fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="#55547A"
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
></path>
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
this.loading = true
let data = {
set_as_default: true,
@ -266,26 +287,33 @@ export default {
if (response.data.success) {
this.refreshTable()
window.toastr['success'](
this.$t('settings.disk.success_set_default_disk')
)
this.showNotification({
type: 'success',
message: this.$t('settings.disk.success_set_default_disk'),
})
}
}
})
},
async removeDisk(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.disk.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deleteFileDisk(id)
if (response.data.success) {
window.toastr['success'](this.$t('settings.disk.deleted_message'))
this.showNotification({
type: 'success',
message: this.$t('settings.disk.deleted_message'),
})
this.refreshTable()
return true
}

View File

@ -76,6 +76,7 @@ export default {
'fetchMailConfig',
'updateMailConfig',
]),
...mapActions('notification', ['showNotification']),
async loadData() {
this.isRequestOnGoing = true
@ -100,17 +101,22 @@ export default {
let response = await this.updateMailConfig(mailConfigData)
if (response.data.success) {
this.isLoading = false
window.toastr['success'](
this.$t('wizard.success.' + response.data.success)
)
this.showNotification({
type: 'success',
message: this.$t('wizard.success.' + response.data.success),
})
} else {
window.toastr['error'](
this.$t('wizard.errors.' + response.data.error)
)
this.showNotification({
type: 'error',
message: this.$t('wizard.errors.' + response.data.error),
})
}
return true
} catch (e) {
window.toastr['error']('Something went wrong')
this.showNotification({
type: 'error',
message: 'Something went wrong',
})
}
},

View File

@ -27,9 +27,9 @@
<sw-table-component
ref="table"
variant="gray"
:show-filter="false"
:data="fetchData"
variant="gray"
>
<sw-table-column
:label="$t('settings.customization.notes.name')"
@ -93,6 +93,8 @@ export default {
...mapActions('notes', ['fetchNotes', 'deleteNote']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -112,26 +114,31 @@ export default {
}
},
removeNote(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.customization.notes.note_confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deleteNote(id)
if (response.data.success) {
window.toastr['success'](
this.$t('settings.customization.notes.deleted_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.customization.notes.deleted_message'),
})
this.$refs.table.refresh()
return true
}
window.toastr['error'](
this.$t('settings.customization.notes.already_in_use')
)
this.showNotification({
type: 'error',
message: this.$t('settings.customization.notes.already_in_use'),
})
}
})
},

View File

@ -133,6 +133,7 @@ export default {
},
methods: {
...mapActions('company', ['fetchCompanySettings', 'updateCompanySettings']),
...mapActions('notification', ['showNotification']),
async fetchData() {
this.isRequestOnGoing = true
@ -176,9 +177,10 @@ export default {
if (response.data.success) {
this.isLoading = false
window.toastr['success'](
this.$tc('settings.notification.email_save_message')
)
this.showNotification({
type: 'success',
message: this.$tc('settings.notification.email_save_message'),
})
}
},
@ -199,7 +201,10 @@ export default {
let response = await this.updateCompanySettings(data)
if (response.data.success) {
window.toastr['success'](this.$tc('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$tc('general.setting_updated'),
})
}
},
@ -220,7 +225,10 @@ export default {
let response = await this.updateCompanySettings(data)
if (response.data) {
window.toastr['success'](this.$tc('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$tc('general.setting_updated'),
})
}
},
},

View File

@ -22,9 +22,9 @@
<sw-table-component
ref="table"
variant="gray"
:show-filter="false"
:data="fetchData"
variant="gray"
>
<sw-table-column
:label="$t('settings.customization.payments.mode_name')"
@ -79,6 +79,8 @@ export default {
...mapActions('payment', ['deletePaymentMode', 'fetchPaymentModes']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -117,29 +119,36 @@ export default {
},
removePaymentMode(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t(
'settings.customization.payments.payment_mode_confirm_delete'
),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deletePaymentMode(id)
if (response.data.success) {
window.toastr['success'](
this.$t('settings.customization.payments.deleted_message')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.payments.deleted_message'
),
})
this.id = null
this.$refs.table.refresh()
return true
}
window.toastr['error'](
this.$t('settings.customization.payments.already_in_use')
)
this.showNotification({
type: 'error',
message: this.$t('settings.customization.payments.already_in_use'),
})
}
})
},

View File

@ -1,5 +1,5 @@
<template>
<form action="" @submit.prevent="updatePreferencesData" class="relative">
<form action="" class="relative" @submit.prevent="updatePreferencesData">
<base-loader v-if="isRequestOnGoing" :show-bg-overlay="true" />
<sw-card variant="setting-card">
<template slot="header">
@ -113,11 +113,11 @@
</div>
<sw-button
:disabled="isLoading"
:loading="isLoading"
class="mt-6"
variant="primary"
type="submit"
:disabled="isLoading"
:loading="isLoading"
>
<save-icon v-if="!isLoading" class="mr-2 -ml-1" />
{{ $tc('settings.company_info.save') }}
@ -267,6 +267,8 @@ export default {
'fetchTimeZones',
]),
...mapActions('notification', ['showNotification']),
currencyNameWithCode({ name, code }) {
return `${code} - ${name}`
},
@ -340,12 +342,16 @@ export default {
this.isLoading = false
// window.i18n.locale = this.formData.language.code
this.setDefaultCurrency(this.formData.currency)
window.toastr['success'](
this.$t('settings.preferences.updated_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.preferences.updated_message'),
})
return true
}
window.toastr['error'](response.data.error)
this.showNotification({
type: 'error',
message: response.data.error,
})
return true
},
@ -357,7 +363,10 @@ export default {
}
let response = await this.updateCompanySettings(data)
if (response.data.success) {
window.toastr['success'](this.$t('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$t('general.setting_updated'),
})
}
},
},

View File

@ -160,6 +160,8 @@ export default {
]),
...mapActions('company', ['fetchCompanySettings', 'updateCompanySettings']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -194,28 +196,38 @@ export default {
}
let response = await this.updateCompanySettings(data)
if (response.data) {
window.toastr['success'](this.$t('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$t('general.setting_updated'),
})
}
},
async removeTax(id, index) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.tax_types.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deleteTaxType(id)
if (response.data.success) {
window.toastr['success'](
this.$t('settings.tax_types.deleted_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.tax_types.deleted_message'),
})
this.$refs.table.refresh()
return true
}
window.toastr['error'](this.$t('settings.tax_types.already_in_use'))
this.showNotification({
type: 'error',
message: this.$t('settings.tax_types.already_in_use'),
})
}
})
},

View File

@ -58,9 +58,9 @@
</label>
<table class="w-1/2 mt-2 border-2 border-gray-200 table-fixed">
<tr
class="p-2 border-2 border-gray-200"
v-for="(ext, i) in requiredExtentions"
:key="i"
class="p-2 border-2 border-gray-200"
>
<td width="70%" class="p-2 text-sm truncate">
{{ i }}
@ -106,8 +106,8 @@
<!-- -->
<ul v-if="isUpdating" class="w-full p-0 list-none">
<li
class="flex justify-between w-full py-3 border-b border-gray-200 border-solid last:border-b-0"
v-for="step in updateSteps"
class="flex justify-between w-full py-3 border-b border-gray-200 border-solid last:border-b-0"
>
<p class="m-0 text-sm leading-8">{{ $t(step.translationKey) }}</p>
<div class="flex flex-row items-center">
@ -115,8 +115,8 @@
{{ step.time }}
</span>
<span
class="block py-1 text-sm text-center uppercase rounded-full"
:class="statusClass(step)"
class="block py-1 text-sm text-center uppercase rounded-full"
style="width: 88px"
>
{{ getStatus(step) }}
@ -130,7 +130,7 @@
<script>
import LoadingIcon from '../../components/icon/LoadingIcon'
import { mapActions } from 'vuex'
export default {
components: {
LoadingIcon,
@ -228,6 +228,8 @@ export default {
},
methods: {
...mapActions('notification', ['showNotification']),
getStatus(step) {
if (step.started && step.completed) {
return 'finished'
@ -266,7 +268,11 @@ export default {
this.isCheckingforUpdate = false
if (!response.data.version) {
window.toastr['info'](this.$t('settings.update_app.latest_message'))
this.showNotification({
title: 'Info!',
type: 'info',
message: this.$t('settings.update_app.latest_message'),
})
return
}
@ -283,16 +289,21 @@ export default {
} catch (e) {
this.isUpdateAvailable = false
this.isCheckingforUpdate = false
window.toastr['error']('Something went wrong')
this.showNotification({
type: 'error',
message: 'Something went wrong',
})
}
},
async onUpdateApp() {
let path = null
if (!this.allowToUpdate) {
window.toastr['error'](
'Your current configuration does not match the update requirements. Please try again after all the requirements are fulfilled. '
)
this.showNotification({
type: 'error',
message:
'Your current configuration does not match the update requirements. Please try again after all the requirements are fulfilled.',
})
return true
}
for (let index = 0; index < this.updateSteps.length; index++) {
@ -321,9 +332,10 @@ export default {
currentStep.translationKey == 'settings.update_app.finishing_update'
) {
this.isUpdating = false
window.toastr['success'](
this.$t('settings.update_app.update_success')
)
this.showNotification({
type: 'success',
message: this.$t('settings.update_app.update_success'),
})
setTimeout(() => {
location.reload()
@ -332,7 +344,10 @@ export default {
} catch (error) {
currentStep.started = false
currentStep.completed = true
window.toastr['error'](this.$t('validation.something_went_wrong'))
this.showNotification({
type: 'error',
message: this.$t('validation.something_went_wrong'),
})
this.onUpdateFailed(currentStep.translationKey)
return false
}
@ -341,7 +356,7 @@ export default {
onUpdateFailed(translationKey) {
let stepName = this.$t(translationKey)
swal({
this.$swal({
title: this.$t('settings.update_app.update_failed'),
text: this.$tc('settings.update_app.update_failed_text', stepName, {
step: stepName,

View File

@ -264,6 +264,8 @@ export default {
...mapActions(['fetchLanguages']),
...mapActions('notification', ['showNotification']),
onUploadHandler(cropper) {
this.previewAvatar = cropper
.getCroppedCanvas()
@ -271,7 +273,10 @@ export default {
},
onHandleUploadError() {
window.toastr['error']('Oops! Something went wrong...')
this.showNotification({
type: 'error',
message: 'Oops! Something went wrong...',
})
},
onChange(file) {
@ -348,10 +353,10 @@ export default {
this.uploadAvatar(avatarData)
}
window.toastr['success'](
this.$t('settings.account_settings.updated_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.account_settings.updated_message'),
})
this.formData.password = ''
this.formData.confirm_password = ''

View File

@ -56,6 +56,7 @@
</transition>
</template>
<script>
import { mapActions } from 'vuex'
export default {
data() {
return {
@ -134,13 +135,17 @@ export default {
},
methods: {
...mapActions('notification', ['showNotification']),
async updateAddressSetting() {
let data = { type: 'ADDRESSES', ...this.addresses, large: true }
// if (this.updateSetting(data)) {
window.toastr['success'](
this.$t('settings.customization.addresses.address_setting_updated')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.addresses.address_setting_updated'
),
})
// }
},
},

View File

@ -107,9 +107,7 @@
</div>
<div class="ml-4">
<p class="p-0 mb-1 text-base leading-snug text-black">
{{
$t('settings.customization.estimates.estimate_email_attachment')
}}
{{ $t('settings.customization.estimates.estimate_email_attachment') }}
</p>
<p
@ -117,7 +115,9 @@
style="max-width: 480px"
>
{{
$t('settings.customization.estimates.estimate_email_attachment_setting_description')
$t(
'settings.customization.estimates.estimate_email_attachment_setting_description'
)
}}
</p>
</div>
@ -126,7 +126,7 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { mapActions } from 'vuex'
const { required, maxLength, alpha } = require('vuelidate/lib/validators')
export default {
@ -244,7 +244,7 @@ export default {
methods: {
...mapActions('company', ['updateCompanySettings']),
...mapActions('notification', ['showNotification']),
async setEstimateSetting() {
let data = {
settings: {
@ -254,7 +254,10 @@ export default {
}
let response = await this.updateCompanySettings(data)
if (response.data) {
window.toastr['success'](this.$t('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$t('general.setting_updated'),
})
}
},
@ -286,9 +289,12 @@ export default {
}
if (this.updateSetting(data)) {
window.toastr['success'](
this.$t('settings.customization.estimates.estimate_setting_updated')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.estimates.estimate_setting_updated'
),
})
}
},

View File

@ -113,9 +113,7 @@
<div class="ml-4">
<p class="p-0 mb-1 text-base leading-snug text-black">
{{
$t('settings.customization.invoices.invoice_email_attachment')
}}
{{ $t('settings.customization.invoices.invoice_email_attachment') }}
</p>
<p
@ -123,7 +121,9 @@
style="max-width: 480px"
>
{{
$t('settings.customization.invoices.invoice_email_attachment_setting_description')
$t(
'settings.customization.invoices.invoice_email_attachment_setting_description'
)
}}
</p>
</div>
@ -240,6 +240,7 @@ export default {
methods: {
...mapActions('company', ['updateCompanySettings']),
...mapActions('notification', ['showNotification']),
async setInvoiceSetting() {
let data = {
@ -252,7 +253,10 @@ export default {
let response = await this.updateCompanySettings(data)
if (response.data) {
window.toastr['success'](this.$t('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$t('general.setting_updated'),
})
}
},
@ -283,9 +287,12 @@ export default {
}
if (this.updateSetting(data)) {
window.toastr['success'](
this.$t('settings.customization.invoices.invoice_setting_updated')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.invoices.invoice_setting_updated'
),
})
}
},

View File

@ -9,9 +9,9 @@
<sw-table-component
ref="table"
variant="gray"
:data="fetchData"
:show-filter="false"
variant="gray"
>
<sw-table-column
:sortable="true"
@ -66,6 +66,8 @@ export default {
...mapActions('item', ['deleteItemUnit', 'fetchItemUnits']),
...mapActions('notification', ['showNotification']),
async fetchData({ page, filter, sort }) {
let data = {
orderByField: sort.fieldName || 'created_at',
@ -104,26 +106,31 @@ export default {
},
async removeItemUnit(id) {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$t('settings.customization.items.item_unit_confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (value) => {
if (value) {
icon: 'question',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let response = await this.deleteItemUnit(id)
if (response.data.success) {
window.toastr['success'](
this.$t('settings.customization.items.deleted_message')
)
this.showNotification({
type: 'success',
message: this.$t('settings.customization.items.deleted_message'),
})
this.$refs.table.refresh()
return true
}
window.toastr['error'](
this.$t('settings.customization.items.already_in_use')
)
this.showNotification({
type: 'error',
message: this.$t('settings.customization.items.already_in_use'),
})
}
})
},

View File

@ -102,9 +102,7 @@
<div class="ml-4">
<p class="p-0 mb-1 text-base leading-snug text-black">
{{
$t('settings.customization.payments.payment_email_attachment')
}}
{{ $t('settings.customization.payments.payment_email_attachment') }}
</p>
<p
@ -112,7 +110,9 @@
style="max-width: 480px"
>
{{
$t('settings.customization.payments.payment_email_attachment_setting_description')
$t(
'settings.customization.payments.payment_email_attachment_setting_description'
)
}}
</p>
</div>
@ -228,6 +228,8 @@ export default {
...mapActions('company', ['updateCompanySettings']),
...mapActions('notification', ['showNotification']),
changeToUppercase(currentTab) {
if (currentTab === 'PAYMENTS') {
this.payments.payment_prefix = this.payments.payment_prefix.toUpperCase()
@ -244,7 +246,10 @@ export default {
}
let response = await this.updateCompanySettings(data)
if (response.data) {
window.toastr['success'](this.$t('general.setting_updated'))
this.showNotification({
type: 'success',
message: this.$t('general.setting_updated'),
})
}
},
@ -266,9 +271,12 @@ export default {
}
if (this.updateSetting(data)) {
window.toastr['success'](
this.$t('settings.customization.payments.payment_setting_updated')
)
this.showNotification({
type: 'success',
message: this.$t(
'settings.customization.payments.payment_setting_updated'
),
})
}
},

View File

@ -1,19 +1,19 @@
<template>
<base-page v-if="isSuperAdmin" class="item-create">
<sw-page-header class="mb-3" :title="pageTitle">
<sw-page-header :title="pageTitle" class="mb-3">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item to="/admin/dashboard" :title="$t('general.home')" />
<sw-breadcrumb-item to="/admin/users" :title="$tc('users.user', 2)" />
<sw-breadcrumb-item :title="$t('general.home')" to="/admin/dashboard" />
<sw-breadcrumb-item :title="$tc('users.user', 2)" to="/admin/users" />
<sw-breadcrumb-item
v-if="$route.name === 'users.edit'"
to="#"
:title="$t('users.edit_user')"
to="#"
active
/>
<sw-breadcrumb-item
v-else
to="#"
:title="$t('users.new_user')"
to="#"
active
/>
</sw-breadcrumb>
@ -43,8 +43,8 @@
<sw-input-group
:label="$t('users.email')"
class="mt-4"
:error="emailError"
class="mt-4"
required
>
<sw-input
@ -229,6 +229,8 @@ export default {
methods: {
...mapActions('users', ['addUser', 'fetchUser', 'updateUser']),
...mapActions('notification', ['showNotification']),
async loadEditData() {
let response = await this.fetchUser(this.$route.params.id)
@ -251,12 +253,18 @@ export default {
response = await this.updateUser(this.formData)
let data
if (response.data.success) {
window.toastr['success'](this.$tc('users.updated_message'))
this.showNotification({
type: 'success',
message: this.$tc('users.updated_message'),
})
this.$router.push('/admin/users')
this.isLoading = false
}
if (response.data.error) {
window.toastr['error'](this.$t('validation.email_already_taken'))
this.showNotification({
type: 'error',
message: this.$t('validation.email_already_taken'),
})
}
} else {
response = await this.addUser(this.formData)
@ -264,7 +272,10 @@ export default {
if (response.data.success) {
this.isLoading = false
if (!this.isEdit) {
window.toastr['success'](this.$tc('users.created_message'))
this.showNotification({
type: 'success',
message: this.$tc('users.created_message'),
})
this.$router.push('/admin/users')
return true
}

View File

@ -2,8 +2,8 @@
<base-page v-if="isSuperAdmin" class="items">
<sw-page-header :title="$t('users.title')">
<sw-breadcrumb slot="breadcrumbs">
<sw-breadcrumb-item to="dashboard" :title="$t('general.home')" />
<sw-breadcrumb-item to="#" :title="$tc('users.title', 2)" active />
<sw-breadcrumb-item :title="$t('general.home')" to="dashboard" />
<sw-breadcrumb-item :title="$tc('users.title', 2)" to="#" active />
</sw-breadcrumb>
<template slot="actions">
@ -91,7 +91,7 @@
</sw-button>
</sw-empty-table-placeholder>
<div class="relative table-container" v-show="!showEmptyScreen">
<div v-show="!showEmptyScreen" class="relative table-container">
<div
class="relative flex items-center justify-between h-10 mt-5 list-none border-b-2 border-gray-200 border-solid"
>
@ -209,8 +209,8 @@
<dot-icon slot="activator" />
<sw-dropdown-item
tag-name="router-link"
:to="`users/${row.id}/edit`"
tag-name="router-link"
>
<pencil-icon class="h-5 mr-3 text-gray-600" />
{{ $t('general.edit') }}
@ -302,17 +302,17 @@ export default {
},
},
},
created() {
if (!this.isSuperAdmin) {
this.$router.push('/admin/dashboard')
}
},
watch: {
filters: {
handler: 'setFilters',
deep: true,
},
},
created() {
if (!this.isSuperAdmin) {
this.$router.push('/admin/dashboard')
}
},
destroyed() {
if (this.selectAllField) {
@ -330,6 +330,8 @@ export default {
'setSelectAllState',
]),
...mapActions('notification', ['showNotification']),
refreshTable() {
this.$refs.table.refresh()
},
@ -384,52 +386,71 @@ export default {
let user = []
user.push(id)
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('users.confirm_delete'),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteUser(user)
if (res.data.success) {
window.toastr['success'](this.$tc('users.deleted_message', 1))
this.showNotification({
type: 'success',
message: this.$tc('users.deleted_message', 1),
})
this.$refs.table.refresh()
return true
}
if (res.data.error === 'user_attached') {
window.toastr['error'](
this.$tc('users.user_attached_message'),
this.$t('general.action_failed')
)
this.showNotification({
type: 'error',
message:
(this.$tc('users.user_attached_message'),
this.$t('general.action_failed')),
})
return true
}
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
return true
}
})
},
async removeMultipleUsers() {
swal({
this.$swal({
title: this.$t('general.are_you_sure'),
text: this.$tc('users.confirm_delete', 2),
icon: '/assets/icon/trash-solid.svg',
buttons: true,
dangerMode: true,
}).then(async (willDelete) => {
if (willDelete) {
icon: 'error',
iconHtml: `<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600"fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>`,
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
let res = await this.deleteMultipleUsers()
if (res.data.success || res.data.users) {
window.toastr['success'](this.$tc('users.deleted_message', 2))
this.showNotification({
type: 'success',
message: this.$tc('users.deleted_message', 2),
})
this.$refs.table.refresh()
} else if (res.data.error) {
window.toastr['error'](res.data.message)
this.showNotification({
type: 'error',
message: res.data.message,
})
}
}
})

View File

@ -33,8 +33,8 @@
</div>
<sw-avatar
trigger="#logo-box"
:preview-avatar="previewLogo"
trigger="#logo-box"
@changed="onChange"
@uploadHandler="onUploadHandler"
@handleUploadError="onHandleUploadError"
@ -156,7 +156,7 @@ const { required, maxLength } = require('vuelidate/lib/validators')
export default {
components: {
CloudUploadIcon
CloudUploadIcon,
},
data() {
return {
@ -197,12 +197,6 @@ export default {
},
},
},
watch: {
country({ id }) {
this.companyData.country_id = id
return true
},
},
computed: {
companyNameError() {
if (!this.$v.companyData.name.$error) {
@ -241,12 +235,18 @@ export default {
}
},
},
watch: {
country({ id }) {
this.companyData.country_id = id
return true
},
},
mounted() {
this.fetchCountries()
},
methods: {
...mapActions('company', ['setSelectedCompany']),
...mapActions('notification', ['showNotification']),
onUploadHandler(cropper) {
this.previewLogo = cropper
.getCroppedCanvas()
@ -254,7 +254,10 @@ export default {
},
onHandleUploadError() {
window.toastr['error']('Oops! Something went wrong...')
this.showNotification({
type: 'error',
message: 'Oops! Something went wrong...',
})
},
onChange(file) {

View File

@ -21,7 +21,7 @@ import Mysql from './database/MysqlDatabase'
import Pgsql from './database/PgsqlDatabase'
import Sqlite from './database/SqliteDatabase'
import Sqlsrv from './database/SqlsrvDatabase'
import { mapActions } from 'vuex'
export default {
components: {
Mysql,
@ -44,6 +44,7 @@ export default {
this.getDatabaseConfig(this.database_connection)
},
methods: {
...mapActions('notification', ['showNotification']),
async getDatabaseConfig(connection) {
this.isLoading = this.isFetching = true
@ -80,20 +81,28 @@ export default {
this.$emit('next', 3)
window.toastr['success'](
this.$t('wizard.success.' + response.data.success)
)
this.showNotification({
type: 'success',
message: this.$t('wizard.success.' + response.data.success),
})
return true
} else if (response.data.error) {
window.toastr['error'](
this.$t('wizard.errors.' + response.data.error)
)
this.showNotification({
type: 'error',
message: this.$t('wizard.errors.' + response.data.error),
})
} else if (response.data.error_message) {
window.toastr['error'](response.data.error_message)
this.showNotification({
type: 'error',
message: response.data.error_message,
})
}
} catch (e) {
window.toastr['error'](e.response.data.message)
this.showNotification({
type: 'error',
message: e.response.data.message,
})
} finally {
this.isLoading = this.isFetching = false
}

View File

@ -24,7 +24,7 @@ import Smtp from './mail-driver/SmtpMailDriver'
import Mailgun from './mail-driver/MailgunMailDriver'
import Ses from './mail-driver/SesMailDriver'
import Basic from './mail-driver/BasicMailDriver'
import { mapActions } from 'vuex'
export default {
components: {
Smtp,
@ -48,6 +48,7 @@ export default {
this.getMailDrivers()
},
methods: {
...mapActions('notification', ['showNotification']),
async getMailDrivers() {
this.isLoading = this.isFetching = true
@ -67,19 +68,24 @@ export default {
)
if (response.data.success) {
this.$emit('next', 4)
window.toastr['success'](
this.$t('wizard.success.' + response.data.success)
)
this.showNotification({
type: 'success',
message: this.$t('wizard.success.' + response.data.success),
})
} else {
window.toastr['error'](
this.$t('wizard.errors.' + response.data.error)
)
this.showNotification({
type: 'error',
message: this.$t('wizard.errors.' + response.data.error),
})
}
this.isLoading = this.isFetching = false
return true
} catch (e) {
this.isLoading = this.isFetching = false
window.toastr['error']('Something went wrong')
this.showNotification({
type: 'error',
message: 'Something went wrong',
})
}
},
},

View File

@ -75,14 +75,14 @@ export default {
let self = this
if (this.errors) {
swal({
this.$swal({
title: this.$t('wizard.permissions.permission_confirm_title'),
text: this.$t('wizard.permissions.permission_confirm_desc'),
icon: 'warning',
buttons: true,
dangerMode: true,
}).then(async (willConfirm) => {
if (willConfirm) {
showCancelButton: true,
showConfirmButton: true,
}).then(async (result) => {
if (result.value) {
self.isLoading = this.isFetching = false
}
})

View File

@ -211,6 +211,7 @@ export default {
},
methods: {
...mapActions('company', ['updateCompanySettings', 'setSelectedCompany']),
...mapActions('notification', ['showNotification']),
...mapActions([
'fetchLanguages',
'fetchCurrencies',
@ -288,7 +289,10 @@ export default {
if (response.data) {
this.$emit('next', 'COMPLETED')
window.toastr['success']('Login Successful')
this.showNotification({
type: 'success',
message: 'Login Successful',
})
this.$router.push('/admin/dashboard')
}
},

View File

@ -208,13 +208,17 @@ export default {
},
methods: {
...mapActions('user', ['uploadAvatar']),
...mapActions('notification', ['showNotification']),
onUploadHandler(cropper) {
this.previewAvatar = cropper
.getCroppedCanvas()
.toDataURL(this.cropperOutputMime)
},
onHandleUploadError() {
window.toastr['error']('Oops! Something went wrong...')
this.showNotification({
type: 'error',
message: 'Oops! Something went wrong...',
})
},
onChange(file) {
this.cropperOutputMime = file.type

View File

@ -5,11 +5,6 @@
@tailwind utilities;
// Plugins
//----------------------------------
@import '../../../node_modules/toastr/toastr';
// Base Components
//----------------------------------
@ -34,3 +29,87 @@
right: 10px;
}
}
.swal2-container .swal2-popup {
transition-property: all !important;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
transition-duration: 150ms !important;
transition-delay: 150ms !important;
padding: 1.5rem !important;
}
.swal2-actions {
display: grid !important;
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
gap: 0.75rem !important;
margin-top: 1.5rem !important;
width: 100% !important;
flex-wrap: nowrap !important;
padding: 0 !important;
}
.swal2-content {
text-align: center !important;
font-size: 0.875rem !important;
line-height: 1.25rem !important;
color: #6b7280 !important;
font-weight: 500 !important;
margin-top: 0.5rem !important;
}
.swal2-icon {
height: 3rem !important;
width: 3rem !important;
border: none !important;
margin: 0 !important;
}
.swal2-header {
display: flex !important;
justify-content: center !important;
align-items: center !important;
}
.swal2-title {
text-align: center !important;
margin-top: 0.75 !important;
font-weight: 500 !important;
color: #111827 !important;
font-size: 1.125rem !important;
line-height: 1.75rem !important;
margin-top: 1.25rem;
}
.swal2-icon.swal2-error {
background: #fed7d7 !important;
border-radius: 9999px !important;
}
.swal2-icon.swal2-success {
background: #c6f6d5 !important;
border-radius: 9999px !important;
}
.swal2-icon.swal2-warning {
background: #feebc8 !important;
border-radius: 9999px !important;
}
.swal2-icon.swal2-info {
background: #bee3f8 !important;
border-radius: 9999px !important;
}
.swal2-icon.swal2-question {
background: #edf2f7 !important;
border-radius: 9999px !important;
}
.swal2-icon-content {
font-size: 2.75em !important;
}
.swal2-title {
margin: 0 !important;
margin-top: 1.25rem !important;
}

View File

@ -10,7 +10,6 @@ module.exports = {
'./node_modules/\\@bytefury/spacewind/src/**/*.vue',
'./node_modules/\\@bytefury/spacewind/plugin/**/*.js',
'flatpickr/**/*.js',
'toastr/**/*.js',
'./public/js/pace/**/*.js',
],
theme: {