Files
portal-api/resources/views/components/data-table.blade.php
T
2026-02-03 10:04:55 +07:00

256 lines
9.8 KiB
PHP

@props(['id', 'columns', 'ajax'])
@php
$dtColumns = collect($columns)->map(function ($c) {
return [
'data' => $c['key'],
'name' => $c['key'],
'orderable' => $c['orderable'] ?? true,
'searchable' => $c['searchable'] ?? true,
'className' => $c['className'] ?? '',
];
})->values();
@endphp
<div class="data-table-wrapper rounded-3 overflow-hidden border border-light">
<table id="{{ $id }}" class="table table-hover align-middle mb-0 bg-white"
style="width:100%; border-collapse: separate; border-spacing: 0;">
<thead class="bg-light">
<tr>
@foreach($columns as $col)
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-3 py-3 border-bottom"
style="font-size: 0.75rem; letter-spacing: 0.5px;">
{{ $col['label'] }}
</th>
@endforeach
</tr>
<tr class="filter-row bg-light">
@foreach($columns as $col)
<th class="p-2 border-bottom">
@if(($col['searchable'] ?? true))
<div class="input-group input-group-sm">
<span class="input-group-text bg-white border-end-0 ps-2 pe-2 text-muted border-light">
<i data-lucide="search" style="font-size: 0.75rem;"></i>
</span>
<input type="text"
class="form-control form-control-sm border-start-0 ps-0 column-search shadow-none border-light bg-white"
placeholder="Filter..." data-column="{{ $loop->index }}" style="font-size: 0.8125rem;">
</div>
@endif
</th>
@endforeach
</tr>
</thead>
<tbody>
{{-- DataTables will populate this --}}
</tbody>
</table>
</div>
@push('custom-scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
$.fn.DataTable.ext.pager.numbers_length = 7;
var table = $('#{{ $id }}').DataTable({
processing: true,
serverSide: true,
ajax: '{{ $ajax }}',
columns: @json($dtColumns),
orderCellsTop: true,
fixedHeader: false,
pagingType: 'full_numbers',
language: {
processing: '<div class="spinner-border text-primary" role="status" style="width: 2rem; height: 2rem;"><span class="visually-hidden">Loading...</span></div>',
searchPlaceholder: "Search...",
search: "",
lengthMenu: "_MENU_",
info: "Showing <span class='fw-bold'>_START_</span> to <span class='fw-bold'>_END_</span> of <span class='fw-bold'>_TOTAL_</span> entries",
paginate: {
first: '<span class="d-flex align-items-center justify-content-center"><i data-lucide="chevrons-left" width="16" height="16"></i></span>',
last: '<span class="d-flex align-items-center justify-content-center"><i data-lucide="chevrons-right" width="16" height="16"></i></span>',
next: '<span class="d-flex align-items-center justify-content-center"><i data-lucide="chevron-right" width="16" height="16"></i></span>',
previous: '<span class="d-flex align-items-center justify-content-center"><i data-lucide="chevron-left" width="16" height="16"></i></span>'
},
zeroRecords: "No matching records found",
emptyTable: "No data available in table"
},
dom: "<'row p-3'<'col-sm-12'tr>>" +
"<'row px-3 pb-3 pt-1 align-items-center'<'col-sm-12 col-md-5 text-muted small'i><'col-sm-12 col-md-7 d-flex align-items-center justify-content-end gap-2'lp>>",
initComplete: function () {
// Initialize select2 with custom styling
$('.dataTables_length select').select2({
minimumResultsForSearch: Infinity,
width: '80px',
dropdownCssClass: 'small-select-dropdown',
selectionCssClass: 'small-select-selection'
});
// Debounced search for column inputs
this.api().columns().every(function () {
var that = this;
$('input', this.header()).on('keyup change clear', function () {
if (that.search() !== this.value) {
that.search(this.value).draw();
}
});
});
},
drawCallback: function () {
// Re-initialize Select2 on draw (if lost)
if ($('.dataTables_length select').data('select2')) {
$('.dataTables_length select').select2('destroy');
}
$('.dataTables_length select').select2({
minimumResultsForSearch: Infinity,
width: '80px'
});
// Re-initialize icons with slight delay to ensuring DOM is ready
setTimeout(function () {
if (window.lucide) {
lucide.createIcons();
}
}, 50);
}
});
// Prevent sorting when clicking on the input
$('#{{ $id }} thead tr:eq(1) th').on('click', function (e) {
e.stopPropagation();
});
// External trigger for column search (redundant but kept for manual binding if needed)
$('#{{ $id }} .column-search').on('keyup change', function () {
var columnIndex = $(this).data('column');
table.column(columnIndex).search(this.value).draw();
});
});
</script>
<style>
/* Custom SAAS DataTable Styles */
.data-table-wrapper {
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
overflow-y: auto;
position: relative;
}
.pagination button.page-link {
height: 100%;
}
.data-table-wrapper .table thead th {
border-color: #f1f5f9 !important;
font-family: 'Inter', sans-serif;
vertical-align: middle;
position: sticky;
top: 0;
z-index: 5;
background-color: #f8f9fa;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* Sticky for second row (filter inputs) if present */
.data-table-wrapper .table thead tr:nth-child(2) th {
top: 45px;
/* Adjust based on typical header height */
z-index: 4;
}
.data-table-wrapper .input-group-text {
color: #94a3b8;
/* Slate 400 */
}
.data-table-wrapper .form-control::placeholder {
color: #cbd5e1;
/* Slate 300 */
}
.dataTables_wrapper .pagination .page-item .page-link {
border: 1px solid transparent;
border-radius: 6px;
margin: 0 2px;
color: #64748b;
padding: 0;
font-size: 0.875rem;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
height: 38px;
min-width: 38px;
}
.dataTables_wrapper .pagination .page-item.active .page-link {
background-color: #0f172a;
/* Slate 900 or Primary Color */
color: white;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-color: #0f172a;
}
.dataTables_wrapper .pagination .page-item.disabled .page-link {
color: #cbd5e1;
background-color: transparent;
}
.dataTables_wrapper .pagination .page-item:not(.active) .page-link:hover {
background-color: #f8fafc;
color: #0f172a;
border-color: #e2e8f0;
}
/* Select2 Customization for Table Length */
.dataTables_length {
margin-right: 1rem;
}
.select2-container--default .select2-selection--single {
border: 1px solid #e2e8f0;
border-radius: 6px;
height: 32px;
display: flex;
align-items: center;
outline: none;
}
.select2-container--default .select2-selection--single:focus {
border-color: #94a3b8;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 30px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #475569;
font-size: 0.8125rem;
padding-left: 10px;
line-height: normal;
}
/* Processing Indicator with improved look - Centered */
div.dataTables_processing {
position: absolute;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%);
width: auto !important;
margin: 0 !important;
padding: 1rem 2rem;
text-align: center;
font-size: 1.2em;
background: rgba(255, 255, 255, 0.9);
border-radius: 8px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #e2e8f0;
color: #0f172a;
}
</style>
@endpush