feat: Implement invoice creation functionality and integrate DataTables for invoice listing with dynamic column rendering.

This commit is contained in:
pbao9
2026-02-15 00:55:17 +07:00
parent 691c7e3765
commit a7614c91cd
18 changed files with 404 additions and 324 deletions
+51 -14
View File
@@ -2,32 +2,34 @@
@php
$dtColumns = collect($columns)
->map(function ($c) {
->map(function ($c, $index) {
return [
'data' => $c['key'],
'name' => $c['key'],
'orderable' => $c['orderable'] ?? true,
'searchable' => $c['searchable'] ?? true,
'className' => $c['className'] ?? '',
'visible' => $c['visible'] ?? true,
];
})
->values();
@endphp
@if ($bulkDeleteUrl)
<div class="d-flex justify-content-start align-items-center mb-3" id="{{ $id }}-bulk-actions"
style="display: none !important;">
<div class="bg-danger-subtle text-danger rounded-3 d-flex align-items-center gap-3 px-4 py-2">
<div class="d-flex align-items-center gap-2">
<i data-lucide="alert-circle" width="16" height="16"></i>
<span class="fw-medium"><span id="{{ $id }}-selected-count">0</span> items selected</span>
</div>
<div class="vr bg-danger opacity-25"></div>
<x-button-c-r-m type="button" variant="danger" size="sm" icon="trash-2" id="{{ $id }}-bulk-delete-opener-btn"
class="shadow-sm">
Delete Selected
</x-button-c-r-m>
<div class="d-flex justify-content-start align-items-center mb-3" id="{{ $id }}-bulk-actions"
style="display: none !important;">
<div class="bg-danger-subtle text-danger rounded-3 d-flex align-items-center gap-3 px-4 py-2">
<div class="d-flex align-items-center gap-2">
<i data-lucide="alert-circle" width="16" height="16"></i>
<span class="fw-medium"><span id="{{ $id }}-selected-count">0</span> items selected</span>
</div>
<div class="vr bg-danger opacity-25"></div>
<x-button-c-r-m type="button" variant="danger" size="sm" icon="trash-2" id="{{ $id }}-bulk-delete-opener-btn"
class="shadow-sm">
Delete Selected
</x-button-c-r-m>
</div>
</div>
<div class="modal fade" id="{{ $id }}-bulk-delete-modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
@@ -56,6 +58,7 @@ $dtColumns = collect($columns)
</div>
</div>
@endif
<div class="data-table-wrapper position-relative shadow-none">
<div class="loading-process" id="{{ $id }}-loading" style="display: none;">
<div class="loading-bar"></div>
@@ -110,6 +113,27 @@ $dtColumns = collect($columns)
</tbody>
</table>
</div>
<div class="offcanvas offcanvas-end" tabindex="-1" id="{{ $id }}-column-offcanvas">
<div class="offcanvas-header border-bottom">
<h5 class="offcanvas-title">Column Visibility</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
@foreach($columns as $index => $col)
@if((!isset($col['visibility']) || $col['visibility'] !== false) && $col['key'] !== 'checkbox')
<div class="form-check form-switch mb-2">
<input type="checkbox" class="form-check-input column-toggle"
data-column-index="{{ $index }}"
id="{{ $id }}-col-{{ $index }}"
{{ !isset($col['visible']) || $col['visible'] !== false ? 'checked' : '' }}>
<label class="form-check-label" for="{{ $id }}-col-{{ $index }}">{{ $col['label'] }}</label>
</div>
@endif
@endforeach
</div>
</div>
@push('custom-scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
@@ -139,7 +163,7 @@ $dtColumns = collect($columns)
zeroRecords: '<div class="d-flex flex-column align-items-center justify-content-center py-5"><div class="text-secondary mb-3"><i data-lucide="inbox" width="28" height="28" stroke-width="1.5"></i></div><h6 class="text-secondary fw-medium">No matching records found</h6></div>',
emptyTable: '<div class="d-flex flex-column align-items-center justify-content-center py-5"><div class="text-secondary mb-3"><i data-lucide="inbox" width="28" height="28" stroke-width="1.5"></i></div><h6 class="text-secondary fw-medium">No data available</h6></div>'
},
dom: "<'row px-3 py-2 align-items-center'<'col-sm-12 col-md-6 d-inline-flex align-items-center gap-2'<'dt-filter-toggle'>f><'col-sm-12 col-md-6 d-inline-flex align-items-center justify-content-end gap-2'l>>" +
dom: "<'row px-3 py-2 align-items-center'<'col-sm-12 col-md-6 d-inline-flex align-items-center gap-2'<'dt-filter-toggle'><'dt-column-toggle'>f><'col-sm-12 col-md-6 d-inline-flex align-items-center justify-content-end gap-2'l>>" +
"<'row p-0'<'col-sm-12'tr>>" +
"<'row px-3 pb-3 pt-3 align-items-center'<'col-sm-12 col-md-5 text-muted small d-inline-flex align-items-center'i><'col-sm-12 col-md-7 d-inline-flex align-items-center justify-content-end gap-2'p>>",
initComplete: function () {
@@ -152,6 +176,13 @@ $dtColumns = collect($columns)
'</button>'
);
wrapper.find('.dt-column-toggle').html(
'<button type="button" class="btn btn-sm btn-outline-secondary d-flex align-items-center gap-1" data-bs-toggle="offcanvas" data-bs-target="#{{ $id }}-column-offcanvas">' +
'<i data-lucide="columns" width="14" height="14"></i>' +
'<span>Columns</span>' +
'</button>'
);
$('#{{ $id }}-filter-toggle-btn').on('click', function () {
var filterRow = $('#{{ $id }}-filter-row');
var btn = $(this);
@@ -207,6 +238,12 @@ $dtColumns = collect($columns)
}
});
$('#{{ $id }}-column-offcanvas').on('change', '.column-toggle', function () {
var columnIdx = $(this).data('column-index');
var column = table.column(columnIdx);
column.visible($(this).is(':checked'));
});
$('#{{ $id }} tbody').on('dblclick', 'tr', function () {
const editLink = $(this).find('a.btn-outline-primary, a[href*="edit"]').first();
if (editLink.length && editLink.attr('href')) {