Working on html client portal

This commit is contained in:
David Bomba 2023-07-11 21:16:15 +10:00
parent f37620300b
commit 22bbbf26fb
4 changed files with 330 additions and 91 deletions

View File

@ -136,8 +136,11 @@ class InvitationController extends Controller
} else {
$is_silent = 'true';
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent]);
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
}
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})]);
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
}

View File

@ -12,14 +12,20 @@
namespace App\Http\Livewire;
use App\Utils\Number;
use Livewire\Component;
use App\Utils\HtmlEngine;
use App\Libraries\MultiDB;
use App\Models\QuoteInvitation;
use App\Utils\VendorHtmlEngine;
use App\Models\CreditInvitation;
use App\Services\Pdf\PdfBuilder;
use App\Services\Pdf\PdfService;
use App\Models\InvoiceInvitation;
use App\Services\Pdf\PdfDesigner;
use App\Services\Pdf\PdfConfiguration;
use App\Models\PurchaseOrderInvitation;
use App\Models\RecurringInvoiceInvitation;
class PdfSlot extends Component
{
@ -33,113 +39,178 @@ class PdfSlot extends Component
public $url;
private $settings;
private $html_variables;
private $entity_type;
public function mount()
{
MultiDB::setDb($this->db);
}
public function getPdf()
{
// $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation);
}
public function render()
{
$this->entity_type = $this->resolveEntityType();
$this->settings = $this->entity->client ? $this->entity->client->getMergedSettings() : $this->entity->company->settings;
$this->html_variables = $this->entity->client ?
(new HtmlEngine($this->invitation))->generateLabelsAndValues() :
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
return render('components.livewire.pdf-slot', [
'invitation' => $this->invitation,
'entity' => $this->entity,
'data' => $this->invitation->company->settings
'data' => $this->invitation->company->settings,
'entity_type' => $this->entity_type,
'products' => $this->getProducts(),
'services' => $this->getServices(),
'amount' => Number::formatMoney($this->entity->amount, $this->entity->client ?: $this->entity->vendor),
'balance' => Number::formatMoney($this->entity->balance, $this->entity->client ?: $this->entity->vendor),
'company_details' => $this->getCompanyDetails(),
'company_address' => $this->getCompanyAddress(),
'entity_details' => $this->getEntityDetails(),
'user_details' => $this->getUserDetails(),
]);
}
public function getPdf()
private function convertVariables($string): string
{
// $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation);
$html = strtr($string, $this->html_variables['labels']);
$html = strtr($html, $this->html_variables['values']);
return $html;
}
public function getHtml()
private function getCompanyAddress()
{
$pdf_service = new PdfService($this->invitation);
$pdf_service->config = (new PdfConfiguration($pdf_service))->init();
$pdf_service->html_variables = $pdf_service->config->client ?
(new HtmlEngine($this->invitation))->generateLabelsAndValues() :
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
}
$company_address = "";
public function getHtmlX()
{
$pdf_service = new PdfService($this->invitation);
$pdf_service->config = (new PdfConfiguration($pdf_service))->init();
$pdf_service->html_variables = $pdf_service->config->client ?
(new HtmlEngine($this->invitation))->generateLabelsAndValues() :
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
$pdf_service->designer = (new PdfDesigner($pdf_service));
$pdf_service->builder = (new PdfBuilder($pdf_service));
$data = [];
foreach(['company-details', 'company-address','client-details','entity-details','product-table','table-totals'] as $item) {
$pdf_service->designer->template = '<div id="'.$item.'"></div>';
match($item){
'company-details' => $block = $pdf_service->builder->companyDetails(),
'company-address' => $block = $pdf_service->builder->companyAddress(),
'client-details' => $block = $pdf_service->builder->clientDetails(),
'entity-details' => $block = $pdf_service->builder->invoiceDetails(),
'product-table' => $block = $this->productTable(),
'table-totals' => $block = $pdf_service->builder->getTableTotals(),
default => $block = [],
};
$section = [
$item => [
'id' => $item,
'elements' => $block,
]
];
$document = new \DOMDocument();
$document->validateOnParse = true;
@$document->loadHTML(mb_convert_encoding($pdf_service->designer->template, 'HTML-ENTITIES', 'UTF-8'));
$pdf_service->builder->document = $document;
$pdf_service->builder->sections = $section;
$html = $pdf_service->builder
->getEmptyElements()
->updateElementProperties()
->updateVariables();
// $pdf_service->builder->document->removeChild($pdf_service->builder->document->doctype);
// $pdf_service->builder->document->replaceChild($pdf_service->builder->document->firstChild->firstChild->firstChild, $pdf_service->builder->document->firstChild);
$data[$item] = $pdf_service->builder->document->saveHTML();
$section = [];
foreach($this->settings->pdf_variables->company_address as $variable) {
$company_address .= "<p>{$variable}</p>";
}
// nlog($pdf_service->builder->document->saveHTML());
nlog($data);
return $data;
}
private function productTable()
{
return $this->convertVariables($company_address);
}
private function sectionBuilder($tag)
private function getCompanyDetails()
{
$company_details = "";
foreach($this->settings->pdf_variables->company_details as $variable) {
$company_details .= "<p>{$variable}</p>";
}
return $this->convertVariables($company_details);
}
private function getEntityDetails()
{
$entity_details = "<dl class=''>";
if($this->entity_type == 'invoice' || $this->entity_type == 'recurring_invoice') {
foreach($this->settings->pdf_variables->invoice_details as $variable)
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
}
elseif($this->entity_type == 'quote'){
foreach($this->settings->pdf_variables->quote_details as $variable)
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
}
elseif($this->entity_type == 'credit') {
foreach($this->settings->pdf_variables->credit_details as $variable)
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
}
elseif($this->entity_type == 'purchase_order'){
foreach($this->settings->pdf_variables->purchase_order_details as $variable)
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
}
$entity_details .= "</dl>";
return $this->convertVariables($entity_details);
}
private function getUserDetails()
{
$user_details = "";
if($this->entity_type == 'purchase_order') {
foreach($this->settings->pdf_variables->vendor_details as $variable) {
$user_details .= "<p>{$variable}</p>";
}
}
else{
foreach($this->settings->pdf_variables->client_details as $variable) {
$user_details .= "<p>{$variable}</p>";
}
}
return $this->convertVariables($user_details);
}
private function getProducts()
{
$product_items = collect($this->entity->line_items)->filter(function ($item) {
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
})->map(function ($item){
return [
'quantity' => $item->quantity,
'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor),
'notes' => $item->notes,
'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor),
];
});
return $product_items;
}
private function getServices()
{
$task_items = collect($this->entity->line_items)->filter(function ($item) {
return $item->type_id == 2;
})->map(function ($item){
return [
'quantity' => $item->quantity,
'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor),
'notes' => $item->notes,
'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor),
];
});
return $task_items;
}
private function resolveEntityType() :string
{
if ($this->invitation instanceof InvoiceInvitation) {
return 'invoice';
} elseif ($this->invitation instanceof QuoteInvitation) {
return 'quote';
} elseif ($this->invitation instanceof CreditInvitation) {
return 'credit';
} elseif ($this->invitation instanceof RecurringInvoiceInvitation) {
return 'recurring_invoice';
} elseif ($this->invitation instanceof PurchaseOrderInvitation) {
return 'purchase_order';
}
return '';
}
}

View File

@ -1,11 +1,176 @@
<div class="flex flex-col w-full">
<div>
<dl>
@foreach($data->pdf_variables->company_details as $cd)
<dd>{{ $cd }}</dd>
@endforeach
</dl>
</div>
</div>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
td
{
max-width: 1px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
span {
display: block;
padding: 3px;
margin-right:10px;
}
.icon {
display: inline-block;
vertical-align: middle;
}
</style>
<div class="w-full">
<div class="flex flex-row content-center border-fuchsia-600 border-b-2">
<div id="company-details" class="mx-auto">
{!! $company_details !!}
</div>
<div id="company-address" class="mx-auto">
{!! $company_address !!}
</div>
</div>
<div> {!! $entity_details !!}</div>
<div id="user-details">
{!! $user_details !!}
</div>
@if($products->count() > 0)
<div id="product-details" class="py-6">
<table width="100%">
<thead>
<tr>
<th style="text-align:left; width:70%; padding-left:2px;">Item</th>
<th style="text-align:right; width:30%; padding-right:2px;">Amount</th>
</tr>
</thead>
<tbody>
@foreach($products as $product)
<tr style="display: table-row;">
<td>
<div class="product-information">
<div class="item-details">
<p class="px-2 mt-2">{{ $product['quantity'] }} × {{ $product['cost'] }}</p>
<p class="overflow-ellipsis overflow-hidden px-2 mb-2">{{ $product['notes'] }}</p>
</div>
</div>
</td>
<td style="text-align:right; padding-right:2px;">{{ $product['line_total'] }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@if($services->count() > 0)
<div id="task-details" class="py-6">
<table width="100%">
<thead>
<tr class="border-bottom>
<th style="text-align:left; width:70%; padding-left:2px;">Service</th>
<th style="text-align:right; width:30%; padding-right:2px;">Amount</th>
</tr>
</thead>
<tbody>
@foreach($services as $service)
<tr style="display: table-row;">
<td>
<div class="">
<div class="">
<p class="px-2 mt-2">{{ $service['quantity'] }} × {{ $service['cost'] }}</p>
<p class="overflow-ellipsis overflow-hidden px-2 mb-2">{{ $service['notes'] }}</p>
</div>
</div>
</td>
<td style="text-align:right; padding-right:2px;">{{ $service['line_total'] }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
<div id="totals" class="mb-20">
<table width="100%">
<thead>
</thead>
<tbody>
<tr style="display: table-row;">
<td>
<div class="">
<div class="">
<p class="px-2">{{ ctrans('texts.total') }}</p>
</div>
</div>
</td>
<td style="text-align:right; padding-right:2px;">{{ $amount }}</td>
</tr>
<tr style="display: table-row;">
<td>
<div class="">
<div class="">
<p class="px-2">{{ ctrans('texts.balance') }}</p>
</div>
</div>
</td>
<td style="text-align:right; padding-right:2px;">{{ $balance }}</td>
</tr>
</tbody>
</table>
</div>
@if(strlen($entity->public_notes) > 3)
<div x-data="{ show_notes: false }" class="mb-10">
<button @click="show_notes = !show_notes" :aria-expanded="show_notes ? 'true' : 'false'" :class="{ 'active': show_notes }" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
<span>{{ ctrans('texts.notes') }}</span>
</button>
<div id="notes" class="py-10 border-b-2 border-fuschia-600" x-show="show_notes">
{{ $entity->public_notes }}
</div>
</div>
@endif
@if(strlen($entity->terms) > 3)
<div x-data="{ show_terms: false }" class="mb-10">
<button @click="show_terms = !show_terms" :aria-expanded="show_terms ? 'true' : 'false'" :class="{ 'active': show_terms }" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
<span>{{ ctrans('texts.terms') }}</span>
</button>
<div id="terms" class="py-10 border-b-2 border-fuschia-600" x-show="show_terms">
{{ $entity->terms }}
</div>
</div>
@endif
@if(strlen($entity->footer) > 3)
<div x-data="{ show_footer: false }" class="mb-10">
<button @click="show_footer = !show_footer" :aria-expanded="show_footer ? 'true' : 'false'" :class="{ 'active': show_footer }" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
<span>{{ ctrans('texts.footer') }}</span>
</button>
<div id="terms" class="py-10 border-b-2 border-fuschia-600" x-show="show_footer">
{{ $entity->footer }}
</div>
</div>
@endif

View File

@ -41,4 +41,4 @@
</div>
@endif
</div>
@include('portal.ninja2020.components.html-viewer', ['data' => $data])
@include('portal.ninja2020.components.html-viewer')