1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00

Fixes for showing signatures / terms

This commit is contained in:
David Bomba 2023-07-16 12:40:32 +10:00
parent 4bf6cb622b
commit 8e65026508
18 changed files with 185 additions and 108 deletions

View File

@ -77,6 +77,7 @@ var Payment = /*#__PURE__*/function () {
}, {
key: "displaySignature",
value: function displaySignature() {
document.getElementById("signature-next-step").disabled = true;
var displaySignatureModal = document.getElementById("displaySignatureModal");
displaySignatureModal.removeAttribute("style");
var signaturePad = new SignaturePad(document.getElementById("signature-pad"), {
@ -91,7 +92,6 @@ var Payment = /*#__PURE__*/function () {
key: "handle",
value: function handle() {
var _this2 = this;
document.getElementById("signature-next-step").disabled = true;
document.querySelectorAll(".dropdown-gateway-button").forEach(function (element) {
element.addEventListener("click", function () {
if (!_this2.submitting) {

View File

@ -54,7 +54,12 @@ var Accept = /*#__PURE__*/function () {
value: function handle() {
var _this = this;
document.getElementById("signature-next-step").disabled = true;
document.getElementById("close-button").addEventListener('click', function () {
var approveButton = document.getElementById("approve-button");
if (approveButton) approveButton.disabled = false;
});
document.getElementById('approve-button').addEventListener('click', function () {
console.log("accepted ");
if (_this.shouldDisplaySignature && _this.shouldDisplayTerms) {
_this.displaySignature();
document.getElementById('signature-next-step').addEventListener('click', function () {

View File

@ -61,12 +61,14 @@ var Approve = /*#__PURE__*/function () {
value: function handle() {
var _this = this;
document.getElementById("signature-next-step").disabled = true;
document.getElementById("close_button").addEventListener('click', function () {
document.getElementById("close-button").addEventListener('click', function () {
var approveButton = document.getElementById("approve-button");
console.log('close button');
if (approveButton) approveButton.disabled = false;
});
document.getElementById("hide_close").addEventListener('click', function () {
document.getElementById("close-terms-button").addEventListener('click', function () {
var approveButton = document.getElementById("approve-button");
console.log('close terms-button');
if (approveButton) approveButton.disabled = false;
});
document.getElementById('approve-button').addEventListener('click', function () {

View File

@ -9,13 +9,13 @@
"/js/clients/payments/stripe-bacs.js": "/js/clients/payments/stripe-bacs.js?id=a82927936510bbecc60de3e9742f9739",
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=b7601721afa9599518ba894a0f88f947",
"/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js?id=b9c510dffa8420879983f04cd521564c",
"/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=d13b4a7934c9f7ddf2154f8f233843f7",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=1bd5f983e7d9394fe9ac603dea94e901",
"/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=e9040b9f5e9bbf5d04b8a02a284d0481",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=edeab1566d21cb93901d571bdcbb7c30",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=f0252b9b140b667794c252ae200cd844",
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=360b39d82a1a7dd90a8351776fbd9edb",
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=a2168c43060a7de40da20b5fc599bcab",
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=4158693089b29ee8e13cb7d9ce4480a9",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=4e596cec23cdd6487534e6ed5499d791",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=4091846cf28ded010380cc2678fbe831",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=bfa116c1df42c641bc7a3ff4fa8d50dd",
"/js/setup/setup.js": "/js/setup/setup.js?id=086b9e114b0b9ee01f909d686f489162",
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=cf50b5ba1fcd1d184bf0c10d710672c8",

View File

@ -84,6 +84,8 @@ class Payment {
}
displaySignature() {
document.getElementById("signature-next-step").disabled = true;
let displaySignatureModal = document.getElementById(
"displaySignatureModal"
);
@ -104,7 +106,6 @@ class Payment {
}
handle() {
document.getElementById("signature-next-step").disabled = true;
document
.querySelectorAll(".dropdown-gateway-button")

View File

@ -48,9 +48,20 @@ class Accept {
document.getElementById("signature-next-step").disabled = true;
document.getElementById("close-button").addEventListener('click', () => {
const approveButton = document.getElementById("approve-button");
if (approveButton)
approveButton.disabled = false;
});
document
.getElementById('approve-button')
.addEventListener('click', () => {
console.log("accepted ");
if (this.shouldDisplaySignature && this.shouldDisplayTerms) {
this.displaySignature();

View File

@ -55,22 +55,27 @@ class Approve {
document.getElementById("signature-next-step").disabled = true;
document.getElementById("close_button").addEventListener('click', () => {
document.getElementById("close-button").addEventListener('click', () => {
const approveButton = document.getElementById("approve-button");
console.log('close button');
if(approveButton)
approveButton.disabled = false;
});
document.getElementById("hide_close").addEventListener('click', () => {
document.getElementById("close-terms-button").addEventListener('click', () => {
const approveButton = document.getElementById("approve-button");
if(approveButton)
console.log('close terms-button');
if (approveButton)
approveButton.disabled = false;
});
document
.getElementById('approve-button')
.addEventListener('click', () => {

View File

@ -1,4 +1,4 @@
@push('head')
<style>
table, th, td {
@ -26,6 +26,7 @@ span {
}
</style>
@endpush
<div class="w-full bg-white py-3 border-2 shadow sm:rounded-lg">
@ -43,7 +44,6 @@ span {
</div>
<div id="user-details" class="mt-3 px-3 border-b-2 border-fuschia-600 flex flex-col items-end">
<div x-data="{ show_user: false }" class="mb-3">
@ -182,10 +182,10 @@ span {
{{ strip_tags($entity->footer) }}
</div>
</div>
@endif
@push('head')
<script>
document.addEventListener('DOMContentLoaded', () => {
@ -232,3 +232,4 @@ span {
});
</script>
@endpush

View File

@ -1,64 +1,64 @@
<div>
<div class="flex flex-col items-end mb-2">
<button wire:loading.attr="disabled" wire:click="downloadPdf" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-2 rounded inline-flex">
<span>{{ ctrans('texts.download_pdf') }}</span>
<div wire:loading wire:target="downloadPdf">
<svg class="animate-spin h-5 w-5 text-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<div class="flex flex-col items-end mb-2">
<button wire:loading.attr="disabled" wire:click="downloadPdf" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-2 rounded inline-flex">
<span>{{ ctrans('texts.download_pdf') }}</span>
<div wire:loading wire:target="downloadPdf">
<svg class="animate-spin h-5 w-5 text-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</button>
</div>
<div class="hidden lg:block">
<div wire:init="getPdf()">
@if($pdf)
<!-- <iframe id="pdf-iframe" src="{!! $pdf !!}" class="h-screen w-full border-0 mt-4"></iframe> -->
<iframe id="pdf-iframe" src="/{{ $route_entity }}/showBlob/{{ $pdf }}" class="h-screen w-full border-0 mt-4"></iframe>
@else
<div class="flex mt-4 place-items-center">
<span class="loader m-auto"></span>
<style type="text/css">
.loader {
width: 48px;
height: 48px;
border-radius: 50%;
position: relative;
animation: rotate 1s linear infinite
}
.loader::before , .loader::after {
content: "";
box-sizing: border-box;
position: absolute;
inset: 0px;
border-radius: 50%;
border: 5px solid #454545;
animation: prixClipFix 2s linear infinite ;
}
.loader::after{
border-color: #FF3D00;
animation: prixClipFix 2s linear infinite , rotate 0.5s linear infinite reverse;
inset: 6px;
}
@keyframes rotate {
0% {transform: rotate(0deg)}
100% {transform: rotate(360deg)}
}
@keyframes prixClipFix {
0% {clip-path:polygon(50% 50%,0 0,0 0,0 0,0 0,0 0)}
25% {clip-path:polygon(50% 50%,0 0,100% 0,100% 0,100% 0,100% 0)}
50% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,100% 100%,100% 100%)}
75% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,0 100%,0 100%)}
100% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,0 100%,0 0)}
}
</style>
</div>
@endif
</div>
</button>
</div>
<div class="hidden lg:block">
<div wire:init="getPdf()">
@if($pdf)
<!-- <iframe id="pdf-iframe" src="{!! $pdf !!}" class="h-screen w-full border-0 mt-4"></iframe> -->
<iframe id="pdf-iframe" src="/{{ $route_entity }}/showBlob/{{ $pdf }}" class="h-screen w-full border-0 mt-4"></iframe>
@else
<div class="flex mt-4 place-items-center">
<span class="loader m-auto"></span>
<style type="text/css">
.loader {
width: 48px;
height: 48px;
border-radius: 50%;
position: relative;
animation: rotate 1s linear infinite
}
.loader::before , .loader::after {
content: "";
box-sizing: border-box;
position: absolute;
inset: 0px;
border-radius: 50%;
border: 5px solid #454545;
animation: prixClipFix 2s linear infinite ;
}
.loader::after{
border-color: #FF3D00;
animation: prixClipFix 2s linear infinite , rotate 0.5s linear infinite reverse;
inset: 6px;
}
@keyframes rotate {
0% {transform: rotate(0deg)}
100% {transform: rotate(360deg)}
}
@keyframes prixClipFix {
0% {clip-path:polygon(50% 50%,0 0,0 0,0 0,0 0,0 0)}
25% {clip-path:polygon(50% 50%,0 0,100% 0,100% 0,100% 0,100% 0)}
50% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,100% 100%,100% 100%)}
75% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,0 100%,0 100%)}
100% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,0 100%,0 0)}
}
</style>
</div>
@endif
</div>
</div>
</div>
<div class="block lg:hidden">
@include('portal.ninja2020.components.html-viewer')
</div>
<div class="block lg:hidden">
@include('portal.ninja2020.components.html-viewer')
</div>
</div>

View File

@ -56,14 +56,14 @@
</section>
</div>
@if($mobile)
<!-- @if($mobile)
<div class="w-full h-full overflow-auto mt-4">
<canvas id="pdf-placeholder" class="shadow rounded-lg bg-white"></canvas>
</div>
@else
@else -->
@livewire('pdf-slot', ['entity' => $entity, 'invitation' => $invitation, 'db' => $invitation->company->db])
<!-- <iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}" class="h-screen w-full border-0 mt-4"></iframe> -->
@endif
<!-- @endif -->
@if($mobile)

View File

@ -32,8 +32,7 @@
</div>
@include('portal.ninja2020.components.entity-documents', ['entity' => $credit])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $credit, 'invitation' => $invitation])
@livewire('pdf-slot', ['entity' => $credit, 'invitation' => $invitation, 'db' => $invitation->company->db])
@endsection

View File

@ -1,4 +1,4 @@
<div style="display: none;" id="displaySignatureModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center">
<div style="display: none;" id="displaySignatureModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-data>
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
@ -21,14 +21,14 @@
</div>
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse" >
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto" x-data>
<button type="button" id="signature-next-step" class="button button-primary bg-primary" @click="document.getElementById('displaySignatureModal').style.display = 'none';">
{{ ctrans('texts.next_step') }}
</button>
</div>
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" id="close_button">
<button @click="document.getElementById('displaySignatureModal').style.display = 'none';" type="button" class="button button-secondary">
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" x-data>
<button @click="document.getElementById('displaySignatureModal').style.display = 'none';" type="button" class="button button-secondary" id="close-button">
{{ ctrans('texts.close') }}
</button>
</div>

View File

@ -1,4 +1,4 @@
<div style="display: none;" id="displayTermsModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center">
<div style="display: none;" id="displayTermsModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-data>
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
@ -24,7 +24,7 @@
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto" x-data>
<button
type="button"
id="accept-terms-button"
@ -33,8 +33,8 @@
{{ ctrans('texts.i_agree') }}
</button>
</div>
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" id="hide_close">
<button @click="document.getElementById('displayTermsModal').style.display = 'none';" type="button" class="button button-secondary">
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" x-data>
<button @click="document.getElementById('displayTermsModal').style.display = 'none';" type="button" class="button button-secondary" id="close-terms-button">
{{ ctrans('texts.close') }}
</button>
</div>

View File

@ -5,7 +5,6 @@
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
@include('portal.ninja2020.components.no-cache')
<script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script>
@endpush
@ -96,18 +95,26 @@
@endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $invoice])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $invoice, 'invitation' => $invitation])
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$invoice], 'entity_type' => ctrans('texts.invoice')])
@include('portal.ninja2020.invoices.includes.signature')
@livewire('pdf-slot', ['entity' => $invoice, 'invitation' => $invitation, 'db' => $invitation->company->db])
@endsection
@section('footer')
<script src="{{ asset('js/clients/invoices/payment.js') }}"></script>
<script src="{{ asset('vendor/clipboard.min.js') }}"></script>
@include('portal.ninja2020.invoices.includes.signature')
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$invoice], 'entity_type' => ctrans('texts.invoice')])
@endsection
@push('head')
<script src="{{ asset('js/clients/invoices/payment.js') }}" defer></script>
<script src="{{ asset('vendor/clipboard.min.js') }}" defer></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
var clipboard = new ClipboardJS('.btn');
});
</script>
@endsection
@endpush

View File

@ -46,19 +46,27 @@
@endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $purchase_order])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $purchase_order, 'invitation' => $invitation])
@livewire('pdf-slot', ['entity' => $purchase_order, 'invitation' => $invitation, 'db' => $invitation->company->db])
@endsection
@section('footer')
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$purchase_order], 'entity_type' => ctrans('texts.purchase_order')])
@include('portal.ninja2020.invoices.includes.signature')
@endsection
@section('footer')
<script src="{{ asset('js/clients/purchase_orders/accept.js') }}"></script>
<script src="{{ asset('vendor/clipboard.min.js') }}"></script>
@push('head')
<script src="{{ asset('js/clients/purchase_orders/accept.js') }}" defer></script>
<script src="{{ asset('vendor/clipboard.min.js') }}" defer></script>
<script type="text/javascript">
var clipboard = new ClipboardJS('.btn');
document.addEventListener('DOMContentLoaded', () => {
var clipboard = new ClipboardJS('.btn');
});
</script>
@endsection
@endpush

View File

@ -1,5 +1,5 @@
<div style="display: none;" id="displaySignatureModal"
class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center">
class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-data>
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"

View File

@ -106,19 +106,27 @@
@endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $quote])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $quote, 'invitation' => $invitation])
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$quote], 'entity_type' => ctrans('texts.quote')])
@include('portal.ninja2020.invoices.includes.signature')
@include('portal.ninja2020.quotes.includes.user-input')
@livewire('pdf-slot', ['entity' => $quote, 'invitation' => $invitation, 'db' => $invitation->company->db])
@endsection
@section('footer')
<script src="{{ asset('js/clients/quotes/approve.js') }}"></script>
<script src="{{ asset('vendor/clipboard.min.js') }}"></script>
@include('portal.ninja2020.quotes.includes.user-input')
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$quote], 'entity_type' => ctrans('texts.quote')])
@include('portal.ninja2020.invoices.includes.signature')
@endsection
<script type="text/javascript">
@push('head')
<script src="{{ asset('js/clients/quotes/approve.js') }}" defer></script>
<script src="{{ asset('vendor/clipboard.min.js') }}" defer></script>
<script type="text/javascript" defer>
document.addEventListener('DOMContentLoaded', () => {
var clipboard = new ClipboardJS('.btn');
});
</script>
@endsection
@endpush

View File

@ -226,4 +226,34 @@ class ReportCsvGenerationTest extends TestCase
}
public function testCreditCsvGeneration()
{
Credit::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
'amount' => 100,
'balance' => 50,
'status_id' => 2,
'discount' => 10,
'po_number' => '1234',
'public_notes' => 'Public',
'private_notes' => 'Private',
'terms' => 'Terms',
]);
$data = [
'date_range' => 'all',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/credits', $data);
}