You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
13 KiB
251 lines
13 KiB
@use(App\Domains\Billing\Enums\Invoicing\InvoiceType)
|
|
@use(App\Domains\System\Support\ValueObjects\Money)
|
|
|
|
<div class="row g-3 g-lg-4">
|
|
<!-- Header Section: Invoice Title and Status Overview -->
|
|
<div class="col-12">
|
|
<div class="card shadow-sm border-0 rounded-3">
|
|
<div class="card-body p-3 p-md-4">
|
|
<div class="d-flex align-items-center justify-content-between flex-wrap gap-3">
|
|
<!-- Left side: Invoice Icon and Name -->
|
|
<div class="d-flex align-items-center gap-3">
|
|
<div class="avatar avatar-lg bg-primary-subtle text-primary rounded-3 d-flex align-items-center justify-content-center p-2 flex-shrink-0">
|
|
<!-- Tabler File Invoice Icon -->
|
|
<x-tabler-file-invoice width="28" height="28" />
|
|
</div>
|
|
<div>
|
|
<!-- Field Label for Invoice Name -->
|
|
<span class="text-body-secondary fw-semibold text-uppercase fs-7 d-block mb-1">
|
|
{{ __('domains/billing/field.invoice.name') }}
|
|
</span>
|
|
<!-- Invoice Display Name with fallback placeholder -->
|
|
<h4 class="fw-bold text-body mb-0">
|
|
{{ $this->invoice->name ?? '-' }}
|
|
</h4>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right side: Current Invoice Status Badge -->
|
|
<div class="d-flex align-items-center gap-2">
|
|
<x-badge
|
|
:variant="$this->invoice->status->variant()"
|
|
:label="$this->invoice->status->label()"
|
|
:rounded="true"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content Column: Customer Details & Item Breakdown -->
|
|
<div class="col-12 col-lg-7 col-xl-8 d-flex flex-column gap-3 gap-lg-4">
|
|
|
|
<!-- Section 1: Customer Details Information Card -->
|
|
<x-card
|
|
:title="__('domains/billing/section.invoice.customer_detail.title')"
|
|
:subtitle="__('domains/billing/section.invoice.customer_detail.subtitle')"
|
|
class="shadow-sm border-0 rounded-3"
|
|
>
|
|
<!-- Grid displaying dynamic schema client information -->
|
|
<div class="row g-3">
|
|
@forelse($this->schema as $schema)
|
|
<div class="{{ count($this->schema) > 5 ? 'col-12 col-md-6' : 'col-12' }}">
|
|
<div class="p-3 rounded-3 bg-body-tertiary border border-subtle h-100">
|
|
<!-- Schema Attribute Label -->
|
|
<small class="text-body-secondary fw-semibold text-uppercase d-block mb-1 fs-7">
|
|
{{ $schema->attributes['label'] ?? __("domains/billing/field.invoice.{$schema->key}") }}
|
|
</small>
|
|
<!-- Dynamic Client Detail Value -->
|
|
<span class="fw-bold text-body fs-6">
|
|
@if(isset($this->invoice->client_detail[$schema->key.'_label']))
|
|
{{ $this->invoice->client_detail[$schema->key.'_label'] }}
|
|
@elseif($this->invoice->client_detail[$schema->key] != '')
|
|
{{ $this->invoice->client_detail[$schema->key] }}
|
|
@else
|
|
-
|
|
@endif
|
|
</span>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<!-- Fallback if schema has no dynamic fields defined -->
|
|
<div class="col-12">
|
|
<div class="p-3 text-center text-body-secondary bg-body-tertiary rounded-3 border border-subtle">
|
|
-
|
|
</div>
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</x-card>
|
|
|
|
<!-- Section 2: Invoice Line Items Table Card -->
|
|
<x-card
|
|
:title="__('domains/billing/section.invoice.items.title')"
|
|
:subtitle="__('domains/billing/section.invoice.items.subtitle')"
|
|
class="shadow-sm border-0 rounded-3"
|
|
>
|
|
<div class="table-responsive rounded-3 border border-subtle">
|
|
<table class="table table-hover align-middle mb-0 text-nowrap small">
|
|
<!-- Table Header -->
|
|
<thead class="bg-body-tertiary">
|
|
<tr class="text-uppercase fs-7 fw-bold text-body-secondary tracking-wider">
|
|
<th class="ps-3 py-3" style="width: 40px;">#</th>
|
|
<th class="py-3">@lang('domains/billing/field.invoice_item.code')</th>
|
|
<th class="py-3">@lang('domains/billing/field.invoice_item.name')</th>
|
|
<th class="text-center py-3">@lang('domains/billing/field.invoice_item.quantity')</th>
|
|
<th class="text-end py-3">@lang('domains/billing/field.invoice_item.gross_price')</th>
|
|
<th class="text-end py-3">@lang('domains/billing/field.invoice_item.discounted_price')</th>
|
|
<th class="text-end pe-3 py-3">@lang('domains/billing/field.invoice_item.net_price')</th>
|
|
</tr>
|
|
</thead>
|
|
<!-- Table Body -->
|
|
<tbody>
|
|
@forelse($this->invoice->items ?? [] as $index => $item)
|
|
<tr>
|
|
<!-- Row Number -->
|
|
<td class="ps-3 py-2 fw-semibold text-body-secondary">{{ $index + 1 }}</td>
|
|
|
|
<!-- Item Code -->
|
|
<td class="py-2 font-monospace text-body-secondary fs-7">
|
|
{{ $item['code'] ?? '-' }}
|
|
</td>
|
|
|
|
<!-- Item Name -->
|
|
<td class="py-2 text-body fw-bold">{{ $item['name'] ?? '-' }}</td>
|
|
|
|
<!-- Quantity -->
|
|
<td class="text-center py-2 font-monospace fw-semibold">{{ $item['quantity'] ?? 1 }}</td>
|
|
|
|
<!-- Gross Amount per Line Item -->
|
|
<td class="text-end py-2 font-monospace text-body-secondary">
|
|
{{ $item['gross_amount'] ?? new Money(0) }}
|
|
</td>
|
|
|
|
<!-- Discount Amount per Line Item -->
|
|
<td class="text-end py-2 font-monospace text-danger fw-medium">
|
|
{{ $item['discount_amount'] ?? new Money(0) }}
|
|
</td>
|
|
|
|
<!-- Net Calculated Amount per Line Item -->
|
|
<td class="text-end pe-3 py-2 font-monospace fw-bold text-success fs-6">
|
|
{{ $item['net_amount'] ?? new Money(0) }}
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<!-- Empty State Display -->
|
|
<tr>
|
|
<td colspan="7" class="text-center py-5 text-body-secondary">
|
|
<div class="avatar avatar-xl mx-auto mb-3 bg-body-tertiary rounded-circle p-3 text-body-tertiary">
|
|
<!-- Receipt Icon for Empty State -->
|
|
<x-tabler-receipt width="36" height="36" />
|
|
</div>
|
|
<h6 class="fw-semibold text-body-secondary mb-1">{{ __('ui/common.no_data') }}</h6>
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</x-card>
|
|
</div>
|
|
|
|
<!-- Right Column: Financial Summary & Sticky Panel -->
|
|
<div class="col-12 col-lg-5 col-xl-4">
|
|
<div class="sticky-top" style="top: 90px; z-index: 10;">
|
|
<x-card
|
|
:title="__('domains/billing/section.invoice.summary.title')"
|
|
:subtitle="__('domains/billing/section.invoice.summary.subtitle')"
|
|
class="shadow-sm border-0 border-top border-4 border-primary rounded-3"
|
|
>
|
|
<div class="p-3 rounded-3 bg-body-tertiary border border-subtle">
|
|
<!-- Invoice Status Header -->
|
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
|
<span class="text-body-secondary fw-bold text-uppercase fs-7 tracking-wider">
|
|
@lang('domains/billing/field.invoice.status')
|
|
</span>
|
|
<x-badge
|
|
:variant="$this->invoice->status->variant()"
|
|
:label="$this->invoice->status->label()"
|
|
:rounded="true"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Breakdown Calculation Card -->
|
|
<div class="d-flex flex-column gap-2 mb-3">
|
|
<!-- Total Gross Amount -->
|
|
<div class="d-flex align-items-center justify-content-between p-2 rounded-2 bg-body border border-subtle">
|
|
<span class="text-body-secondary fw-medium fs-7">
|
|
@lang('domains/billing/field.invoice.gross_amount')
|
|
</span>
|
|
<span class="fw-bold text-body font-monospace fs-6">
|
|
{{ $this->invoice->gross_amount ?? '-' }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Total Discount Amount -->
|
|
<div class="d-flex align-items-center justify-content-between p-2 rounded-2 bg-body border border-subtle">
|
|
<span class="text-body-secondary fw-medium fs-7">
|
|
@lang('domains/billing/field.invoice.discount_amount')
|
|
</span>
|
|
<span class="fw-bold text-danger font-monospace fs-6">
|
|
-{{ $this->invoice->discount_amount ?? '-' }}
|
|
</span>
|
|
</div>
|
|
|
|
<hr class="my-1 border-subtle opacity-50">
|
|
|
|
<!-- Highlight Final Net Amount -->
|
|
<div class="d-flex align-items-center justify-content-between p-3 rounded-3 bg-primary text-white shadow-sm">
|
|
<div class="d-flex flex-column">
|
|
<small class="fw-bold text-uppercase fs-7 opacity-75 tracking-wider">
|
|
@lang('domains/billing/field.invoice.net_amount')
|
|
</small>
|
|
</div>
|
|
<span class="fw-bold font-monospace fs-4">
|
|
{{ $this->invoice->net_amount ?? '-' }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Informational Actions / Options -->
|
|
<div class="d-flex flex-column gap-2">
|
|
@if($this->invoice->status->isDraft())
|
|
<!-- Update Status from Draft -->
|
|
<x-button
|
|
type="button"
|
|
theme="outline-info"
|
|
size="md"
|
|
icon="tabler-file-invoice"
|
|
wire:click="issueInvoice"
|
|
:label="__('domains/billing/button.issue_this_invoice')"
|
|
class="w-100 fw-bold shadow-xs py-2"
|
|
/>
|
|
@endif
|
|
|
|
<!-- Print / Export Action Button -->
|
|
<x-button
|
|
type="button"
|
|
theme="outline-primary"
|
|
size="md"
|
|
icon="tabler-printer"
|
|
:label="__('ui/button.print')"
|
|
class="w-100 fw-bold shadow-xs py-2"
|
|
/>
|
|
|
|
<!-- Back / Return Button -->
|
|
<x-button
|
|
type="button"
|
|
theme="secondary"
|
|
size="md"
|
|
icon="tabler-arrow-left"
|
|
:label="__('ui/button.back')"
|
|
x-on:click="window.history.back()"
|
|
class="w-100 fw-semibold py-2"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</x-card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|