Files
Kent-logistics-Laravel/resources/views/admin/invoice_edit.blade.php
Utkarsh Khedkar 785f2564be changes
2026-03-13 23:06:19 +05:30

929 lines
35 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('admin.layouts.app')
@section('page-title', 'Edit Invoice')
@section('content')
<style>
:root {
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
--warning-gradient: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
--danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
--glass-bg: rgba(255, 255, 255, 0.95);
--glass-border: rgba(255, 255, 255, 0.2);
--shadow-soft: 0 8px 32px rgba(0, 0, 0, 0.08);
--shadow-medium: 0 12px 48px rgba(0, 0, 0, 0.12);
--shadow-strong: 0 20px 60px rgba(0, 0, 0, 0.15);
--border-radius: 12px;
}
body {
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
}
@keyframes fadeUp {
0% {
opacity: 0;
transform: translateY(20px) scale(0.98);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-radius: var(--border-radius);
border: 1px solid var(--glass-border);
box-shadow: var(--shadow-strong);
position: relative;
overflow: hidden;
animation: fadeUp 0.6s ease;
margin-bottom: 1.5rem;
}
.glass-card::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
45deg,
rgba(102, 126, 234, 0.03),
rgba(118, 75, 162, 0.03) 50%,
rgba(16, 185, 129, 0.03)
);
pointer-events: none;
}
.card-header-compact {
background: var(--primary-gradient);
color: #fff;
padding: 1rem 1.5rem;
border: none;
position: relative;
overflow: hidden;
}
.card-header-compact h4 {
margin: 0;
font-weight: 700;
font-size: 1.3rem;
display: flex;
align-items: center;
gap: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.card-body-compact {
padding: 1.5rem;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
position: relative;
z-index: 1;
}
.form-grid-compact {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.form-group-compact {
position: relative;
}
.form-label-compact {
font-weight: 600;
color: #374151;
margin-bottom: 8px;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 8px;
}
.form-control-compact,
.form-select-compact {
border: 2px solid #e2e8f0;
border-radius: 8px;
padding: 10px 12px;
font-size: 0.9rem;
transition: all 0.3s ease;
background: #ffffff;
width: 100%;
}
.form-control-compact:focus,
.form-select-compact:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
outline: none;
}
.btn-compact {
padding: 10px 20px;
border-radius: 8px;
font-weight: 600;
font-size: 0.9rem;
transition: all 0.3s ease;
border: none;
display: inline-flex;
align-items: center;
gap: 6px;
text-decoration: none;
}
.btn-success-compact {
background: var(--success-gradient);
color: white;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
}
.btn-success-compact:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
color: white;
}
.btn-primary-compact {
background: var(--primary-gradient);
color: white;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.btn-primary-compact:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
color: white;
}
.btn-danger-compact {
background: var(--danger-gradient);
color: white;
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
}
.btn-danger-compact:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(239, 68, 68, 0.4);
color: white;
}
.summary-grid-compact {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.summary-card-compact {
background: #ffffff;
padding: 1rem;
border-radius: 8px;
box-shadow: var(--shadow-soft);
border-left: 4px solid;
text-align: center;
}
.summary-card-compact.total {
border-left-color: #10b981;
}
.summary-card-compact.paid {
border-left-color: #3b82f6;
}
.summary-card-compact.remaining {
border-left-color: #f59e0b;
}
.summary-value-compact {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.25rem;
}
.summary-label-compact {
font-size: 0.8rem;
color: #64748b;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.amount-breakdown-compact {
background: #ffffff;
padding: 1rem;
border-radius: 8px;
box-shadow: var(--shadow-soft);
margin-bottom: 1.5rem;
}
.breakdown-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0;
border-bottom: 1px solid #f1f5f9;
}
.breakdown-row:last-child {
border-bottom: none;
}
.breakdown-label {
font-weight: 600;
color: #374151;
font-size: 0.9rem;
}
.breakdown-value {
font-weight: 600;
font-size: 0.9rem;
}
.table-compact {
width: 100%;
border-collapse: collapse;
margin-bottom: 0;
font-size: 0.85rem;
}
.table-compact thead th {
background: var(--primary-gradient);
color: #ffffff;
padding: 0.75rem 1rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
font-size: 0.8rem;
border: none;
}
.table-compact tbody tr {
background: #ffffff;
transition: all 0.2s ease;
}
.table-compact tbody tr:hover {
background: #f8fafc;
}
.table-compact tbody td {
padding: 0.75rem 1rem;
border-bottom: 1px solid #e2e8f0;
vertical-align: middle;
}
.badge-compact {
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
}
@media (max-width: 768px) {
.glass-card {
margin: 0.5rem;
border-radius: 10px;
}
.card-body-compact {
padding: 1rem;
}
.form-grid-compact {
grid-template-columns: 1fr;
gap: 0.75rem;
}
.summary-grid-compact {
grid-template-columns: 1fr;
gap: 0.75rem;
}
.btn-compact {
width: 100%;
justify-content: center;
}
.table-compact {
font-size: 0.8rem;
}
}
</style>
<div class="container-fluid py-3">
{{-- Invoice Overview --}}
<div class="glass-card">
<div class="card-header-compact">
<h4>
<i class="fas fa-file-invoice me-2"></i>
Invoice Overview
</h4>
</div>
<div class="card-body-compact">
@include('admin.popup_invoice', ['invoice' => $invoice, 'shipment' => $shipment])
</div>
</div>
{{-- Edit Invoice Header --}}
<div class="glass-card">
<div class="card-header-compact d-flex justify-content-between align-items-center">
<h4>
<i class="fas fa-edit me-2"></i>
Edit Invoice Details
</h4>
<small id="headerUpdateMsg" class="text-light"></small>
</div>
<div class="card-body-compact">
<form id="invoiceHeaderForm" action="{{ route('admin.invoices.update', $invoice->id) }}" method="POST">
@csrf
<div class="form-grid-compact">
{{-- Invoice Date --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-calendar-day"></i> Invoice Date
</label>
<input type="date"
name="invoice_date"
class="form-control-compact"
value="{{ old('invoice_date', $invoice->invoice_date) }}"
required>
</div>
{{-- Due Date --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-clock"></i> Due Date
</label>
<input type="date"
name="due_date"
class="form-control-compact"
value="{{ old('due_date', $invoice->due_date) }}"
required>
</div>
{{-- Final Amount (With GST) Charge Groups Total --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-money-bill-wave"></i> Final Amount (With GST)
</label>
<input type="number"
step="0.01"
class="form-control-compact"
value="{{ $invoice->grand_total_with_charges }}"
readonly>
</div>
{{-- Status --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-tasks"></i> Status
</label>
<select name="status" id="statusSelect" class="form-select-compact" required>
<option value="pending" {{ old('status', $invoice->status) === 'pending' ? 'selected' : '' }}>Pending</option>
<option value="paying" {{ old('status', $invoice->status) === 'paying' ? 'selected' : '' }}>Paying</option>
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>Paid</option>
<option value="overdue" {{ old('status', $invoice->status) === 'overdue' ? 'selected' : '' }}>Overdue</option>
</select>
</div>
{{-- Notes --}}
<div class="form-group-compact" style="grid-column: 1 / -1;">
<label class="form-label-compact">
<i class="fas fa-sticky-note"></i> Notes
</label>
<textarea name="notes"
rows="3"
class="form-control-compact"
placeholder="Add any additional notes...">{{ old('notes', $invoice->notes) }}</textarea>
</div>
</div>
<div class="text-end mt-3">
<button type="submit" id="btnHeaderSave" class="btn-success-compact btn-compact">
<i class="fas fa-save me-2"></i>Update Invoice
</button>
</div>
</form>
</div>
</div>
@php
$totalPaid = $invoice->totalPaid();
$remaining = $invoice->remainingAmount();
// Mixed tax type label from charge groups
$taxTypes = $invoice->chargeGroups
? $invoice->chargeGroups->pluck('tax_type')->filter()->unique()->values()
: collect([]);
$taxTypeLabel = 'None';
if ($taxTypes->count() === 1) {
if ($taxTypes[0] === 'gst') {
$taxTypeLabel = 'GST (CGST + SGST)';
} elseif ($taxTypes[0] === 'igst') {
$taxTypeLabel = 'IGST';
} else {
$taxTypeLabel = strtoupper($taxTypes[0]);
}
} elseif ($taxTypes->count() > 1) {
$parts = [];
if ($taxTypes->contains('gst')) {
$parts[] = 'GST (CGST + SGST)';
}
if ($taxTypes->contains('igst')) {
$parts[] = 'IGST';
}
$taxTypeLabel = implode(' + ', $parts);
}
@endphp
{{-- Amount Breakdown --}}
<div class="amount-breakdown-compact">
<h6 class="fw-bold mb-3 text-dark">
<i class="fas fa-calculator me-2"></i>Amount Breakdown
</h6>
<div class="breakdown-row">
<span class="breakdown-label">Tax Type</span>
<span class="breakdown-value text-primary">
{{ $taxTypeLabel }}
</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label">GST Amount</span>
<span class="breakdown-value text-warning" id="gstAmount">
{{ number_format($invoice->gst_amount, 2) }}
</span>
</div>
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
<span class="breakdown-label fw-bold">Grand Total (Charges + GST)</span>
<span class="breakdown-value fw-bold text-dark" id="totalInvoiceWithGst">
{{ number_format($invoice->grand_total_with_charges, 2) }}
</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label text-success">Total Paid</span>
<span class="breakdown-value fw-bold text-success" id="paidAmount">
{{ number_format($totalPaid, 2) }}
</span>
</div>
<div class="breakdown-row" style="border-bottom: none;">
<span class="breakdown-label text-danger">Remaining</span>
<span class="breakdown-value fw-bold text-danger" id="remainingAmount">
{{ number_format(max(0, $remaining), 2) }}
</span>
</div>
</div>
{{-- Summary cards --}}
<div class="summary-grid-compact">
<div class="summary-card-compact total">
<div class="summary-value-compact text-success" id="totalInvoiceWithGstCard">
{{ number_format($invoice->grand_total_with_charges, 2) }}
</div>
<div class="summary-label-compact">Grand Total (Charges + GST)</div>
</div>
<div class="summary-card-compact paid">
<div class="summary-value-compact text-primary">
{{ number_format($totalPaid, 2) }}
</div>
<div class="summary-label-compact">Total Paid</div>
</div>
<div class="summary-card-compact remaining">
<div class="summary-value-compact text-warning">
{{ number_format(max(0, $remaining), 2) }}
</div>
<div class="summary-label-compact">Remaining</div>
</div>
</div>
{{-- Installment Management --}}
<div class="glass-card">
<div class="card-header-compact d-flex justify-content-between align-items-center">
<h4 class="mb-0">
<i class="fas fa-credit-card me-2"></i>Installment Payments
</h4>
@if($remaining > 0)
<button id="toggleInstallmentForm" class="btn-primary-compact btn-compact">
<i class="fas fa-plus-circle me-2"></i>Add Installment
</button>
@endif
</div>
<div class="card-body-compact">
{{-- Installment Form --}}
<div id="installmentForm" class="d-none mb-3">
<div class="glass-card" style="background: rgba(248, 250, 252, 0.8);">
<div class="card-header-compact" style="background: var(--success-gradient);">
<h4 class="mb-0">
<i class="fas fa-plus-circle me-2"></i>Add New Installment
</h4>
</div>
<div class="card-body-compact">
<form id="installmentSubmitForm">
@csrf
<div class="form-grid-compact">
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-calendar-day"></i> Installment Date
</label>
<input type="date"
name="installment_date"
class="form-control-compact"
required>
</div>
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-credit-card"></i> Payment Method
</label>
<select name="payment_method"
class="form-select-compact"
required>
<option value="cash">Cash</option>
<option value="bank">Bank Transfer</option>
<option value="upi">UPI</option>
<option value="cheque">Cheque</option>
</select>
</div>
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-hashtag"></i> Reference No
</label>
<input type="text"
name="reference_no"
class="form-control-compact"
placeholder="Enter reference number">
</div>
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-money-bill-wave"></i> Amount
</label>
<input type="number"
name="amount"
id="installmentAmount"
class="form-control-compact"
step="0.01"
min="1"
max="{{ max(0, $remaining) }}"
required
placeholder="Enter installment amount">
</div>
</div>
<div class="text-end mt-2">
<button type="submit"
class="btn-success-compact btn-compact"
id="installmentSubmitBtn">
<i class="fas fa-paper-plane me-2"></i>Submit Installment
</button>
</div>
</form>
</div>
</div>
</div>
{{-- Installment History --}}
<h6 class="fw-bold mb-2 text-dark">
<i class="fas fa-history me-2"></i>Installment History
</h6>
<div id="noInstallmentsMsg"
class="{{ $invoice->installments->count() ? 'd-none' : '' }} text-center text-muted fw-bold py-4">
No installments found. Click Add Installment to create one.
</div>
<div class="table-responsive">
<table class="table-compact">
<thead>
<tr>
<th>#</th>
<th>Date</th>
<th>Payment Method</th>
<th>Reference No</th>
<th>Amount</th>
<th>Action</th>
</tr>
</thead>
<tbody id="installmentTable">
@foreach($invoice->installments as $i)
<tr data-id="{{ $i->id }}">
<td class="fw-bold text-muted">{{ $loop->iteration }}</td>
<td>{{ $i->installment_date }}</td>
<td>
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
{{ strtoupper($i->payment_method) }}
</span>
</td>
<td>
@if($i->reference_no)
<span class="text-muted">{{ $i->reference_no }}</span>
@else
<span class="text-muted">-</span>
@endif
</td>
<td class="fw-bold text-success">
{{ number_format($i->amount, 2) }}
</td>
<td>
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
<i class="fas fa-trash me-1"></i>Delete
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const toggleBtn = document.getElementById("toggleInstallmentForm");
const formBox = document.getElementById("installmentForm");
if (toggleBtn && formBox) {
toggleBtn.addEventListener("click", function (e) {
e.preventDefault();
formBox.classList.toggle("d-none");
});
}
const submitForm = document.getElementById("installmentSubmitForm");
const submitBtn = document.getElementById("installmentSubmitBtn");
const formatINR = (amt) => {
return Number(amt).toLocaleString("en-IN", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
};
if (submitForm && submitBtn) {
submitForm.addEventListener("submit", function (e) {
e.preventDefault();
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Processing...';
submitBtn.disabled = true;
fetch("{{ route('admin.invoice.installment.store', $invoice->id) }}", {
method: "POST",
headers: {
"X-CSRF-TOKEN": submitForm.querySelector('input[name="_token"]').value,
"Accept": "application/json",
},
body: new FormData(submitForm)
})
.then(res => res.json())
.then(data => {
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
if (data.status === "error") {
alert(data.message);
return;
}
const table = document.getElementById("installmentTable");
const index = table.rows.length + 1;
document.getElementById("noInstallmentsMsg")?.classList.add("d-none");
table.insertAdjacentHTML("beforeend", `
<tr data-id="${data.installment.id}">
<td class="fw-bold text-muted">${index}</td>
<td>${data.installment.installment_date}</td>
<td>
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
${data.installment.payment_method.toUpperCase()}
</span>
</td>
<td>
${data.installment.reference_no
? `<span class="text-muted">${data.installment.reference_no}</span>`
: '<span class="text-muted">-</span>'}
</td>
<td class="fw-bold text-success">
₹${formatINR(data.installment.amount)}
</td>
<td>
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
<i class="fas fa-trash me-1"></i>Delete
</button>
</td>
</tr>
`);
if (document.getElementById("paidAmount")) {
document.getElementById("paidAmount").textContent = "" + formatINR(data.totalPaid);
}
if (document.getElementById("remainingAmount")) {
document.getElementById("remainingAmount").textContent = "" + formatINR(data.remaining);
}
if (document.getElementById("baseAmount")) {
document.getElementById("baseAmount").textContent = "" + formatINR(data.chargeGroupsTotal);
}
if (document.getElementById("gstAmount")) {
document.getElementById("gstAmount").textContent = "" + formatINR(data.gstAmount);
}
if (document.getElementById("totalInvoiceWithGst")) {
document.getElementById("totalInvoiceWithGst").textContent =
"" + formatINR(data.grandTotal);
}
const totalCard = document.getElementById("totalInvoiceWithGstCard");
if (totalCard) {
totalCard.textContent = "" + formatINR(data.grandTotal);
}
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = "" + formatINR(data.totalPaid);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = "" + formatINR(data.remaining);
submitForm.reset();
if (data.isCompleted && toggleBtn) {
toggleBtn.remove();
formBox.classList.add("d-none");
}
alert(data.message);
})
.catch(() => {
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
alert("Something went wrong. Please try again.");
});
});
}
document.addEventListener("click", function (e) {
if (!e.target.classList.contains("deleteInstallment") &&
!e.target.closest(".deleteInstallment")) {
return;
}
e.preventDefault();
if (!confirm("Are you sure you want to delete this installment?")) {
return;
}
const btn = e.target.closest(".deleteInstallment");
const row = btn.closest("tr");
const id = row.getAttribute("data-id");
fetch("{{ url('admin/installment') }}/" + id, {
method: "DELETE",
headers: {
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content"),
"Accept": "application/json",
},
})
.then(res => res.json())
.then(data => {
if (data.status === "success") {
row.style.opacity = 0;
setTimeout(() => row.remove(), 300);
if (document.getElementById("paidAmount")) {
document.getElementById("paidAmount").textContent = "" + formatINR(data.totalPaid);
}
if (document.getElementById("remainingAmount")) {
document.getElementById("remainingAmount").textContent = "" + formatINR(data.remaining);
}
if (document.getElementById("baseAmount")) {
document.getElementById("baseAmount").textContent = "" + formatINR(data.chargeGroupsTotal);
}
if (document.getElementById("gstAmount")) {
document.getElementById("gstAmount").textContent = "" + formatINR(data.gstAmount);
}
if (document.getElementById("totalInvoiceWithGst")) {
document.getElementById("totalInvoiceWithGst").textContent =
"" + formatINR(data.grandTotal);
}
const totalCard = document.getElementById("totalInvoiceWithGstCard");
if (totalCard) {
totalCard.textContent = "" + formatINR(data.grandTotal);
}
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = "" + formatINR(data.totalPaid);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = "" + formatINR(data.remaining);
if (data.isZero) {
document.getElementById("noInstallmentsMsg")?.classList.remove("d-none");
}
alert(data.message);
} else {
alert(data.message || "Something went wrong. Please try again.");
}
})
.catch(() => {
alert("Something went wrong. Please try again.");
});
});
// Auto due date = invoice date + 10 days
const invoiceDateInput = document.querySelector('input[name="invoice_date"]');
const dueDateInput = document.querySelector('input[name="due_date"]');
if (invoiceDateInput && dueDateInput) {
invoiceDateInput.addEventListener('change', function() {
const selectedDate = new Date(this.value);
if (!isNaN(selectedDate.getTime())) {
selectedDate.setDate(selectedDate.getDate() + 10);
const year = selectedDate.getFullYear();
const month = String(selectedDate.getMonth() + 1).padStart(2, '0');
const day = String(selectedDate.getDate()).padStart(2, '0');
dueDateInput.value = `${year}-${month}-${day}`;
}
});
}
// ✅ Invoice header AJAX save (no page refresh)
const headerForm = document.getElementById('invoiceHeaderForm');
const headerBtn = document.getElementById('btnHeaderSave');
const headerMsg = document.getElementById('headerUpdateMsg');
if (headerForm && headerBtn) {
headerForm.addEventListener('submit', function(e) {
e.preventDefault();
headerMsg.textContent = '';
headerBtn.disabled = true;
headerBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
const formData = new FormData(headerForm);
fetch(headerForm.action, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
body: formData
})
.then(async res => {
let data = null;
try { data = await res.json(); } catch(e) {}
if (!res.ok) {
if (data && data.errors) {
const firstError = Object.values(data.errors)[0][0] ?? 'Validation error.';
throw new Error(firstError);
}
throw new Error(data && data.message ? data.message : 'Failed to update invoice.');
}
headerMsg.textContent = 'Invoice header updated.';
headerMsg.classList.remove('text-danger');
headerMsg.classList.add('text-light');
// popup_invoice वरचा status badge update करायचा असल्यास:
const status = document.getElementById('statusSelect')?.value;
const badge = document.querySelector('.status-badge');
if (badge && status) {
badge.classList.remove('status-paid','status-pending','status-overdue','status-default');
if (status === 'paid') badge.classList.add('status-paid');
else if (status === 'pending') badge.classList.add('status-pending');
else if (status === 'overdue') badge.classList.add('status-overdue');
else badge.classList.add('status-default');
badge.innerHTML = status.charAt(0).toUpperCase() + status.slice(1);
}
})
.catch(err => {
headerMsg.textContent = err.message || 'Error updating invoice.';
headerMsg.classList.remove('text-light');
headerMsg.classList.add('text-warning');
})
.finally(() => {
headerBtn.disabled = false;
headerBtn.innerHTML = '<i class="fas fa-save me-2"></i>Update Invoice';
});
});
}
});
</script>
@endsection