mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 19:51:09 -04:00
Add customization
This commit is contained in:
@ -530,6 +530,7 @@
|
||||
"menu_title": {
|
||||
"account_settings": "Account Settings",
|
||||
"company_information": "Company Information",
|
||||
"customization": "Customization",
|
||||
"preferences": "Preferences",
|
||||
"notifications": "Notifications",
|
||||
"tax_types": "Tax Types",
|
||||
@ -598,6 +599,67 @@
|
||||
"save": "Save",
|
||||
"updated_message": "Company information updated successfully"
|
||||
},
|
||||
"customization": {
|
||||
"customization": "customization",
|
||||
"save": "Save",
|
||||
"addresses": {
|
||||
"title": "Addresses",
|
||||
"section_description": "You can set Customer Billing Address and Customer Shipping Address Format (Displayed in PDF only). ",
|
||||
"customer_billing_address": "Customer Billing Address",
|
||||
"customer_shipping_address": "Customer Shipping Address",
|
||||
"company_address": "Company Address",
|
||||
"insert_fields": "Insert Fields",
|
||||
"contact": "Contact",
|
||||
"address": "Address",
|
||||
"display_name": "Display Name",
|
||||
"primary_contact_name": "Primary Contact Name",
|
||||
"email": "Email",
|
||||
"website": "Website",
|
||||
"name": "Name",
|
||||
"country": "Country",
|
||||
"state": "State",
|
||||
"city": "City",
|
||||
"company_name": "Company Name",
|
||||
"address_street_1": "Address Street 1",
|
||||
"address_street_2": "Address Street 2",
|
||||
"phone": "Phone",
|
||||
"zip_code": "Zip Code",
|
||||
"address_setting_updated": "Address Setting updated successfully"
|
||||
},
|
||||
"updated_message": "Company information updated successfully",
|
||||
|
||||
"invoices": {
|
||||
"title": "Invoices",
|
||||
"notes": "Notes",
|
||||
"invoice_prefix": "Invoice Prefix",
|
||||
"invoice_settings": "Invoice Settings",
|
||||
"autogenerate_invoice_number": "Autogenerate Invoice Number",
|
||||
"invoice_setting_description": "Disable this, If you don't wish to auto-generate invoice numbers each time you create a new invoice.",
|
||||
"enter_invoice_prefix": "Enter invoice prefix",
|
||||
"terms_and_conditions": "Terms and Conditions",
|
||||
"invoice_setting_updated": "Invoice Setting updated successfully"
|
||||
},
|
||||
|
||||
"estimates": {
|
||||
"title": "Estimates",
|
||||
"estimate_prefix": "Estimate Prefix",
|
||||
"estimate_settings": "Estimate Settings",
|
||||
"autogenerate_estimate_number": "Autogenerate Estimate Number",
|
||||
"estimate_setting_description": "Disable this, If you don't wish to auto-generate estimate numbers each time you create a new estimate.",
|
||||
"enter_estimate_prefix": "Enter estmiate prefix",
|
||||
"estimate_setting_updated": "Estimate Setting updated successfully"
|
||||
},
|
||||
|
||||
"payments": {
|
||||
"title": "Payments",
|
||||
"payment_prefix": "Payment Prefix",
|
||||
"payment_settings": "Payment Settings",
|
||||
"autogenerate_payment_number": "Autogenerate Payment Number",
|
||||
"payment_setting_description": "Disable this, If you don't wish to auto-generate payment numbers each time you create a new payment.",
|
||||
"enter_payment_prefix": "Enter Payment Prefix",
|
||||
"payment_setting_updated": "Payment Setting updated successfully"
|
||||
}
|
||||
},
|
||||
"account_settings": {
|
||||
"profile_picture": "Profile Picture",
|
||||
"name": "Name",
|
||||
@ -812,6 +874,7 @@
|
||||
"maximum_options_error": "Maximum of {max} options selected. First remove a selected option to select another.",
|
||||
"notes_maxlength": "Notes should not be greater than 255 characters.",
|
||||
"address_maxlength": "Address should not be greater than 255 characters.",
|
||||
"ref_number_maxlength": "Ref Number should not be greater than 255 characters."
|
||||
"ref_number_maxlength": "Ref Number should not be greater than 255 characters.",
|
||||
"prefix_maxlength": "Prefix should not be greater than 5 characters."
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ import ReportLayout from './views/reports/layout/Index.vue'
|
||||
// Settings
|
||||
import SettingsLayout from './views/settings/layout/Index.vue'
|
||||
import CompanyInfo from './views/settings/CompanyInfo.vue'
|
||||
import Customization from './views/settings/Customization.vue'
|
||||
import Notifications from './views/settings/Notifications.vue'
|
||||
import Preferences from './views/settings/Preferences.vue'
|
||||
import UserProfile from './views/settings/UserProfile.vue'
|
||||
@ -309,6 +310,11 @@ const routes = [
|
||||
name: 'company.info',
|
||||
component: CompanyInfo
|
||||
},
|
||||
{
|
||||
path: 'customization',
|
||||
name: 'customization',
|
||||
component: Customization
|
||||
},
|
||||
{
|
||||
path: 'user-profile',
|
||||
name: 'user.profile',
|
||||
|
||||
375
resources/assets/js/views/settings/Customization.vue
Normal file
375
resources/assets/js/views/settings/Customization.vue
Normal file
@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<div class="setting-main-container customization">
|
||||
<div class="card setting-card">
|
||||
<ul class="tabs">
|
||||
<li class="tab" @click="setActiveTab('INVOICES')">
|
||||
<a :class="['tab-link', {'a-active': activeTab === 'INVOICES'}]" href="#">{{ $t('settings.customization.invoices.title') }}</a>
|
||||
</li>
|
||||
<li class="tab" @click="setActiveTab('ESTIMATES')">
|
||||
<a :class="['tab-link', {'a-active': activeTab === 'ESTIMATES'}]" href="#">{{ $t('settings.customization.estimates.title') }}</a>
|
||||
</li>
|
||||
<li class="tab" @click="setActiveTab('PAYMENTS')">
|
||||
<a :class="['tab-link', {'a-active': activeTab === 'PAYMENTS'}]" href="#">{{ $t('settings.customization.payments.title') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Invoices Tab -->
|
||||
<transition name="fade">
|
||||
<div v-if="activeTab === 'INVOICES'" class="invoice-tab">
|
||||
<form action="" class="form-section" @submit.prevent="updateInvoiceSetting">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4">
|
||||
<label class="input-label">{{ $t('settings.customization.invoices.invoice_prefix') }}</label>
|
||||
<base-input
|
||||
v-model="invoices.invoice_prefix"
|
||||
:invalid="$v.invoices.invoice_prefix.$error"
|
||||
class="prefix-input"
|
||||
@input="$v.invoices.invoice_prefix.$touch()"
|
||||
@keyup="changeToUppercase('INVOICES')"
|
||||
/>
|
||||
<span v-show="!$v.invoices.invoice_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span>
|
||||
<span v-if="!$v.invoices.invoice_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span>
|
||||
<span v-if="!$v.invoices.invoice_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<base-button
|
||||
icon="save"
|
||||
color="theme"
|
||||
type="submit"
|
||||
>
|
||||
{{ $t('settings.customization.save') }}
|
||||
</base-button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</form>
|
||||
<div class="col-md-12 mt-3">
|
||||
<div class="page-header">
|
||||
<h3 class="page-title">
|
||||
{{ $t('settings.customization.invoices.invoice_settings') }}
|
||||
</h3>
|
||||
<div class="flex-box">
|
||||
<div class="left">
|
||||
<base-switch
|
||||
v-model="invoiceAutogenerate"
|
||||
class="btn-switch"
|
||||
@change="setInvoiceSetting"
|
||||
/>
|
||||
</div>
|
||||
<div class="right ml-15">
|
||||
<p class="box-title"> {{ $t('settings.customization.invoices.autogenerate_invoice_number') }} </p>
|
||||
<p class="box-desc"> {{ $t('settings.customization.invoices.invoice_setting_description') }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- Estimates Tab -->
|
||||
<transition name="fade">
|
||||
<div v-if="activeTab === 'ESTIMATES'" class="estimate-tab">
|
||||
<form action="" class="form-section" @submit.prevent="updateEstimateSetting">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4">
|
||||
<label class="input-label">{{ $t('settings.customization.estimates.estimate_prefix') }}</label>
|
||||
<base-input
|
||||
v-model="estimates.estimate_prefix"
|
||||
:invalid="$v.estimates.estimate_prefix.$error"
|
||||
class="prefix-input"
|
||||
@input="$v.estimates.estimate_prefix.$touch()"
|
||||
@keyup="changeToUppercase('ESTIMATES')"
|
||||
/>
|
||||
<span v-show="!$v.estimates.estimate_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span>
|
||||
<span v-if="!$v.estimates.estimate_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span>
|
||||
<span v-if="!$v.estimates.estimate_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<base-button
|
||||
icon="save"
|
||||
color="theme"
|
||||
type="submit"
|
||||
>
|
||||
{{ $t('settings.customization.save') }}
|
||||
</base-button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</form>
|
||||
<div class="col-md-12 mt-3">
|
||||
<div class="page-header">
|
||||
<h3 class="page-title">
|
||||
{{ $t('settings.customization.estimates.estimate_settings') }}
|
||||
</h3>
|
||||
<div class="flex-box">
|
||||
<div class="left">
|
||||
<base-switch
|
||||
v-model="estimateAutogenerate"
|
||||
class="btn-switch"
|
||||
@change="setEstimateSetting"
|
||||
/>
|
||||
</div>
|
||||
<div class="right ml-15">
|
||||
<p class="box-title"> {{ $t('settings.customization.estimates.autogenerate_estimate_number') }} </p>
|
||||
<p class="box-desc"> {{ $t('settings.customization.estimates.estimate_setting_description') }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- Payments Tab -->
|
||||
<transition name="fade">
|
||||
<div v-if="activeTab === 'PAYMENTS'" class="payment-tab">
|
||||
<form action="" class="form-section" @submit.prevent="updatePaymentSetting">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4">
|
||||
<label class="input-label">{{ $t('settings.customization.payments.payment_prefix') }}</label>
|
||||
<base-input
|
||||
v-model="payments.payment_prefix"
|
||||
:invalid="$v.payments.payment_prefix.$error"
|
||||
class="prefix-input"
|
||||
@input="$v.payments.payment_prefix.$touch()"
|
||||
@keyup="changeToUppercase('PAYMENTS')"
|
||||
/>
|
||||
<span v-show="!$v.payments.payment_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span>
|
||||
<span v-if="!$v.payments.payment_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span>
|
||||
<span v-if="!$v.payments.payment_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<base-button
|
||||
icon="save"
|
||||
color="theme"
|
||||
type="submit"
|
||||
>
|
||||
{{ $t('settings.customization.save') }}
|
||||
</base-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="col-md-12 mt-4">
|
||||
<div class="page-header">
|
||||
<h3 class="page-title">
|
||||
{{ $t('settings.customization.payments.payment_settings') }}
|
||||
</h3>
|
||||
<div class="flex-box">
|
||||
<div class="left">
|
||||
<base-switch
|
||||
v-model="paymentAutogenerate"
|
||||
class="btn-switch"
|
||||
@change="setPaymentSetting"
|
||||
/>
|
||||
</div>
|
||||
<div class="right ml-15">
|
||||
<p class="box-title"> {{ $t('settings.customization.payments.autogenerate_payment_number') }} </p>
|
||||
<p class="box-desc"> {{ $t('settings.customization.payments.payment_setting_description') }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { validationMixin } from 'vuelidate'
|
||||
const { required, maxLength, alpha } = require('vuelidate/lib/validators')
|
||||
export default {
|
||||
mixins: [validationMixin],
|
||||
data () {
|
||||
return {
|
||||
activeTab: 'INVOICES',
|
||||
invoiceAutogenerate: false,
|
||||
estimateAutogenerate: false,
|
||||
paymentAutogenerate: false,
|
||||
invoices: {
|
||||
invoice_prefix: null,
|
||||
invoice_notes: null,
|
||||
invoice_terms_and_conditions: null
|
||||
},
|
||||
estimates: {
|
||||
estimate_prefix: null,
|
||||
estimate_notes: null,
|
||||
estimate_terms_and_conditions: null
|
||||
},
|
||||
payments: {
|
||||
payment_prefix: null
|
||||
},
|
||||
currentData: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
validations: {
|
||||
invoices: {
|
||||
invoice_prefix: {
|
||||
required,
|
||||
maxLength: maxLength(5),
|
||||
alpha
|
||||
},
|
||||
invoice_notes: {
|
||||
maxLength: maxLength(255)
|
||||
}
|
||||
},
|
||||
estimates: {
|
||||
estimate_prefix: {
|
||||
required,
|
||||
maxLength: maxLength(5),
|
||||
alpha
|
||||
},
|
||||
estimate_notes: {
|
||||
maxLength: maxLength(255)
|
||||
}
|
||||
},
|
||||
payments: {
|
||||
payment_prefix: {
|
||||
required,
|
||||
maxLength: maxLength(5),
|
||||
alpha
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
async setInvoiceSetting () {
|
||||
let data = {
|
||||
key: 'invoice_auto_generate',
|
||||
value: this.invoiceAutogenerate ? 'YES' : 'NO'
|
||||
}
|
||||
let response = await window.axios.put('/api/settings/update-setting', data)
|
||||
if (response.data) {
|
||||
window.toastr['success'](this.$t('general.setting_updated'))
|
||||
}
|
||||
},
|
||||
async setEstimateSetting () {
|
||||
let data = {
|
||||
key: 'estimate_auto_generate',
|
||||
value: this.estimateAutogenerate ? 'YES' : 'NO'
|
||||
}
|
||||
let response = await window.axios.put('/api/settings/update-setting', data)
|
||||
if (response.data) {
|
||||
window.toastr['success'](this.$t('general.setting_updated'))
|
||||
}
|
||||
},
|
||||
changeToUppercase (currentTab) {
|
||||
if (currentTab === 'INVOICES') {
|
||||
this.invoices.invoice_prefix = this.invoices.invoice_prefix.toUpperCase()
|
||||
return true
|
||||
}
|
||||
|
||||
if (currentTab === 'ESTIMATES') {
|
||||
this.estimates.estimate_prefix = this.estimates.estimate_prefix.toUpperCase()
|
||||
return true
|
||||
}
|
||||
|
||||
if (currentTab === 'PAYMENTS') {
|
||||
this.payments.payment_prefix = this.payments.payment_prefix.toUpperCase()
|
||||
return true
|
||||
}
|
||||
},
|
||||
async setPaymentSetting () {
|
||||
let data = {
|
||||
key: 'payment_auto_generate',
|
||||
value: this.paymentAutogenerate ? 'YES' : 'NO'
|
||||
}
|
||||
let response = await window.axios.put('/api/settings/update-setting', data)
|
||||
if (response.data) {
|
||||
window.toastr['success'](this.$t('general.setting_updated'))
|
||||
}
|
||||
},
|
||||
async loadData () {
|
||||
let res = await window.axios.get('/api/settings/get-customize-setting')
|
||||
|
||||
if (res.data) {
|
||||
this.invoices.invoice_prefix = res.data.invoice_prefix
|
||||
this.invoices.invoice_notes = res.data.invoice_notes
|
||||
this.invoices.invoice_terms_and_conditions = res.data.invoice_terms_and_conditions
|
||||
this.estimates.estimate_prefix = res.data.estimate_prefix
|
||||
this.estimates.estimate_notes = res.data.estimate_notes
|
||||
this.estimates.estimate_terms_and_conditions = res.data.estimate_terms_and_conditions
|
||||
this.payments.payment_prefix = res.data.payment_prefix
|
||||
|
||||
if (res.data.invoice_auto_generate === 'YES') {
|
||||
this.invoiceAutogenerate = true
|
||||
} else {
|
||||
this.invoiceAutogenerate = false
|
||||
}
|
||||
|
||||
if (res.data.estimate_auto_generate === 'YES') {
|
||||
this.estimateAutogenerate = true
|
||||
} else {
|
||||
this.estimateAutogenerate = false
|
||||
}
|
||||
|
||||
if (res.data.payment_auto_generate === 'YES') {
|
||||
this.paymentAutogenerate = true
|
||||
} else {
|
||||
this.paymentAutogenerate = false
|
||||
}
|
||||
}
|
||||
},
|
||||
async updateInvoiceSetting () {
|
||||
this.$v.invoices.$touch()
|
||||
|
||||
if (this.$v.invoices.$invalid) {
|
||||
return false
|
||||
}
|
||||
|
||||
let data = {type: 'INVOICES', ...this.invoices}
|
||||
|
||||
if (this.updateSetting(data)) {
|
||||
window.toastr['success'](this.$t('settings.customization.invoices.invoice_setting_updated'))
|
||||
}
|
||||
},
|
||||
async updateEstimateSetting () {
|
||||
this.$v.estimates.$touch()
|
||||
|
||||
if (this.$v.estimates.$invalid) {
|
||||
return false
|
||||
}
|
||||
|
||||
let data = {type: 'ESTIMATES', ...this.estimates}
|
||||
|
||||
if (this.updateSetting(data)) {
|
||||
window.toastr['success'](this.$t('settings.customization.estimates.estimate_setting_updated'))
|
||||
}
|
||||
},
|
||||
async updatePaymentSetting () {
|
||||
this.$v.payments.$touch()
|
||||
|
||||
if (this.$v.payments.$invalid) {
|
||||
return false
|
||||
}
|
||||
|
||||
let data = {type: 'PAYMENTS', ...this.payments}
|
||||
|
||||
if (this.updateSetting(data)) {
|
||||
window.toastr['success'](this.$t('settings.customization.payments.payment_setting_updated'))
|
||||
}
|
||||
},
|
||||
async updateSetting (data) {
|
||||
let res = await window.axios.put('/api/settings/update-customize-setting', data)
|
||||
|
||||
if (res.data.success) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
setActiveTab (val) {
|
||||
this.activeTab = val
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -45,6 +45,12 @@ export default {
|
||||
icon: 'building',
|
||||
iconType: 'far'
|
||||
},
|
||||
{
|
||||
link: '/admin/settings/customization',
|
||||
title: 'settings.menu_title.customization',
|
||||
icon: 'edit',
|
||||
iconType: 'fa'
|
||||
},
|
||||
{
|
||||
link: '/admin/settings/preferences',
|
||||
title: 'settings.menu_title.preferences',
|
||||
|
||||
1
resources/assets/sass/crater.scss
vendored
1
resources/assets/sass/crater.scss
vendored
@ -91,6 +91,7 @@
|
||||
@import 'pages/login';
|
||||
@import 'pages/login-3';
|
||||
@import 'pages/404';
|
||||
@import 'pages/customization';
|
||||
@import 'pages/settings';
|
||||
@import 'pages/invoices/create';
|
||||
@import 'pages/invoices/view';
|
||||
|
||||
39
resources/assets/sass/pages/customization.scss
vendored
Normal file
39
resources/assets/sass/pages/customization.scss
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
.customization {
|
||||
|
||||
.prefix-input {
|
||||
max-width: 30%;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
.invoice-customization-card {
|
||||
border: 1px solid #EBF1FA;border-radius: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: $x-small-breakpoint) {
|
||||
.address-customization-card {
|
||||
|
||||
.address-fields-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.fields-list {
|
||||
border-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tabs {
|
||||
|
||||
.tab {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user