Merge pull request #6686 from beganovich/v5-statements-json-fixes

Client portal: Statements page in pure Javascript
This commit is contained in:
David Bomba 2021-09-21 22:20:26 +10:00 committed by GitHub
commit 289c4952a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 95 deletions

View File

@ -1,49 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Livewire;
use Illuminate\View\View;
use Livewire\Component;
class Statement extends Component
{
public string $url;
public array $options = [
'show_payments_table' => 0,
'show_aging_table' => 0,
];
public function mount(): void
{
$this->options['start_date'] = now()->startOfYear()->format('Y-m-d');
$this->options['end_date'] = now()->format('Y-m-d');
}
protected function getCurrentUrl(): string
{
return route('client.statement.raw', $this->options);
}
public function download()
{
return redirect()->route('client.statement.raw', \array_merge($this->options, ['download' => 1]));
}
public function render(): View
{
$this->url = route('client.statement.raw', $this->options);
return render('components.statement');
}
}

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/clients/statements/view.js vendored Normal file
View File

@ -0,0 +1,2 @@
/*! For license information please see view.js.LICENSE.txt */
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var a=t[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,n),a.l=!0,a.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(r,a,function(t){return e[t]}.bind(null,a));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=26)}({26:function(e,t,n){e.exports=n("LcZE")},LcZE:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.url=new URL(document.querySelector("meta[name=pdf-url]").content),this.startDate="",this.endDate="",this.showPaymentsTable=!1,this.showAgingTable=!1}var t,r,a;return t=e,(r=[{key:"bindEventListeners",value:function(){var e=this;["#date-from","#date-to","#show-payments-table","#show-aging-table"].forEach((function(t){document.querySelector(t).addEventListener("change",(function(t){return e.handleValueChange(t)}))}))}},{key:"handleValueChange",value:function(e){"checkbox"===e.target.type?this[e.target.dataset.field]=e.target.checked:this[e.target.dataset.field]=e.target.value,this.updatePdf()}},{key:"composedUrl",get:function(){return this.url.search="",this.startDate.length>0&&this.url.searchParams.append("start_date",this.startDate),this.endDate.length>0&&this.url.searchParams.append("end_date",this.endDate),this.url.searchParams.append("show_payments_table",+this.showPaymentsTable),this.url.searchParams.append("show_aging_table",+this.showAgingTable),this.url.href}},{key:"updatePdf",value:function(){document.querySelector("meta[name=pdf-url]").content=this.composedUrl;var e=document.querySelector("#pdf-iframe");e&&(e.src=this.composedUrl),document.querySelector("meta[name=pdf-url]").dispatchEvent(new Event("change"))}},{key:"handle",value:function(){this.bindEventListeners()}}])&&n(t.prototype,r),a&&n(t,a),e}())).handle()}});

View File

@ -0,0 +1,9 @@
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@ -1,6 +1,6 @@
{ {
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5", "/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
"/css/app.css": "/css/app.css?id=46021f35ee55aca9ff20", "/css/app.css": "/css/app.css?id=08bae341ed680d6ba544",
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4", "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1", "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7", "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",
@ -24,7 +24,8 @@
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa", "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12", "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45", "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fc3055d6a099f523ea98", "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=2a99d83305ba87bfa6cc",
"/js/clients/statements/view.js": "/js/clients/statements/view.js?id=9efa4bfa2d5600cded6a",
"/js/setup/setup.js": "/js/setup/setup.js?id=8d454e7090f119552a6c", "/js/setup/setup.js": "/js/setup/setup.js?id=8d454e7090f119552a6c",
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad" "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
} }

View File

@ -80,6 +80,15 @@ class PDF {
.getElementById('zoom-out') .getElementById('zoom-out')
.addEventListener('click', () => this.handleZoomChange()); .addEventListener('click', () => this.handleZoomChange());
document
.querySelector('meta[name=pdf-url]')
.addEventListener('change', () => {
this.canvas.getContext('2d').clearRect(0, 0, this.canvas.width, this.canvas.height);
this.url = document.querySelector("meta[name='pdf-url']").content;
this.handle();
})
return this; return this;
} }

86
resources/js/clients/statements/view.js vendored Normal file
View File

@ -0,0 +1,86 @@
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
class Statement {
constructor() {
this.url = new URL(
document.querySelector('meta[name=pdf-url]').content
);
this.startDate = '';
this.endDate = '';
this.showPaymentsTable = false;
this.showAgingTable = false;
}
bindEventListeners() {
[
'#date-from',
'#date-to',
'#show-payments-table',
'#show-aging-table',
].forEach((selector) => {
document
.querySelector(selector)
.addEventListener('change', (event) =>
this.handleValueChange(event)
);
});
}
handleValueChange(event) {
if (event.target.type === 'checkbox') {
this[event.target.dataset.field] = event.target.checked;
} else {
this[event.target.dataset.field] = event.target.value;
}
this.updatePdf();
}
get composedUrl() {
this.url.search = '';
if (this.startDate.length > 0) {
this.url.searchParams.append('start_date', this.startDate);
}
if (this.endDate.length > 0) {
this.url.searchParams.append('end_date', this.endDate);
}
this.url.searchParams.append(
'show_payments_table',
+this.showPaymentsTable
);
this.url.searchParams.append('show_aging_table', +this.showAgingTable);
return this.url.href;
}
updatePdf() {
document.querySelector('meta[name=pdf-url]').content = this.composedUrl;
let iframe = document.querySelector('#pdf-iframe');
if (iframe) {
iframe.src = this.composedUrl;
}
document
.querySelector('meta[name=pdf-url]')
.dispatchEvent(new Event('change'));
}
handle() {
this.bindEventListeners();
}
}
new Statement().handle();

View File

@ -86,7 +86,7 @@
<canvas id="pdf-placeholder" class="shadow rounded-lg bg-white mt-4 p-4"></canvas> <canvas id="pdf-placeholder" class="shadow rounded-lg bg-white mt-4 p-4"></canvas>
</div> </div>
@else @else
<iframe src="{{ $url ?? $entity->pdf_file_path(null, 'url', true) }}" class="h-screen w-full border-0 mt-4"></iframe> <iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path(null, 'url', true) }}" class="h-screen w-full border-0 mt-4"></iframe>
@endif @endif

View File

@ -1,39 +0,0 @@
<div>
<div class="flex flex-col md:flex-row md:justify-between">
<div class="flex flex-col md:flex-row md:items-center">
{{-- <label for="status" class="flex items-center mr-4">
<span class="mr-2">{{ ctrans('texts.status') }}</span>
<select class="input">
<option value="all">{{ ctrans('texts.all') }}</option>
<option value="unpaid">{{ ctrans('texts.unpaid') }}</option>
<option value="paid">{{ ctrans('texts.paid') }}</option>
</select>
</label> <!-- End status dropdown --> --}}
<div class="flex">
<label for="from" class="block w-full flex items-center mr-4">
<span class="mr-2">{{ ctrans('texts.from') }}:</span>
<input wire:model="options.start_date" type="date" class="input w-full">
</label>
<label for="to" class="block w-full flex items-center mr-4">
<span class="mr-2">{{ ctrans('texts.to') }}:</span>
<input wire:model="options.end_date" type="date" class="input w-full">
</label>
</div> <!-- End date range -->
<label for="show_payments" class="block flex items-center mr-4 mt-4 md:mt-0">
<input wire:model="options.show_payments_table" type="checkbox" class="form-checkbox" autocomplete="off">
<span class="ml-2">{{ ctrans('texts.show_payments') }}</span>
</label> <!-- End show payments checkbox -->
<label for="show_aging" class="block flex items-center">
<input wire:model="options.show_aging_table" type="checkbox" class="form-checkbox" autocomplete="off">
<span class="ml-2">{{ ctrans('texts.show_aging') }}</span>
</label> <!-- End show aging checkbox -->
</div>
<button wire:click="download" class="button button-primary bg-primary mt-4 md:mt-0">{{ ctrans('texts.download') }}</button>
</div>
@include('portal.ninja2020.components.pdf-viewer', ['url' => $url])
</div>

View File

@ -2,6 +2,41 @@
@section('meta_title', ctrans('texts.statement')) @section('meta_title', ctrans('texts.statement'))
@push('head')
<meta name="pdf-url" content="{{ route('client.statement.raw') }}">
@endpush
@section('body') @section('body')
@livewire('statement') <div class="flex flex-col md:flex-row md:justify-between">
<div class="flex flex-col md:flex-row md:items-center">
<div class="flex">
<label for="from" class="block w-full flex items-center mr-4">
<span class="mr-2">{{ ctrans('texts.from') }}:</span>
<input id="date-from" type="date" class="input w-full" data-field="startDate" value="{{ now()->startOfYear()->format('Y-m-d') }}">
</label>
<label for="to" class="block w-full flex items-center mr-4">
<span class="mr-2">{{ ctrans('texts.to') }}:</span>
<input id="date-to" type="date" class="input w-full" data-field="endDate" value="{{ now()->format('Y-m-d') }}">
</label>
</div> <!-- End date range -->
<label for="show_payments" class="block flex items-center mr-4 mt-4 md:mt-0">
<input id="show-payments-table" type="checkbox" data-field="showPaymentsTable" class="form-checkbox" autocomplete="off">
<span class="ml-2">{{ ctrans('texts.show_payments') }}</span>
</label> <!-- End show payments checkbox -->
<label for="show_aging" class="block flex items-center">
<input id="show-aging-table" type="checkbox" data-field="showAgingTable" class="form-checkbox" autocomplete="off">
<span class="ml-2">{{ ctrans('texts.show_aging') }}</span>
</label> <!-- End show aging checkbox -->
</div>
<button class="button button-primary bg-primary mt-4 md:mt-0">{{ ctrans('texts.download') }}</button>
</div>
@include('portal.ninja2020.components.pdf-viewer', ['url' => route('client.statement.raw')])
@endsection @endsection
@push('footer')
<script src="{{ asset('js/clients/statements/view.js') }}"></script>
@endpush

6
webpack.mix.js vendored
View File

@ -101,7 +101,11 @@ mix.js("resources/js/app.js", "public/js")
.js( .js(
"resources/js/clients/payments/square-credit-card.js", "resources/js/clients/payments/square-credit-card.js",
"public/js/clients/payments/square-credit-card.js" "public/js/clients/payments/square-credit-card.js"
); )
.js(
"resources/js/clients/statements/view.js",
"public/js/clients/statements/view.js",
)
mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css'); mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css');