diff --git a/resources/assets/js/helpers/utilities.js b/resources/assets/js/helpers/utilities.js
index 442af4c4..be9afd51 100644
--- a/resources/assets/js/helpers/utilities.js
+++ b/resources/assets/js/helpers/utilities.js
@@ -1,16 +1,16 @@
export default {
- toggleSidebar () {
+ toggleSidebar() {
let icon = document.getElementsByClassName('hamburger')[0]
document.body.classList.toggle('sidebar-open')
icon.classList.toggle('is-active')
},
- addClass (el, className) {
+ addClass(el, className) {
if (el.classList) el.classList.add(className)
else el.className += ' ' + className
},
- hasClass (el, className) {
+ hasClass(el, className) {
const hasClass = el.classList
? el.classList.contains(className)
: new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className)
@@ -18,33 +18,38 @@ export default {
return hasClass
},
- reset (prefix) {
+ reset(prefix) {
let regx = new RegExp('\\b' + prefix + '(.*)?\\b', 'g')
document.body.className = document.body.className.replace(regx, '')
},
- setLayout (layoutName) {
+ setLayout(layoutName) {
this.reset('layout-')
document.body.classList.add('layout-' + layoutName)
},
- setSkin (skinName) {
+ setSkin(skinName) {
this.reset('skin-')
document.body.classList.add('skin-' + skinName)
},
- setLogo (logoSrc) {
+ setLogo(logoSrc) {
document.getElementById('logo-desk').src = logoSrc
},
- formatMoney (amount, currency = 0) {
+ formatMoney(amount, currency = 0) {
if (!currency) {
- currency = {precision: 2, thousand_separator: ',', decimal_separator: '.', symbol: '$'}
+ currency = {
+ precision: 2,
+ thousand_separator: ',',
+ decimal_separator: '.',
+ symbol: '$',
+ }
}
amount = amount / 100
- let {precision, decimal_separator, thousand_separator, symbol} = currency
+ let { precision, decimal_separator, thousand_separator, symbol } = currency
try {
precision = Math.abs(precision)
@@ -52,25 +57,44 @@ export default {
const negativeSign = amount < 0 ? '-' : ''
- let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(precision)).toString()
- let j = (i.length > 3) ? i.length % 3 : 0
+ let i = parseInt(
+ (amount = Math.abs(Number(amount) || 0).toFixed(precision))
+ ).toString()
+ let j = i.length > 3 ? i.length % 3 : 0
let moneySymbol = `${symbol}`
- return moneySymbol + ' ' + negativeSign + (j ? i.substr(0, j) + thousand_separator : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand_separator) + (precision ? decimal_separator + Math.abs(amount - i).toFixed(precision).slice(2) : '')
+ return (
+ moneySymbol +
+ ' ' +
+ negativeSign +
+ (j ? i.substr(0, j) + thousand_separator : '') +
+ i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand_separator) +
+ (precision
+ ? decimal_separator +
+ Math.abs(amount - i)
+ .toFixed(precision)
+ .slice(2)
+ : '')
+ )
} catch (e) {
console.log(e)
}
},
- formatGraphMoney (amount, currency = 0) {
+ formatGraphMoney(amount, currency = 0) {
if (!currency) {
- currency = {precision: 2, thousand_separator: ',', decimal_separator: '.', symbol: '$'}
+ currency = {
+ precision: 2,
+ thousand_separator: ',',
+ decimal_separator: '.',
+ symbol: '$',
+ }
}
amount = amount / 100
- let {precision, decimal_separator, thousand_separator, symbol} = currency
+ let { precision, decimal_separator, thousand_separator, symbol } = currency
try {
precision = Math.abs(precision)
@@ -78,25 +102,76 @@ export default {
const negativeSign = amount < 0 ? '-' : ''
- let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(precision)).toString()
- let j = (i.length > 3) ? i.length % 3 : 0
+ let i = parseInt(
+ (amount = Math.abs(Number(amount) || 0).toFixed(precision))
+ ).toString()
+ let j = i.length > 3 ? i.length % 3 : 0
let moneySymbol = `${symbol}`
- return moneySymbol + ' ' + negativeSign + (j ? i.substr(0, j) + thousand_separator : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand_separator) + (precision ? decimal_separator + Math.abs(amount - i).toFixed(precision).slice(2) : '')
+ return (
+ moneySymbol +
+ ' ' +
+ negativeSign +
+ (j ? i.substr(0, j) + thousand_separator : '') +
+ i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand_separator) +
+ (precision
+ ? decimal_separator +
+ Math.abs(amount - i)
+ .toFixed(precision)
+ .slice(2)
+ : '')
+ )
} catch (e) {
console.log(e)
}
},
- checkValidUrl (url) {
- let pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
+ checkValidUrl(url) {
+ let pattern = new RegExp(
+ '^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
- '(\\#[-a-z\\d_]*)?$', 'i') // fragment locator
+ '(\\#[-a-z\\d_]*)?$',
+ 'i'
+ ) // fragment locator
return !!pattern.test(url)
- }
+ },
+
+ fallbackCopyTextToClipboard(text) {
+ var textArea = document.createElement('textarea')
+ textArea.value = text
+ // Avoid scrolling to bottom
+ textArea.style.top = '0'
+ textArea.style.left = '0'
+ textArea.style.position = 'fixed'
+ document.body.appendChild(textArea)
+ textArea.focus()
+ textArea.select()
+ try {
+ var successful = document.execCommand('copy')
+ var msg = successful ? 'successful' : 'unsuccessful'
+ console.log('Fallback: Copying text command was ' + msg)
+ } catch (err) {
+ console.error('Fallback: Oops, unable to copy', err)
+ }
+ document.body.removeChild(textArea)
+ },
+ copyTextToClipboard(text) {
+ if (!navigator.clipboard) {
+ this.fallbackCopyTextToClipboard(text)
+ return
+ }
+ navigator.clipboard.writeText(text).then(
+ function () {
+ return true
+ },
+ function (err) {
+ return false
+ }
+ )
+ },
}
diff --git a/resources/assets/js/plugins/en.json b/resources/assets/js/plugins/en.json
index 430ca4b1..eb78878f 100644
--- a/resources/assets/js/plugins/en.json
+++ b/resources/assets/js/plugins/en.json
@@ -13,6 +13,7 @@
},
"general": {
"view_pdf": "View PDF",
+ "copy_pdf_url": "Copy PDF Url",
"download_pdf": "Download PDF",
"save": "Save",
"cancel": "Cancel",
@@ -230,6 +231,7 @@
"convert_to_invoice": "Convert to Invoice",
"mark_as_sent": "Mark as Sent",
"send_estimate": "Send Estimate",
+ "resend_estimate": "Resend Estimate",
"record_payment": "Record Payment",
"add_estimate": "Add Estimate",
"save_estimate": "Save Estimate",
@@ -316,6 +318,7 @@
"notes": "Notes",
"view": "View",
"send_invoice": "Send Invoice",
+ "resend_invoice": "Resend Invoice",
"invoice_template": "Invoice Template",
"template": "Template",
"mark_as_sent": "Mark as sent",
@@ -618,85 +621,85 @@
"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",
+ "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": "Auto-generate 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"
- },
+ "invoices": {
+ "title": "Invoices",
+ "notes": "Notes",
+ "invoice_prefix": "Invoice Prefix",
+ "invoice_settings": "Invoice Settings",
+ "autogenerate_invoice_number": "Auto-generate 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": "Auto-generate 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"
- },
+ "estimates": {
+ "title": "Estimates",
+ "estimate_prefix": "Estimate Prefix",
+ "estimate_settings": "Estimate Settings",
+ "autogenerate_estimate_number": "Auto-generate 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": "Auto-generate 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",
- "payment_mode": "Payment Mode",
- "add_payment_mode": "Add Payment Mode",
- "mode_name": "Mode Name",
- "payment_mode_added": "Payment Mode Added",
- "payment_mode_updated": "Payment Mode Updated",
- "payment_mode_confirm_delete":"You will not be able to recover this Payment Mode",
- "already_in_use": "Payment Mode is already in use",
- "deleted_message": "Payment Mode deleted successfully"
- },
+ "payments": {
+ "title": "Payments",
+ "payment_prefix": "Payment Prefix",
+ "payment_settings": "Payment Settings",
+ "autogenerate_payment_number": "Auto-generate 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",
+ "payment_mode": "Payment Mode",
+ "add_payment_mode": "Add Payment Mode",
+ "mode_name": "Mode Name",
+ "payment_mode_added": "Payment Mode Added",
+ "payment_mode_updated": "Payment Mode Updated",
+ "payment_mode_confirm_delete": "You will not be able to recover this Payment Mode",
+ "already_in_use": "Payment Mode is already in use",
+ "deleted_message": "Payment Mode deleted successfully"
+ },
- "items": {
- "title": "Items",
- "units": "units",
- "add_item_unit": "Add Item Unit",
- "unit_name": "Unit Name",
- "item_unit_added": "Item Unit Added",
- "item_unit_updated": "Item Unit Updated",
- "item_unit_confirm_delete":"You will not be able to recover this Item unit",
- "already_in_use": "Item Unit is already in use",
- "deleted_message": "Item Unit deleted successfully"
- }
+ "items": {
+ "title": "Items",
+ "units": "units",
+ "add_item_unit": "Add Item Unit",
+ "unit_name": "Unit Name",
+ "item_unit_added": "Item Unit Added",
+ "item_unit_updated": "Item Unit Updated",
+ "item_unit_confirm_delete": "You will not be able to recover this Item unit",
+ "already_in_use": "Item Unit is already in use",
+ "deleted_message": "Item Unit deleted successfully"
+ }
},
"account_settings": {
"profile_picture": "Profile Picture",
diff --git a/resources/assets/js/views/estimates/Index.vue b/resources/assets/js/views/estimates/Index.vue
index 2ca0cd1d..96cb300d 100644
--- a/resources/assets/js/views/estimates/Index.vue
+++ b/resources/assets/js/views/estimates/Index.vue
@@ -4,16 +4,12 @@
{{ $t('estimates.title') }}
-
-
+
{{ $t('general.home') }}
-
-
+
{{ $tc('estimates.estimate', 2) }}
@@ -33,11 +29,9 @@
-
- {{ $t('estimates.new_estimate') }}
+
+ {{ $t('estimates.new_estimate') }}
@@ -46,7 +40,7 @@
-
+
-
+
-
+
-
-
+
+
-
+
-
{{ $t('general.showing') }}: {{ estimates.length }} {{ $t('general.of') }} {{ totalEstimates }}
+
+ {{ $t('general.showing') }}: {{ estimates.length }}
+ {{ $t('general.of') }} {{ totalEstimates }}
+
-
+
{{ $t('general.actions') }}
-
+
{{ $t('general.delete') }}
@@ -153,8 +183,12 @@
type="checkbox"
class="custom-control-input"
@change="selectAllEstimates"
+ />
+
@@ -178,7 +212,7 @@
:value="row.id"
type="checkbox"
class="custom-control-input"
- >
+ />
@@ -186,30 +220,30 @@
+ show="formattedEstimateDate"
+ />
+ show="name"
+ />
-
-
+
+
{{ $t('estimates.status') }}
- {{ row.status }}
+ {{
+ row.status
+ }}
-
+ show="estimate_number"
+ />
+
{{ $t('estimates.total') }}
@@ -227,50 +261,114 @@
-
-
+
+
{{ $t('general.edit') }}
-
+
{{ $t('general.delete') }}
-
+
{{ $t('general.view') }}
-
-
+
+
{{ $t('estimates.convert_to_invoice') }}
-
-
+
+
{{ $t('estimates.mark_as_sent') }}
-
-
-
+
+
+
{{ $t('estimates.send_estimate') }}
+
+
+
+
+ {{ $t('estimates.resend_estimate') }}
+
+
+
-
-
+
+
{{ $t('estimates.mark_as_accepted') }}
-
-
+
+
{{ $t('estimates.mark_as_rejected') }}
diff --git a/resources/assets/js/views/estimates/View.vue b/resources/assets/js/views/estimates/View.vue
index 1525ba9e..58ff3271 100644
--- a/resources/assets/js/views/estimates/View.vue
+++ b/resources/assets/js/views/estimates/View.vue
@@ -1,7 +1,7 @@
@@ -289,6 +338,13 @@ export default {
}
})
},
+ copyPdfUrl () {
+ let pdfUrl = `${window.location.origin}/estimates/pdf/${this.estimate.unique_hash}`
+
+ let response = this.$utils.copyTextToClipboard(pdfUrl)
+
+ window.toastr['success'](this.$tc('Copied PDF url to clipboard!'))
+ },
async removeEstimate (id) {
window.swal({
title: 'Deleted',
diff --git a/resources/assets/js/views/invoices/Index.vue b/resources/assets/js/views/invoices/Index.vue
index 5a59f728..b116fc71 100644
--- a/resources/assets/js/views/invoices/Index.vue
+++ b/resources/assets/js/views/invoices/Index.vue
@@ -1,19 +1,15 @@
-
+
{{ $t('invoices.new_invoice') }}
@@ -44,7 +44,7 @@
-
{{ $tc('customers.customer',1) }}
+
{{ $tc('customers.customer', 1) }}
{{ $t('invoices.invoice_number') }}
-
+
-
{{ $t('general.clear_all') }}
+
{{
+ $t('general.clear_all')
+ }}
-
-
+
+
{{ $t('invoices.no_invoices') }}
- {{ $t('invoices.list_of_invoices') }}
+ {{
+ $t('invoices.list_of_invoices')
+ }}
-
{{ $t('general.showing') }}: {{ invoices.length }} {{ $t('general.of') }} {{ totalInvoices }}
+
+ {{ $t('general.showing') }}: {{ invoices.length }}
+ {{ $t('general.of') }} {{ totalInvoices }}
+
-
+
{{ $t('general.actions') }}
-
+
{{ $t('general.delete') }}
@@ -155,8 +199,12 @@
type="checkbox"
class="custom-control-input"
@change="selectAllInvoices"
+ />
+
-
{{ $t('general.select_all') }}
@@ -180,8 +228,8 @@
:value="row.id"
type="checkbox"
class="custom-control-input"
- >
-
+ />
+
@@ -195,35 +243,33 @@
width="20%"
show="name"
/>
-
-
+
+
{{ $t('invoices.status') }}
- {{ (row.status != 'PARTIALLY_PAID')? row.status : row.status.replace('_', ' ') }}
+ {{
+ row.status != 'PARTIALLY_PAID'
+ ? row.status
+ : row.status.replace('_', ' ')
+ }}
-
+
{{ $t('invoices.paid_status') }}
- {{ (row.paid_status != 'PARTIALLY_PAID')? row.paid_status : row.paid_status.replace('_', ' ') }}
+ {{
+ row.paid_status != 'PARTIALLY_PAID'
+ ? row.paid_status
+ : row.paid_status.replace('_', ' ')
+ }}
-
-
+
+
{{ $t('invoices.amount_due') }}
-
+
-
-
+
+
{{ $t('general.edit') }}
-
+
{{ $t('invoices.view') }}
-
-
+
+
{{ $t('invoices.send_invoice') }}
+
+
+
+ {{ $t('invoices.resend_invoice') }}
+
+
-
-
+
+
{{ $t('invoices.mark_as_sent') }}
-
-
-
+
+
+
{{ $t('payments.record_payment') }}
-
+
{{ $t('invoices.clone_invoice') }}
-
+
{{ $t('general.delete') }}
diff --git a/resources/assets/js/views/invoices/View.vue b/resources/assets/js/views/invoices/View.vue
index e62a91b3..384d843e 100644
--- a/resources/assets/js/views/invoices/View.vue
+++ b/resources/assets/js/views/invoices/View.vue
@@ -1,7 +1,7 @@