cms-frontend/resources/views/components/reports-component.blade.php

339 lines
13 KiB
PHP

@props([
'pageTitle' => '',
'data' => [],
'columns' => [],
'allFields' => [],
'actions' => [],
'showAddButton' => false,
'addButtonUrl' => '#',
'showCheckboxes' => false,
'showBatchDelete' => false,
'showSearch' => true,
'currentPage' => 1,
'lastPage' => 1,
'total' => 0,
])
<div class="card-body">
<!-- Filters -->
<div class="d-flex justify-content-between mb-3 flex-wrap gap-2">
<div class="d-flex align-items-center gap-2">
@if ($showSearch)
<div class="d-flex flex-column">
<label for="searchInput" class="form-label mb-1">Search</label>
<div class="input-group input-group-sm">
<span class="input-group-text bg-light border-end-0">
<i class="fa-solid fa-magnifying-glass text-muted"></i>
</span>
<input type="text" class="form-control border-start-0" placeholder="Search..." id="searchInput" data-search-param="_search" value="{{ request('_search') }}">
</div>
</div>
@endif
</div>
<div class="d-flex gap-2 align-items-end">
<button class="btn btn-outline-secondary btn-sm" id="clearFilters">
<i class="fa-solid fa-filter-circle-xmark me-1"></i> Clear Filters
</button>
</div>
</div>
<!-- No Data Message -->
@if (empty($data))
<div class="alert alert-info" id="no-data-message">No data available.</div>
@endif
<!-- Table -->
<div class="table-container">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
@foreach ($columns as $index => $column)
<th class="{{ $column['sortable'] ? 'sortable' : '' }}" data-column="{{ $index }}" data-key="{{ $column['key'] }}">
{{ $column['name'] }}
@if ($column['sortable'])
<i class="fa-solid fa-sort"></i>
@endif
</th>
@endforeach
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<!-- Pagination -->
<div class="d-flex justify-content-end mt-4">
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm" id="pagination"></ul>
</nav>
</div>
</div>
<style>
.card, .table, .btn, .form-control, .input-group-text {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
font-weight: 400;
line-height: 1.5;
}
.table-container {
overflow-x: auto;
}
.table th, .table td {
white-space: nowrap;
}
.sortable {
cursor: pointer;
position: relative;
}
.sortable:hover {
background-color: #f8f9fa;
}
.sortable i {
margin-left: 5px;
color: #6c757d;
}
.btn-export-csv {
background-color: #ff6200;
color: white;
border: none;
}
.btn-export-csv:hover {
background-color: #e65a00;
}
.input-group-text {
background-color: #f8f9fa;
}
</style>
<script>
const tableConfig = {
data: @json($data),
columns: @json($columns),
pageTitle: @json($pageTitle),
csrfToken: '{{ csrf_token() }}',
currentPage: {{ $currentPage }},
lastPage: {{ $lastPage }},
total: {{ $total }},
apiEndpoint: '{{ route( Str::kebab(str_replace(" ", "-", strtolower($pageTitle)))) }}',
exportEndpoint: '{{ route( Str::kebab(str_replace(" ", "-", strtolower($pageTitle)))) }}Export',
};
console.log('Table Config:', tableConfig); // Log initial config
const rowsPerPage = 5;
let currentPage = tableConfig.currentPage;
let filteredRows = [...tableConfig.data];
let sortColumn = '';
let sortDirection = '';
function formatDateForDisplay(dateStr, isDateTime = false) {
if (!dateStr) return '';
const date = new Date(dateStr);
if (isDateTime) {
return date.toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: '2-digit',
}).replace(/,/, '');
}
function renderTable() {
console.log('Rendering table with filteredRows:', filteredRows); // Log data
const tableBody = document.getElementById('tableBody');
if (!tableBody) {
console.error('Table body not found');
return;
}
tableBody.innerHTML = '';
if (filteredRows.length === 0) {
tableBody.innerHTML = `<tr><td colspan="${tableConfig.columns.length}">No data to display</td></tr>`;
console.log('No data to render');
return;
}
filteredRows.forEach(row => {
const tr = document.createElement('tr');
let rowHtml = '';
tableConfig.columns.forEach(col => {
let value = row[col.key] || '';
if (col.format === 'date' || col.key.includes('DateTime')) {
value = formatDateForDisplay(value, col.key.includes('DateTime'));
}
rowHtml += `<td>${value}</td>`;
});
tr.innerHTML = rowHtml;
tableBody.appendChild(tr);
});
}
function renderPagination() {
console.log('Rendering pagination:', { currentPage, lastPage: tableConfig.lastPage }); // Log pagination
const pagination = document.getElementById('pagination');
if (!pagination) {
console.error('Pagination element not found');
return;
}
pagination.innerHTML = '';
const routeBase = tableConfig.pageTitle.toLowerCase().replace(/\s+/g, '-');
const searchTerm = document.getElementById('searchInput')?.value || '';
const startDate = document.getElementById('startDate')?.value || '';
const endDate = document.getElementById('endDate')?.value || '';
const prevLi = document.createElement('li');
prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
prevLi.innerHTML = `<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">«</span></a>`;
prevLi.addEventListener('click', (e) => {
e.preventDefault();
if (currentPage > 1) {
updateUrl(currentPage - 1, searchTerm, startDate, endDate);
}
});
pagination.appendChild(prevLi);
for (let i = 1; i <= tableConfig.lastPage; i++) {
const li = document.createElement('li');
li.className = `page-item ${currentPage === i ? 'active' : ''}`;
li.innerHTML = `<a class="page-link" href="#">${i}</a>`;
li.addEventListener('click', (e) => {
e.preventDefault();
updateUrl(i, searchTerm, startDate, endDate);
});
pagination.appendChild(li);
}
const nextLi = document.createElement('li');
nextLi.className = `page-item ${currentPage === tableConfig.lastPage ? 'disabled' : ''}`;
nextLi.innerHTML = `<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">»</span></a>`;
nextLi.addEventListener('click', (e) => {
e.preventDefault();
if (currentPage < tableConfig.lastPage) {
updateUrl(currentPage + 1, searchTerm, startDate, endDate);
}
});
pagination.appendChild(nextLi);
}
function updateUrl(page, search, startDate, endDate) {
console.log('Updating URL:', { page, search, startDate, endDate, sortColumn, sortDirection }); // Log URL update
const url = new URL(window.location);
url.searchParams.set('page', page);
if (search) url.searchParams.set('_search', search);
else url.searchParams.delete('_search');
if (startDate) url.searchParams.set('date_start', startDate);
else url.searchParams.delete('date_start');
if (endDate) url.searchParams.set('date_end', endDate);
else url.searchParams.delete('date_end');
if (sortColumn && sortDirection) {
url.searchParams.set('sort', `${sortColumn}|${sortDirection}`);
} else {
url.searchParams.delete('sort');
}
window.location.href = url.toString();
}
function attachEventListeners() {
const searchInput = document.getElementById('searchInput');
if (searchInput) {
searchInput.addEventListener('input', function() {
console.log('Search input changed:', this.value); // Log search
const url = new URL(window.location);
const searchTerm = this.value;
if (searchTerm) {
url.searchParams.set('_search', searchTerm);
} else {
url.searchParams.delete('_search');
}
url.searchParams.set('page', 1);
window.location.href = url.toString();
});
}
document.querySelectorAll('.sortable').forEach(header => {
header.addEventListener('click', function() {
const columnKey = this.getAttribute('data-key');
sortDirection = sortColumn === columnKey && sortDirection === 'asc' ? 'desc' : 'asc';
sortColumn = columnKey;
console.log('Sorting:', { columnKey, sortDirection }); // Log sorting
document.querySelectorAll('.sortable i').forEach(icon => {
icon.classList.remove('fa-sort-up', 'fa-sort-down');
icon.classList.add('fa-sort');
});
const icon = this.querySelector('i');
if (icon) {
icon.classList.remove('fa-sort');
icon.classList.add(sortDirection === 'asc' ? 'fa-sort-up' : 'fa-sort-down');
}
updateUrl(1, document.getElementById('searchInput')?.value || '',
document.getElementById('startDate')?.value || '',
document.getElementById('endDate')?.value || '');
});
});
const startDateInput = document.getElementById('startDate');
const endDateInput = document.getElementById('endDate');
if (startDateInput && endDateInput) {
startDateInput.addEventListener('change', () => {
console.log('Start date changed:', startDateInput.value); // Log date
updateUrl(1, document.getElementById('searchInput')?.value || '', startDateInput.value, endDateInput.value);
});
endDateInput.addEventListener('change', () => {
console.log('End date changed:', endDateInput.value); // Log date
updateUrl(1, document.getElementById('searchInput')?.value || '', startDateInput.value, endDateInput.value);
});
}
const clearFilters = document.getElementById('clearFilters');
if (clearFilters) {
clearFilters.addEventListener('click', function() {
console.log('Clearing filters'); // Log filter clear
if (searchInput) searchInput.value = '';
if (startDateInput) startDateInput.value = '';
if (endDateInput) endDateInput.value = '';
sortColumn = '';
sortDirection = '';
document.querySelectorAll('.sortable i').forEach(icon => {
icon.classList.remove('fa-sort-up', 'fa-sort-down');
icon.classList.add('fa-sort');
});
updateUrl(1, '', '', '');
});
}
const exportCsvBtn = document.getElementById('exportCsv');
if (exportCsvBtn) {
exportCsvBtn.addEventListener('click', () => {
console.log('Exporting CSV'); // Log export
const url = new URL(tableConfig.exportEndpoint, window.location.origin);
const searchTerm = document.getElementById('searchInput')?.value || '';
const startDate = document.getElementById('startDate')?.value || '';
const endDate = document.getElementById('endDate')?.value || '';
if (searchTerm) url.searchParams.set('_search', searchTerm);
if (startDate) url.searchParams.set('date_start', startDate);
if (endDate) url.searchParams.set('date_end', endDate);
if (sortColumn && sortDirection) {
url.searchParams.set('sort', `${sortColumn}|${sortDirection}`);
}
console.log('Export URL:', url.toString()); // Log export URL
window.location.href = url.toString();
});
}
}
renderTable();
renderPagination();
attachEventListeners();
</script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>