Merge branch 'resend-email' into 'master'

Add Resend Email Option

See merge request mohit.panjvani/crater-web!236
This commit is contained in:
Mohit Panjwani
2020-05-21 05:51:36 +00:00
8 changed files with 641 additions and 211 deletions

View File

@ -1,16 +1,16 @@
export default { export default {
toggleSidebar () { toggleSidebar() {
let icon = document.getElementsByClassName('hamburger')[0] let icon = document.getElementsByClassName('hamburger')[0]
document.body.classList.toggle('sidebar-open') document.body.classList.toggle('sidebar-open')
icon.classList.toggle('is-active') icon.classList.toggle('is-active')
}, },
addClass (el, className) { addClass(el, className) {
if (el.classList) el.classList.add(className) if (el.classList) el.classList.add(className)
else el.className += ' ' + className else el.className += ' ' + className
}, },
hasClass (el, className) { hasClass(el, className) {
const hasClass = el.classList const hasClass = el.classList
? el.classList.contains(className) ? el.classList.contains(className)
: new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className) : new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className)
@ -18,33 +18,38 @@ export default {
return hasClass return hasClass
}, },
reset (prefix) { reset(prefix) {
let regx = new RegExp('\\b' + prefix + '(.*)?\\b', 'g') let regx = new RegExp('\\b' + prefix + '(.*)?\\b', 'g')
document.body.className = document.body.className.replace(regx, '') document.body.className = document.body.className.replace(regx, '')
}, },
setLayout (layoutName) { setLayout(layoutName) {
this.reset('layout-') this.reset('layout-')
document.body.classList.add('layout-' + layoutName) document.body.classList.add('layout-' + layoutName)
}, },
setSkin (skinName) { setSkin(skinName) {
this.reset('skin-') this.reset('skin-')
document.body.classList.add('skin-' + skinName) document.body.classList.add('skin-' + skinName)
}, },
setLogo (logoSrc) { setLogo(logoSrc) {
document.getElementById('logo-desk').src = logoSrc document.getElementById('logo-desk').src = logoSrc
}, },
formatMoney (amount, currency = 0) { formatMoney(amount, currency = 0) {
if (!currency) { if (!currency) {
currency = {precision: 2, thousand_separator: ',', decimal_separator: '.', symbol: '$'} currency = {
precision: 2,
thousand_separator: ',',
decimal_separator: '.',
symbol: '$',
}
} }
amount = amount / 100 amount = amount / 100
let {precision, decimal_separator, thousand_separator, symbol} = currency let { precision, decimal_separator, thousand_separator, symbol } = currency
try { try {
precision = Math.abs(precision) precision = Math.abs(precision)
@ -52,25 +57,44 @@ export default {
const negativeSign = amount < 0 ? '-' : '' const negativeSign = amount < 0 ? '-' : ''
let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(precision)).toString() let i = parseInt(
let j = (i.length > 3) ? i.length % 3 : 0 (amount = Math.abs(Number(amount) || 0).toFixed(precision))
).toString()
let j = i.length > 3 ? i.length % 3 : 0
let moneySymbol = `<span style="font-family: sans-serif">${symbol}</span>` let moneySymbol = `<span style="font-family: sans-serif">${symbol}</span>`
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) { } catch (e) {
console.log(e) console.log(e)
} }
}, },
formatGraphMoney (amount, currency = 0) { formatGraphMoney(amount, currency = 0) {
if (!currency) { if (!currency) {
currency = {precision: 2, thousand_separator: ',', decimal_separator: '.', symbol: '$'} currency = {
precision: 2,
thousand_separator: ',',
decimal_separator: '.',
symbol: '$',
}
} }
amount = amount / 100 amount = amount / 100
let {precision, decimal_separator, thousand_separator, symbol} = currency let { precision, decimal_separator, thousand_separator, symbol } = currency
try { try {
precision = Math.abs(precision) precision = Math.abs(precision)
@ -78,25 +102,76 @@ export default {
const negativeSign = amount < 0 ? '-' : '' const negativeSign = amount < 0 ? '-' : ''
let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(precision)).toString() let i = parseInt(
let j = (i.length > 3) ? i.length % 3 : 0 (amount = Math.abs(Number(amount) || 0).toFixed(precision))
).toString()
let j = i.length > 3 ? i.length % 3 : 0
let moneySymbol = `${symbol}` 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) { } catch (e) {
console.log(e) console.log(e)
} }
}, },
checkValidUrl (url) { checkValidUrl(url) {
let pattern = new RegExp('^(https?:\\/\\/)?' + // protocol let pattern = new RegExp(
'^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name '((([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{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$', 'i') // fragment locator '(\\#[-a-z\\d_]*)?$',
'i'
) // fragment locator
return !!pattern.test(url) 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
}
)
},
} }

View File

@ -13,6 +13,7 @@
}, },
"general": { "general": {
"view_pdf": "View PDF", "view_pdf": "View PDF",
"copy_pdf_url": "Copy PDF Url",
"download_pdf": "Download PDF", "download_pdf": "Download PDF",
"save": "Save", "save": "Save",
"cancel": "Cancel", "cancel": "Cancel",
@ -230,6 +231,7 @@
"convert_to_invoice": "Convert to Invoice", "convert_to_invoice": "Convert to Invoice",
"mark_as_sent": "Mark as Sent", "mark_as_sent": "Mark as Sent",
"send_estimate": "Send Estimate", "send_estimate": "Send Estimate",
"resend_estimate": "Resend Estimate",
"record_payment": "Record Payment", "record_payment": "Record Payment",
"add_estimate": "Add Estimate", "add_estimate": "Add Estimate",
"save_estimate": "Save Estimate", "save_estimate": "Save Estimate",
@ -316,6 +318,7 @@
"notes": "Notes", "notes": "Notes",
"view": "View", "view": "View",
"send_invoice": "Send Invoice", "send_invoice": "Send Invoice",
"resend_invoice": "Resend Invoice",
"invoice_template": "Invoice Template", "invoice_template": "Invoice Template",
"template": "Template", "template": "Template",
"mark_as_sent": "Mark as sent", "mark_as_sent": "Mark as sent",

View File

@ -4,16 +4,12 @@
<h3 class="page-title">{{ $t('estimates.title') }}</h3> <h3 class="page-title">{{ $t('estimates.title') }}</h3>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<router-link <router-link slot="item-title" to="dashboard">
slot="item-title"
to="dashboard">
{{ $t('general.home') }} {{ $t('general.home') }}
</router-link> </router-link>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<router-link <router-link slot="item-title" to="#">
slot="item-title"
to="#">
{{ $tc('estimates.estimate', 2) }} {{ $tc('estimates.estimate', 2) }}
</router-link> </router-link>
</li> </li>
@ -33,11 +29,9 @@
</base-button> </base-button>
</div> </div>
<router-link slot="item-title" class="col-xs-2" to="estimates/create"> <router-link slot="item-title" class="col-xs-2" to="estimates/create">
<base-button <base-button size="large" icon="plus" color="theme">
size="large" {{ $t('estimates.new_estimate') }}</base-button
icon="plus" >
color="theme" >
{{ $t('estimates.new_estimate') }}</base-button>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -46,7 +40,7 @@
<div v-show="showFilters" class="filter-section"> <div v-show="showFilters" class="filter-section">
<div class="filter-container"> <div class="filter-container">
<div class="filter-customer"> <div class="filter-customer">
<label>{{ $tc('customers.customer',1) }} </label> <label>{{ $tc('customers.customer', 1) }} </label>
<base-customer-select <base-customer-select
ref="customerSelect" ref="customerSelect"
@select="onSelectCustomer" @select="onSelectCustomer"
@ -85,21 +79,28 @@
</div> </div>
<div class="filter-estimate"> <div class="filter-estimate">
<label>{{ $t('estimates.estimate_number') }}</label> <label>{{ $t('estimates.estimate_number') }}</label>
<base-input <base-input v-model="filters.estimate_number" icon="hashtag" />
v-model="filters.estimate_number"
icon="hashtag"/>
</div> </div>
</div> </div>
<label class="clear-filter" @click="clearFilter">{{ $t('general.clear_all') }}</label> <label class="clear-filter" @click="clearFilter">{{
$t('general.clear_all')
}}</label>
</div> </div>
</transition> </transition>
<div v-cloak v-show="showEmptyScreen" class="col-xs-1 no-data-info" align="center"> <div
<moon-walker-icon class="mt-5 mb-4"/> v-cloak
v-show="showEmptyScreen"
class="col-xs-1 no-data-info"
align="center"
>
<moon-walker-icon class="mt-5 mb-4" />
<div class="row" align="center"> <div class="row" align="center">
<label class="col title">{{ $t('estimates.no_estimates') }}</label> <label class="col title">{{ $t('estimates.no_estimates') }}</label>
</div> </div>
<div class="row"> <div class="row">
<label class="description col mt-1" align="center">{{ $t('estimates.list_of_estimates') }}</label> <label class="description col mt-1" align="center">{{
$t('estimates.list_of_estimates')
}}</label>
</div> </div>
<div class="btn-container"> <div class="btn-container">
<base-button <base-button
@ -116,28 +117,57 @@
<div v-show="!showEmptyScreen" class="table-container"> <div v-show="!showEmptyScreen" class="table-container">
<div class="table-actions mt-5"> <div class="table-actions mt-5">
<p class="table-stats">{{ $t('general.showing') }}: <b>{{ estimates.length }}</b> {{ $t('general.of') }} <b>{{ totalEstimates }}</b></p> <p class="table-stats">
{{ $t('general.showing') }}: <b>{{ estimates.length }}</b>
{{ $t('general.of') }} <b>{{ totalEstimates }}</b>
</p>
<!-- Tabs --> <!-- Tabs -->
<ul class="tabs"> <ul class="tabs">
<li class="tab" @click="getStatus('DRAFT')"> <li class="tab" @click="getStatus('DRAFT')">
<a :class="['tab-link', {'a-active': filters.status === 'DRAFT'}]" href="#">{{ $t('general.draft') }}</a> <a
:class="['tab-link', { 'a-active': filters.status === 'DRAFT' }]"
href="#"
>{{ $t('general.draft') }}</a
>
</li> </li>
<li class="tab" @click="getStatus('SENT')"> <li class="tab" @click="getStatus('SENT')">
<a :class="['tab-link', {'a-active': filters.status === 'SENT'}]" href="#" >{{ $t('general.sent') }}</a> <a
:class="['tab-link', { 'a-active': filters.status === 'SENT' }]"
href="#"
>{{ $t('general.sent') }}</a
>
</li> </li>
<li class="tab" @click="getStatus('')"> <li class="tab" @click="getStatus('')">
<a :class="['tab-link', {'a-active': filters.status === '' || filters.status !== 'DRAFT' && filters.status !== 'SENT'}]" href="#">{{ $t('general.all') }}</a> <a
:class="[
'tab-link',
{
'a-active':
filters.status === '' ||
(filters.status !== 'DRAFT' && filters.status !== 'SENT'),
},
]"
href="#"
>{{ $t('general.all') }}</a
>
</li> </li>
</ul> </ul>
<transition name="fade"> <transition name="fade">
<v-dropdown v-if="selectedEstimates.length" :show-arrow="false"> <v-dropdown v-if="selectedEstimates.length" :show-arrow="false">
<span slot="activator" href="#" class="table-actions-button dropdown-toggle"> <span
slot="activator"
href="#"
class="table-actions-button dropdown-toggle"
>
{{ $t('general.actions') }} {{ $t('general.actions') }}
</span> </span>
<v-dropdown-item> <v-dropdown-item>
<div class="dropdown-item" @click="removeMultipleEstimates"> <div class="dropdown-item" @click="removeMultipleEstimates">
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> <font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>
@ -153,8 +183,12 @@
type="checkbox" type="checkbox"
class="custom-control-input" class="custom-control-input"
@change="selectAllEstimates" @change="selectAllEstimates"
/>
<label
v-show="!isRequestOngoing"
for="select-all"
class="custom-control-label selectall"
> >
<label v-show="!isRequestOngoing" for="select-all" class="custom-control-label selectall">
<span class="select-all-label">{{ $t('general.select_all') }} </span> <span class="select-all-label">{{ $t('general.select_all') }} </span>
</label> </label>
</div> </div>
@ -178,7 +212,7 @@
:value="row.id" :value="row.id"
type="checkbox" type="checkbox"
class="custom-control-input" class="custom-control-input"
> />
<label :for="row.id" class="custom-control-label" /> <label :for="row.id" class="custom-control-label" />
</div> </div>
</template> </template>
@ -186,30 +220,30 @@
<table-column <table-column
:label="$t('estimates.date')" :label="$t('estimates.date')"
sort-as="estimate_date" sort-as="estimate_date"
show="formattedEstimateDate" /> show="formattedEstimateDate"
/>
<table-column <table-column
:label="$t('estimates.customer')" :label="$t('estimates.customer')"
sort-as="name" sort-as="name"
show="name" /> show="name"
/>
<!-- <table-column <!-- <table-column
:label="$t('estimates.expiry_date')" :label="$t('estimates.expiry_date')"
sort-as="expiry_date" sort-as="expiry_date"
show="formattedExpiryDate" /> --> show="formattedExpiryDate" /> -->
<table-column <table-column :label="$t('estimates.status')" show="status">
:label="$t('estimates.status')" <template slot-scope="row">
show="status" >
<template slot-scope="row" >
<span> {{ $t('estimates.status') }}</span> <span> {{ $t('estimates.status') }}</span>
<span :class="'est-status-'+row.status.toLowerCase()">{{ row.status }}</span> <span :class="'est-status-' + row.status.toLowerCase()">{{
row.status
}}</span>
</template> </template>
</table-column> </table-column>
<table-column <table-column
:label="$tc('estimates.estimate', 1)" :label="$tc('estimates.estimate', 1)"
show="estimate_number"/> show="estimate_number"
<table-column />
:label="$t('invoices.total')" <table-column :label="$t('invoices.total')" sort-as="total">
sort-as="total"
>
<template slot-scope="row"> <template slot-scope="row">
<span> {{ $t('estimates.total') }}</span> <span> {{ $t('estimates.total') }}</span>
<div v-html="$utils.formatMoney(row.total, row.user.currency)" /> <div v-html="$utils.formatMoney(row.total, row.user.currency)" />
@ -227,50 +261,114 @@
<dot-icon /> <dot-icon />
</a> </a>
<v-dropdown-item> <v-dropdown-item>
<router-link :to="{path: `estimates/${row.id}/edit`}" class="dropdown-item"> <router-link
<font-awesome-icon :icon="['fas', 'pencil-alt']" class="dropdown-item-icon" /> :to="{ path: `estimates/${row.id}/edit` }"
class="dropdown-item"
>
<font-awesome-icon
:icon="['fas', 'pencil-alt']"
class="dropdown-item-icon"
/>
{{ $t('general.edit') }} {{ $t('general.edit') }}
</router-link> </router-link>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item> <v-dropdown-item>
<div class="dropdown-item" @click="removeEstimate(row.id)"> <div class="dropdown-item" @click="removeEstimate(row.id)">
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> <font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item> <v-dropdown-item>
<router-link :to="{path: `estimates/${row.id}/view`}" class="dropdown-item"> <router-link
:to="{ path: `estimates/${row.id}/view` }"
class="dropdown-item"
>
<font-awesome-icon icon="eye" class="dropdown-item-icon" /> <font-awesome-icon icon="eye" class="dropdown-item-icon" />
{{ $t('general.view') }} {{ $t('general.view') }}
</router-link> </router-link>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item> <v-dropdown-item>
<a class="dropdown-item" href="#/" @click="convertInToinvoice(row.id)"> <a
<font-awesome-icon icon="file-alt" class="dropdown-item-icon" /> class="dropdown-item"
href="#/"
@click="convertInToinvoice(row.id)"
>
<font-awesome-icon
icon="file-alt"
class="dropdown-item-icon"
/>
{{ $t('estimates.convert_to_invoice') }} {{ $t('estimates.convert_to_invoice') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item v-if="row.status !== 'SENT'"> <v-dropdown-item v-if="row.status !== 'SENT'">
<a class="dropdown-item" href="#/" @click.self="onMarkAsSent(row.id)"> <a
<font-awesome-icon icon="check-circle" class="dropdown-item-icon" /> class="dropdown-item"
href="#/"
@click.self="onMarkAsSent(row.id)"
>
<font-awesome-icon
icon="check-circle"
class="dropdown-item-icon"
/>
{{ $t('estimates.mark_as_sent') }} {{ $t('estimates.mark_as_sent') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item v-if="row.status !== 'SENT'"> <v-dropdown-item v-if="row.status === 'DRAFT'">
<a class="dropdown-item" href="#/" @click.self="sendEstimate(row.id)"> <a
<font-awesome-icon icon="paper-plane" class="dropdown-item-icon" /> class="dropdown-item"
href="#/"
@click.self="sendEstimate(row.id)"
>
<font-awesome-icon
icon="paper-plane"
class="dropdown-item-icon"
/>
{{ $t('estimates.send_estimate') }} {{ $t('estimates.send_estimate') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<!-- resend estimte -->
<v-dropdown-item
v-if="row.status == 'SENT' || row.status == 'VIEWED'"
>
<a
class="dropdown-item"
href="#/"
@click.self="sendEstimate(row.id)"
>
<font-awesome-icon
icon="paper-plane"
class="dropdown-item-icon"
/>
{{ $t('estimates.resend_estimate') }}
</a>
</v-dropdown-item>
<!-- -->
<v-dropdown-item v-if="row.status !== 'ACCEPTED'"> <v-dropdown-item v-if="row.status !== 'ACCEPTED'">
<a class="dropdown-item" href="#/" @click.self="onMarkAsAccepted(row.id)"> <a
<font-awesome-icon icon="check-circle" class="dropdown-item-icon" /> class="dropdown-item"
href="#/"
@click.self="onMarkAsAccepted(row.id)"
>
<font-awesome-icon
icon="check-circle"
class="dropdown-item-icon"
/>
{{ $t('estimates.mark_as_accepted') }} {{ $t('estimates.mark_as_accepted') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item v-if="row.status !== 'REJECTED'"> <v-dropdown-item v-if="row.status !== 'REJECTED'">
<a class="dropdown-item" href="#/" @click.self="onMarkAsRejected(row.id)"> <a
<font-awesome-icon icon="times-circle" class="dropdown-item-icon" /> class="dropdown-item"
href="#/"
@click.self="onMarkAsRejected(row.id)"
>
<font-awesome-icon
icon="times-circle"
class="dropdown-item-icon"
/>
{{ $t('estimates.mark_as_rejected') }} {{ $t('estimates.mark_as_rejected') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-if="estimate" class="main-content estimate-view-page"> <div v-if="estimate" class="main-content estimate-view-page">
<div class="page-header"> <div class="page-header">
<h3 class="page-title"> {{ estimate.estimate_number }}</h3> <h3 class="page-title">{{ estimate.estimate_number }}</h3>
<div class="page-actions row"> <div class="page-actions row">
<div class="col-xs-2 mr-3"> <div class="col-xs-2 mr-3">
<base-button <base-button
@ -26,19 +26,42 @@
{{ $t('estimates.send_estimate') }} {{ $t('estimates.send_estimate') }}
</base-button> </base-button>
</div> </div>
<v-dropdown :close-on-select="false" align="left" class="filter-container"> <v-dropdown
:close-on-select="true"
align="left"
class="filter-container"
>
<a slot="activator" href="#"> <a slot="activator" href="#">
<base-button color="theme"> <base-button color="theme">
<font-awesome-icon icon="ellipsis-h" /> <font-awesome-icon icon="ellipsis-h" />
</base-button> </base-button>
</a> </a>
<v-dropdown-item> <v-dropdown-item>
<router-link :to="{path: `/admin/estimates/${$route.params.id}/edit`}" class="dropdown-item"> <div class="dropdown-item" @click="copyPdfUrl()">
<font-awesome-icon :icon="['fas', 'pencil-alt']" class="dropdown-item-icon"/> <font-awesome-icon
:icon="['fas', 'link']"
class="dropdown-item-icon"
/>
{{ $t('general.copy_pdf_url') }}
</div>
<router-link
:to="{ path: `/admin/estimates/${$route.params.id}/edit` }"
class="dropdown-item"
>
<font-awesome-icon
:icon="['fas', 'pencil-alt']"
class="dropdown-item-icon"
/>
{{ $t('general.edit') }} {{ $t('general.edit') }}
</router-link> </router-link>
<div class="dropdown-item" @click="removeEstimate($route.params.id)"> <div
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> class="dropdown-item"
@click="removeEstimate($route.params.id)"
>
<font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>
@ -57,14 +80,18 @@
align-icon="right" align-icon="right"
@input="onSearched()" @input="onSearched()"
/> />
<div <div class="btn-group ml-3" role="group" aria-label="First group">
class="btn-group ml-3" <v-dropdown
role="group" :close-on-select="false"
aria-label="First group" align="left"
> class="filter-container"
<v-dropdown :close-on-select="false" align="left" class="filter-container"> >
<a slot="activator" href="#"> <a slot="activator" href="#">
<base-button class="inv-button inv-filter-fields-btn" color="default" size="medium"> <base-button
class="inv-button inv-filter-fields-btn"
color="default"
size="medium"
>
<font-awesome-icon icon="filter" /> <font-awesome-icon icon="filter" />
</base-button> </base-button>
</a> </a>
@ -80,8 +107,10 @@
class="inv-radio" class="inv-radio"
value="estimate_date" value="estimate_date"
@change="onSearched" @change="onSearched"
> />
<label class="inv-label" for="filter_estimate_date">{{ $t('reports.estimates.estimate_date') }}</label> <label class="inv-label" for="filter_estimate_date">{{
$t('reports.estimates.estimate_date')
}}</label>
</div> </div>
<div class="filter-items"> <div class="filter-items">
<input <input
@ -92,8 +121,10 @@
class="inv-radio" class="inv-radio"
value="expiry_date" value="expiry_date"
@change="onSearched" @change="onSearched"
> />
<label class="inv-label" for="filter_due_date">{{ $t('estimates.due_date') }}</label> <label class="inv-label" for="filter_due_date">{{
$t('estimates.due_date')
}}</label>
</div> </div>
<div class="filter-items"> <div class="filter-items">
<input <input
@ -104,11 +135,19 @@
class="inv-radio" class="inv-radio"
value="estimate_number" value="estimate_number"
@change="onSearched" @change="onSearched"
> />
<label class="inv-label" for="filter_estimate_number">{{ $t('estimates.estimate_number') }}</label> <label class="inv-label" for="filter_estimate_number">{{
$t('estimates.estimate_number')
}}</label>
</div> </div>
</v-dropdown> </v-dropdown>
<base-button v-tooltip.top-center="{ content: getOrderName }" class="inv-button inv-filter-sorting-btn" color="default" size="medium" @click="sortData"> <base-button
v-tooltip.top-center="{ content: getOrderName }"
class="inv-button inv-filter-sorting-btn"
color="default"
size="medium"
@click="sortData"
>
<font-awesome-icon v-if="getOrderBy" icon="sort-amount-up" /> <font-awesome-icon v-if="getOrderBy" icon="sort-amount-up" />
<font-awesome-icon v-else icon="sort-amount-down" /> <font-awesome-icon v-else icon="sort-amount-down" />
</base-button> </base-button>
@ -116,7 +155,7 @@
</div> </div>
<div class="side-content"> <div class="side-content">
<router-link <router-link
v-for="(estimate,index) in estimates" v-for="(estimate, index) in estimates"
:to="`/admin/estimates/${estimate.id}/view`" :to="`/admin/estimates/${estimate.id}/view`"
:key="index" :key="index"
class="side-estimate" class="side-estimate"
@ -124,10 +163,20 @@
<div class="left"> <div class="left">
<div class="inv-name">{{ estimate.user.name }}</div> <div class="inv-name">{{ estimate.user.name }}</div>
<div class="inv-number">{{ estimate.estimate_number }}</div> <div class="inv-number">{{ estimate.estimate_number }}</div>
<div :class="'est-status-'+estimate.status.toLowerCase()" class="inv-status">{{ estimate.status }}</div> <div
:class="'est-status-' + estimate.status.toLowerCase()"
class="inv-status"
>
{{ estimate.status }}
</div>
</div> </div>
<div class="right"> <div class="right">
<div class="inv-amount" v-html="$utils.formatMoney(estimate.total, estimate.user.currency)" /> <div
class="inv-amount"
v-html="
$utils.formatMoney(estimate.total, estimate.user.currency)
"
/>
<div class="inv-date">{{ estimate.formattedEstimateDate }}</div> <div class="inv-date">{{ estimate.formattedEstimateDate }}</div>
</div> </div>
</router-link> </router-link>
@ -137,7 +186,7 @@
</div> </div>
</div> </div>
<div class="estimate-view-page-container"> <div class="estimate-view-page-container">
<iframe :src="`${shareableLink}`" class="frame-style"/> <iframe :src="`${shareableLink}`" class="frame-style" />
</div> </div>
</div> </div>
</template> </template>
@ -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) { async removeEstimate (id) {
window.swal({ window.swal({
title: 'Deleted', title: 'Deleted',

View File

@ -1,19 +1,15 @@
<template> <template>
<div class="invoice-index-page invoices main-content"> <div class="invoice-index-page invoices main-content">
<div class="page-header"> <div class="page-header">
<h3 class="page-title"> {{ $t('invoices.title') }}</h3> <h3 class="page-title">{{ $t('invoices.title') }}</h3>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<router-link <router-link slot="item-title" to="dashboard">
slot="item-title"
to="dashboard">
{{ $t('general.home') }} {{ $t('general.home') }}
</router-link> </router-link>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<router-link <router-link slot="item-title" to="#">
slot="item-title"
to="#">
{{ $tc('invoices.invoice', 2) }} {{ $tc('invoices.invoice', 2) }}
</router-link> </router-link>
</li> </li>
@ -32,7 +28,11 @@
{{ $t('general.filter') }} {{ $t('general.filter') }}
</base-button> </base-button>
</div> </div>
<router-link slot="item-title" class="col-xs-2" to="/admin/invoices/create"> <router-link
slot="item-title"
class="col-xs-2"
to="/admin/invoices/create"
>
<base-button size="large" icon="plus" color="theme"> <base-button size="large" icon="plus" color="theme">
{{ $t('invoices.new_invoice') }} {{ $t('invoices.new_invoice') }}
</base-button> </base-button>
@ -44,7 +44,7 @@
<div v-show="showFilters" class="filter-section"> <div v-show="showFilters" class="filter-section">
<div class="filter-container"> <div class="filter-container">
<div class="filter-customer"> <div class="filter-customer">
<label>{{ $tc('customers.customer',1) }} </label> <label>{{ $tc('customers.customer', 1) }} </label>
<base-customer-select <base-customer-select
ref="customerSelect" ref="customerSelect"
@select="onSelectCustomer" @select="onSelectCustomer"
@ -88,22 +88,29 @@
</div> </div>
<div class="filter-invoice"> <div class="filter-invoice">
<label>{{ $t('invoices.invoice_number') }}</label> <label>{{ $t('invoices.invoice_number') }}</label>
<base-input <base-input v-model="filters.invoice_number" icon="hashtag" />
v-model="filters.invoice_number"
icon="hashtag"/>
</div> </div>
</div> </div>
<label class="clear-filter" @click="clearFilter">{{ $t('general.clear_all') }}</label> <label class="clear-filter" @click="clearFilter">{{
$t('general.clear_all')
}}</label>
</div> </div>
</transition> </transition>
<div v-cloak v-show="showEmptyScreen" class="col-xs-1 no-data-info" align="center"> <div
<moon-walker-icon class="mt-5 mb-4"/> v-cloak
v-show="showEmptyScreen"
class="col-xs-1 no-data-info"
align="center"
>
<moon-walker-icon class="mt-5 mb-4" />
<div class="row" align="center"> <div class="row" align="center">
<label class="col title">{{ $t('invoices.no_invoices') }}</label> <label class="col title">{{ $t('invoices.no_invoices') }}</label>
</div> </div>
<div class="row"> <div class="row">
<label class="description col mt-1" align="center">{{ $t('invoices.list_of_invoices') }}</label> <label class="description col mt-1" align="center">{{
$t('invoices.list_of_invoices')
}}</label>
</div> </div>
<div class="btn-container"> <div class="btn-container">
<base-button <base-button
@ -120,28 +127,65 @@
<div v-show="!showEmptyScreen" class="table-container"> <div v-show="!showEmptyScreen" class="table-container">
<div class="table-actions mt-5"> <div class="table-actions mt-5">
<p class="table-stats">{{ $t('general.showing') }}: <b>{{ invoices.length }}</b> {{ $t('general.of') }} <b>{{ totalInvoices }}</b></p> <p class="table-stats">
{{ $t('general.showing') }}: <b>{{ invoices.length }}</b>
{{ $t('general.of') }} <b>{{ totalInvoices }}</b>
</p>
<!-- Tabs --> <!-- Tabs -->
<ul class="tabs"> <ul class="tabs">
<li class="tab" @click="getStatus('UNPAID')"> <li class="tab" @click="getStatus('UNPAID')">
<a :class="['tab-link', {'a-active': filters.status.value === 'UNPAID'}]" href="#" >{{ $t('general.due') }}</a> <a
:class="[
'tab-link',
{ 'a-active': filters.status.value === 'UNPAID' },
]"
href="#"
>{{ $t('general.due') }}</a
>
</li> </li>
<li class="tab" @click="getStatus('DRAFT')"> <li class="tab" @click="getStatus('DRAFT')">
<a :class="['tab-link', {'a-active': filters.status.value === 'DRAFT'}]" href="#">{{ $t('general.draft') }}</a> <a
:class="[
'tab-link',
{ 'a-active': filters.status.value === 'DRAFT' },
]"
href="#"
>{{ $t('general.draft') }}</a
>
</li> </li>
<li class="tab" @click="getStatus('')"> <li class="tab" @click="getStatus('')">
<a :class="['tab-link', {'a-active': filters.status.value === '' || filters.status.value === null || filters.status.value !== 'DRAFT' && filters.status.value !== 'UNPAID'}]" href="#">{{ $t('general.all') }}</a> <a
:class="[
'tab-link',
{
'a-active':
filters.status.value === '' ||
filters.status.value === null ||
(filters.status.value !== 'DRAFT' &&
filters.status.value !== 'UNPAID'),
},
]"
href="#"
>{{ $t('general.all') }}</a
>
</li> </li>
</ul> </ul>
<transition name="fade"> <transition name="fade">
<v-dropdown v-if="selectedInvoices.length" :show-arrow="false"> <v-dropdown v-if="selectedInvoices.length" :show-arrow="false">
<span slot="activator" href="#" class="table-actions-button dropdown-toggle"> <span
slot="activator"
href="#"
class="table-actions-button dropdown-toggle"
>
{{ $t('general.actions') }} {{ $t('general.actions') }}
</span> </span>
<v-dropdown-item> <v-dropdown-item>
<div class="dropdown-item" @click="removeMultipleInvoices"> <div class="dropdown-item" @click="removeMultipleInvoices">
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> <font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>
@ -155,8 +199,12 @@
type="checkbox" type="checkbox"
class="custom-control-input" class="custom-control-input"
@change="selectAllInvoices" @change="selectAllInvoices"
/>
<label
v-show="!isRequestOngoing"
for="select-all"
class="custom-control-label selectall"
> >
<label v-show="!isRequestOngoing" for="select-all" class="custom-control-label selectall">
<span class="select-all-label">{{ $t('general.select_all') }} </span> <span class="select-all-label">{{ $t('general.select_all') }} </span>
</label> </label>
</div> </div>
@ -180,8 +228,8 @@
:value="row.id" :value="row.id"
type="checkbox" type="checkbox"
class="custom-control-input" class="custom-control-input"
> />
<label :for="row.id" class="custom-control-label"/> <label :for="row.id" class="custom-control-label" />
</div> </div>
</template> </template>
</table-column> </table-column>
@ -195,35 +243,33 @@
width="20%" width="20%"
show="name" show="name"
/> />
<table-column <table-column :label="$t('invoices.status')" sort-as="status">
:label="$t('invoices.status')" <template slot-scope="row">
sort-as="status"
>
<template slot-scope="row" >
<span> {{ $t('invoices.status') }}</span> <span> {{ $t('invoices.status') }}</span>
<span :class="'inv-status-'+row.status.toLowerCase()">{{ (row.status != 'PARTIALLY_PAID')? row.status : row.status.replace('_', ' ') }}</span> <span :class="'inv-status-' + row.status.toLowerCase()">{{
row.status != 'PARTIALLY_PAID'
? row.status
: row.status.replace('_', ' ')
}}</span>
</template> </template>
</table-column> </table-column>
<table-column <table-column :label="$t('invoices.paid_status')" sort-as="paid_status">
:label="$t('invoices.paid_status')"
sort-as="paid_status"
>
<template slot-scope="row"> <template slot-scope="row">
<span>{{ $t('invoices.paid_status') }}</span> <span>{{ $t('invoices.paid_status') }}</span>
<span :class="'inv-status-'+row.paid_status.toLowerCase()">{{ (row.paid_status != 'PARTIALLY_PAID')? row.paid_status : row.paid_status.replace('_', ' ') }}</span> <span :class="'inv-status-' + row.paid_status.toLowerCase()">{{
row.paid_status != 'PARTIALLY_PAID'
? row.paid_status
: row.paid_status.replace('_', ' ')
}}</span>
</template> </template>
</table-column> </table-column>
<table-column <table-column :label="$t('invoices.number')" show="invoice_number" />
:label="$t('invoices.number')" <table-column :label="$t('invoices.amount_due')" sort-as="due_amount">
show="invoice_number"
/>
<table-column
:label="$t('invoices.amount_due')"
sort-as="due_amount"
>
<template slot-scope="row"> <template slot-scope="row">
<span>{{ $t('invoices.amount_due') }}</span> <span>{{ $t('invoices.amount_due') }}</span>
<div v-html="$utils.formatMoney(row.due_amount, row.user.currency)"/> <div
v-html="$utils.formatMoney(row.due_amount, row.user.currency)"
/>
</template> </template>
</table-column> </table-column>
<table-column <table-column
@ -238,42 +284,91 @@
<dot-icon /> <dot-icon />
</a> </a>
<v-dropdown-item> <v-dropdown-item>
<router-link :to="{path: `invoices/${row.id}/edit`}" class="dropdown-item"> <router-link
<font-awesome-icon :icon="['fas', 'pencil-alt']" class="dropdown-item-icon"/> :to="{ path: `invoices/${row.id}/edit` }"
class="dropdown-item"
>
<font-awesome-icon
:icon="['fas', 'pencil-alt']"
class="dropdown-item-icon"
/>
{{ $t('general.edit') }} {{ $t('general.edit') }}
</router-link> </router-link>
<router-link :to="{path: `invoices/${row.id}/view`}" class="dropdown-item"> <router-link
:to="{ path: `invoices/${row.id}/view` }"
class="dropdown-item"
>
<font-awesome-icon icon="eye" class="dropdown-item-icon" /> <font-awesome-icon icon="eye" class="dropdown-item-icon" />
{{ $t('invoices.view') }} {{ $t('invoices.view') }}
</router-link> </router-link>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item v-if="row.status == 'DRAFT'"> <v-dropdown-item v-if="row.status == 'DRAFT'">
<a class="dropdown-item" href="#/" @click="sendInvoice(row.id)" > <a class="dropdown-item" href="#/" @click="sendInvoice(row.id)">
<font-awesome-icon icon="paper-plane" class="dropdown-item-icon" /> <font-awesome-icon
icon="paper-plane"
class="dropdown-item-icon"
/>
{{ $t('invoices.send_invoice') }} {{ $t('invoices.send_invoice') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item
v-if="row.status === 'SENT' || row.status === 'VIEWED'"
>
<a class="dropdown-item" href="#/" @click="sendInvoice(row.id)">
<font-awesome-icon
icon="paper-plane"
class="dropdown-item-icon"
/>
{{ $t('invoices.resend_invoice') }}
</a>
</v-dropdown-item>
<v-dropdown-item v-if="row.status == 'DRAFT'"> <v-dropdown-item v-if="row.status == 'DRAFT'">
<a class="dropdown-item" href="#/" @click="markInvoiceAsSent(row.id)"> <a
<font-awesome-icon icon="check-circle" class="dropdown-item-icon" /> class="dropdown-item"
href="#/"
@click="markInvoiceAsSent(row.id)"
>
<font-awesome-icon
icon="check-circle"
class="dropdown-item-icon"
/>
{{ $t('invoices.mark_as_sent') }} {{ $t('invoices.mark_as_sent') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item v-if="row.status === 'SENT' || row.status === 'VIEWED' || row.status === 'OVERDUE'"> <v-dropdown-item
<router-link :to="`/admin/payments/${row.id}/create`" class="dropdown-item"> v-if="
<font-awesome-icon :icon="['fas', 'credit-card']" class="dropdown-item-icon"/> row.status === 'SENT' ||
row.status === 'VIEWED' ||
row.status === 'OVERDUE'
"
>
<router-link
:to="`/admin/payments/${row.id}/create`"
class="dropdown-item"
>
<font-awesome-icon
:icon="['fas', 'credit-card']"
class="dropdown-item-icon"
/>
{{ $t('payments.record_payment') }} {{ $t('payments.record_payment') }}
</router-link> </router-link>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item> <v-dropdown-item>
<a class="dropdown-item" href="#/" @click="onCloneInvoice(row.id)"> <a
class="dropdown-item"
href="#/"
@click="onCloneInvoice(row.id)"
>
<font-awesome-icon icon="copy" class="dropdown-item-icon" /> <font-awesome-icon icon="copy" class="dropdown-item-icon" />
{{ $t('invoices.clone_invoice') }} {{ $t('invoices.clone_invoice') }}
</a> </a>
</v-dropdown-item> </v-dropdown-item>
<v-dropdown-item> <v-dropdown-item>
<div class="dropdown-item" @click="removeInvoice(row.id)"> <div class="dropdown-item" @click="removeInvoice(row.id)">
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> <font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-if="invoice" class="main-content invoice-view-page"> <div v-if="invoice" class="main-content invoice-view-page">
<div class="page-header"> <div class="page-header">
<h3 class="page-title"> {{ invoice.invoice_number }}</h3> <h3 class="page-title">{{ invoice.invoice_number }}</h3>
<div class="page-actions row"> <div class="page-actions row">
<div class="col-xs-2 mr-3"> <div class="col-xs-2 mr-3">
<base-button <base-button
@ -24,26 +24,47 @@
> >
{{ $t('invoices.send_invoice') }} {{ $t('invoices.send_invoice') }}
</base-button> </base-button>
<router-link v-if="invoice.status === 'SENT'" :to="`/admin/payments/${$route.params.id}/create`"> <router-link
<base-button v-if="invoice.status === 'SENT'"
color="theme" :to="`/admin/payments/${$route.params.id}/create`"
> >
<base-button color="theme">
{{ $t('payments.record_payment') }} {{ $t('payments.record_payment') }}
</base-button> </base-button>
</router-link> </router-link>
<v-dropdown :close-on-select="false" align="left" class="filter-container"> <v-dropdown
:close-on-select="true"
align="left"
class="filter-container"
>
<a slot="activator" href="#"> <a slot="activator" href="#">
<base-button color="theme"> <base-button color="theme">
<font-awesome-icon icon="ellipsis-h" /> <font-awesome-icon icon="ellipsis-h" />
</base-button> </base-button>
</a> </a>
<v-dropdown-item> <v-dropdown-item>
<router-link :to="{path: `/admin/invoices/${$route.params.id}/edit`}" class="dropdown-item"> <div class="dropdown-item" @click="copyPdfUrl">
<font-awesome-icon :icon="['fas', 'pencil-alt']" class="dropdown-item-icon"/> <font-awesome-icon
:icon="['fas', 'link']"
class="dropdown-item-icon"
/>
{{ $t('general.copy_pdf_url') }}
</div>
<router-link
:to="{ path: `/admin/invoices/${$route.params.id}/edit` }"
class="dropdown-item"
>
<font-awesome-icon
:icon="['fas', 'pencil-alt']"
class="dropdown-item-icon"
/>
{{ $t('general.edit') }} {{ $t('general.edit') }}
</router-link> </router-link>
<div class="dropdown-item" @click="removeInvoice($route.params.id)"> <div class="dropdown-item" @click="removeInvoice($route.params.id)">
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> <font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>
@ -61,14 +82,18 @@
align-icon="right" align-icon="right"
@input="onSearch" @input="onSearch"
/> />
<div <div class="btn-group ml-3" role="group" aria-label="First group">
class="btn-group ml-3" <v-dropdown
role="group" :close-on-select="false"
aria-label="First group" align="left"
> class="filter-container"
<v-dropdown :close-on-select="false" align="left" class="filter-container"> >
<a slot="activator" href="#"> <a slot="activator" href="#">
<base-button class="inv-button inv-filter-fields-btn" color="default" size="medium"> <base-button
class="inv-button inv-filter-fields-btn"
color="default"
size="medium"
>
<font-awesome-icon icon="filter" /> <font-awesome-icon icon="filter" />
</base-button> </base-button>
</a> </a>
@ -84,8 +109,10 @@
class="inv-radio" class="inv-radio"
value="invoice_date" value="invoice_date"
@change="onSearch" @change="onSearch"
> />
<label class="inv-label" for="filter_invoice_date">{{ $t('invoices.invoice_date') }}</label> <label class="inv-label" for="filter_invoice_date">{{
$t('invoices.invoice_date')
}}</label>
</div> </div>
<div class="filter-items"> <div class="filter-items">
<input <input
@ -96,8 +123,10 @@
class="inv-radio" class="inv-radio"
value="due_date" value="due_date"
@change="onSearch" @change="onSearch"
> />
<label class="inv-label" for="filter_due_date">{{ $t('invoices.due_date') }}</label> <label class="inv-label" for="filter_due_date">{{
$t('invoices.due_date')
}}</label>
</div> </div>
<div class="filter-items"> <div class="filter-items">
<input <input
@ -108,11 +137,19 @@
class="inv-radio" class="inv-radio"
value="invoice_number" value="invoice_number"
@change="onSearch" @change="onSearch"
> />
<label class="inv-label" for="filter_invoice_number">{{ $t('invoices.invoice_number') }}</label> <label class="inv-label" for="filter_invoice_number">{{
$t('invoices.invoice_number')
}}</label>
</div> </div>
</v-dropdown> </v-dropdown>
<base-button v-tooltip.top-center="{ content: getOrderName }" class="inv-button inv-filter-sorting-btn" color="default" size="medium" @click="sortData"> <base-button
v-tooltip.top-center="{ content: getOrderName }"
class="inv-button inv-filter-sorting-btn"
color="default"
size="medium"
@click="sortData"
>
<font-awesome-icon v-if="getOrderBy" icon="sort-amount-up" /> <font-awesome-icon v-if="getOrderBy" icon="sort-amount-up" />
<font-awesome-icon v-else icon="sort-amount-down" /> <font-awesome-icon v-else icon="sort-amount-down" />
</base-button> </base-button>
@ -121,7 +158,7 @@
<base-loader v-if="isSearching" /> <base-loader v-if="isSearching" />
<div v-else class="side-content"> <div v-else class="side-content">
<router-link <router-link
v-for="(invoice,index) in invoices" v-for="(invoice, index) in invoices"
:to="`/admin/invoices/${invoice.id}/view`" :to="`/admin/invoices/${invoice.id}/view`"
:key="index" :key="index"
class="side-invoice" class="side-invoice"
@ -129,10 +166,20 @@
<div class="left"> <div class="left">
<div class="inv-name">{{ invoice.user.name }}</div> <div class="inv-name">{{ invoice.user.name }}</div>
<div class="inv-number">{{ invoice.invoice_number }}</div> <div class="inv-number">{{ invoice.invoice_number }}</div>
<div :class="'inv-status-'+invoice.status.toLowerCase()" class="inv-status">{{ invoice.status }}</div> <div
:class="'inv-status-' + invoice.status.toLowerCase()"
class="inv-status"
>
{{ invoice.status }}
</div>
</div> </div>
<div class="right"> <div class="right">
<div class="inv-amount" v-html="$utils.formatMoney(invoice.due_amount, invoice.user.currency)" /> <div
class="inv-amount"
v-html="
$utils.formatMoney(invoice.due_amount, invoice.user.currency)
"
/>
<div class="inv-date">{{ invoice.formattedInvoiceDate }}</div> <div class="inv-date">{{ invoice.formattedInvoiceDate }}</div>
</div> </div>
</router-link> </router-link>
@ -141,8 +188,8 @@
</p> </p>
</div> </div>
</div> </div>
<div class="invoice-view-page-container" > <div class="invoice-view-page-container">
<iframe :src="`${shareableLink}`" class="frame-style"/> <iframe :src="`${shareableLink}`" class="frame-style" />
</div> </div>
</div> </div>
</template> </template>
@ -291,6 +338,14 @@ export default {
} }
}) })
}, },
copyPdfUrl () {
let pdfUrl = `${window.location.origin}/invoices/pdf/${this.invoice.unique_hash}`
let response = this.$utils.copyTextToClipboard(pdfUrl)
window.toastr['success'](this.$tc('Copied PDF url to clipboard!'))
},
async removeInvoice (id) { async removeInvoice (id) {
this.selectInvoice([parseInt(id)]) this.selectInvoice([parseInt(id)])
this.id = id this.id = id

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-if="payment" class="main-content payment-view-page"> <div v-if="payment" class="main-content payment-view-page">
<div class="page-header"> <div class="page-header">
<h3 class="page-title"> {{ payment.payment_number }}</h3> <h3 class="page-title">{{ payment.payment_number }}</h3>
<div class="page-actions row"> <div class="page-actions row">
<base-button <base-button
:loading="isSendingEmail" :loading="isSendingEmail"
@ -11,19 +11,39 @@
> >
{{ $t('payments.send_payment_receipt') }} {{ $t('payments.send_payment_receipt') }}
</base-button> </base-button>
<v-dropdown :close-on-select="false" align="left" class="filter-container"> <v-dropdown
:close-on-select="true"
align="left"
class="filter-container"
>
<a slot="activator" href="#"> <a slot="activator" href="#">
<base-button color="theme"> <base-button color="theme">
<font-awesome-icon icon="ellipsis-h" /> <font-awesome-icon icon="ellipsis-h" />
</base-button> </base-button>
</a> </a>
<v-dropdown-item> <v-dropdown-item>
<router-link :to="{path: `/admin/payments/${$route.params.id}/edit`}" class="dropdown-item"> <div class="dropdown-item" @click="copyPdfUrl">
<font-awesome-icon :icon="['fas', 'pencil-alt']" class="dropdown-item-icon"/> <font-awesome-icon
:icon="['fas', 'link']"
class="dropdown-item-icon"
/>
{{ $t('general.copy_pdf_url') }}
</div>
<router-link
:to="{ path: `/admin/payments/${$route.params.id}/edit` }"
class="dropdown-item"
>
<font-awesome-icon
:icon="['fas', 'pencil-alt']"
class="dropdown-item-icon"
/>
{{ $t('general.edit') }} {{ $t('general.edit') }}
</router-link> </router-link>
<div class="dropdown-item" @click="removePayment($route.params.id)"> <div class="dropdown-item" @click="removePayment($route.params.id)">
<font-awesome-icon :icon="['fas', 'trash']" class="dropdown-item-icon" /> <font-awesome-icon
:icon="['fas', 'trash']"
class="dropdown-item-icon"
/>
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>
</v-dropdown-item> </v-dropdown-item>
@ -41,14 +61,18 @@
align-icon="right" align-icon="right"
@input="onSearch" @input="onSearch"
/> />
<div <div class="btn-group ml-3" role="group" aria-label="First group">
class="btn-group ml-3" <v-dropdown
role="group" :close-on-select="false"
aria-label="First group" align="left"
> class="filter-container"
<v-dropdown :close-on-select="false" align="left" class="filter-container"> >
<a slot="activator" href="#"> <a slot="activator" href="#">
<base-button class="inv-button inv-filter-fields-btn" color="default" size="medium"> <base-button
class="inv-button inv-filter-fields-btn"
color="default"
size="medium"
>
<font-awesome-icon icon="filter" /> <font-awesome-icon icon="filter" />
</base-button> </base-button>
</a> </a>
@ -64,8 +88,10 @@
class="inv-radio" class="inv-radio"
value="invoice_number" value="invoice_number"
@change="onSearch" @change="onSearch"
> />
<label class="inv-label" for="filter_invoice_number">{{ $t('invoices.title') }}</label> <label class="inv-label" for="filter_invoice_number">{{
$t('invoices.title')
}}</label>
</div> </div>
<div class="filter-items"> <div class="filter-items">
<input <input
@ -76,8 +102,10 @@
class="inv-radio" class="inv-radio"
value="payment_date" value="payment_date"
@change="onSearch" @change="onSearch"
> />
<label class="inv-label" for="filter_payment_date">{{ $t('payments.date') }}</label> <label class="inv-label" for="filter_payment_date">{{
$t('payments.date')
}}</label>
</div> </div>
<div class="filter-items"> <div class="filter-items">
<input <input
@ -88,11 +116,19 @@
class="inv-radio" class="inv-radio"
value="payment_number" value="payment_number"
@change="onSearch" @change="onSearch"
> />
<label class="inv-label" for="filter_payment_number">{{ $t('payments.payment_number') }}</label> <label class="inv-label" for="filter_payment_number">{{
$t('payments.payment_number')
}}</label>
</div> </div>
</v-dropdown> </v-dropdown>
<base-button v-tooltip.top-center="{ content: getOrderName }" class="inv-button inv-filter-sorting-btn" color="default" size="medium" @click="sortData"> <base-button
v-tooltip.top-center="{ content: getOrderName }"
class="inv-button inv-filter-sorting-btn"
color="default"
size="medium"
@click="sortData"
>
<font-awesome-icon v-if="getOrderBy" icon="sort-amount-up" /> <font-awesome-icon v-if="getOrderBy" icon="sort-amount-up" />
<font-awesome-icon v-else icon="sort-amount-down" /> <font-awesome-icon v-else icon="sort-amount-down" />
</base-button> </base-button>
@ -101,7 +137,7 @@
<base-loader v-if="isSearching" /> <base-loader v-if="isSearching" />
<div v-else class="side-content"> <div v-else class="side-content">
<router-link <router-link
v-for="(payment,index) in payments" v-for="(payment, index) in payments"
:to="`/admin/payments/${payment.id}/view`" :to="`/admin/payments/${payment.id}/view`"
:key="index" :key="index"
class="side-payment" class="side-payment"
@ -112,7 +148,10 @@
<div class="inv-number">{{ payment.invoice_number }}</div> <div class="inv-number">{{ payment.invoice_number }}</div>
</div> </div>
<div class="right"> <div class="right">
<div class="inv-amount" v-html="$utils.formatMoney(payment.amount, payment.user.currency)" /> <div
class="inv-amount"
v-html="$utils.formatMoney(payment.amount, payment.user.currency)"
/>
<div class="inv-date">{{ payment.formattedPaymentDate }}</div> <div class="inv-date">{{ payment.formattedPaymentDate }}</div>
<!-- <div class="inv-number">{{ payment.payment_method.name }}</div> --> <!-- <div class="inv-number">{{ payment.payment_method.name }}</div> -->
</div> </div>
@ -122,8 +161,8 @@
</p> </p>
</div> </div>
</div> </div>
<div class="payment-view-page-container" > <div class="payment-view-page-container">
<iframe :src="`${shareableLink}`" class="frame-style"/> <iframe :src="`${shareableLink}`" class="frame-style" />
</div> </div>
</div> </div>
</template> </template>
@ -260,6 +299,13 @@ export default {
} }
}) })
}, },
copyPdfUrl () {
let pdfUrl = `${window.location.origin}/payments/pdf/${this.payment.unique_hash}`
let response = this.$utils.copyTextToClipboard(pdfUrl)
window.toastr['success'](this.$tc('Copied PDF url to clipboard!'))
},
async removePayment (id) { async removePayment (id) {
this.id = id this.id = id
window.swal({ window.swal({

View File

@ -54,7 +54,8 @@ import {
faEyeSlash, faEyeSlash,
faSyncAlt, faSyncAlt,
faRocket, faRocket,
faCamera faCamera,
faLink,
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { far } from '@fortawesome/free-regular-svg-icons' import { far } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
@ -119,7 +120,8 @@ library.add(
faPaperPlane, faPaperPlane,
faSyncAlt, faSyncAlt,
faRocket, faRocket,
faCamera faCamera,
faLink
) )
Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.component('font-awesome-icon', FontAwesomeIcon)