pagination added [not working]
This commit is contained in:
parent
bc8f68a610
commit
d49b0ff407
|
@ -2,50 +2,51 @@
|
||||||
|
|
||||||
namespace App\Livewire\Components;
|
namespace App\Livewire\Components;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class Table extends Component
|
class Table extends Component
|
||||||
{
|
{
|
||||||
// Table Configuration
|
// Table Configuration
|
||||||
public $columns = []; // Columns to display
|
public $columns = [];
|
||||||
public $rows = []; // Data rows for the table
|
public $rows = [];
|
||||||
public $selected = []; // Selected row IDs
|
public $selected = [];
|
||||||
public $addRoute = null; // Route for adding new rows (optional)
|
public $addRoute = null;
|
||||||
public $hasAddButton = true;
|
public $hasAddButton = true;
|
||||||
public $hasDelete = true; // Whether the 'Add' button is shown
|
public $hasDelete = true;
|
||||||
public $selectAll = false; // Whether 'Select All' is active
|
public $selectAll = false;
|
||||||
public $hasCheckbox = true; // Whether checkboxes are shown for row selection
|
public $hasCheckbox = true;
|
||||||
public $hasActions = false; // Whether action buttons (like Edit/Delete) are shown
|
public $hasActions = false;
|
||||||
public $isViewPage = false; // Whether the table is for a view page
|
public $isViewPage = false;
|
||||||
public $search = ''; // Search query for filtering rows
|
public $search = '';
|
||||||
public $sortField = null; // Column to sort by
|
public $sortField = null;
|
||||||
public $sortDirection = 'asc'; // Sorting direction (asc or desc)
|
public $sortDirection = 'asc';
|
||||||
public bool $hasSearch = true;
|
public bool $hasSearch = true;
|
||||||
|
|
||||||
|
|
||||||
public $startDate;
|
public $startDate;
|
||||||
public $endDate;
|
public $endDate;
|
||||||
|
|
||||||
|
// Pagination Configuration
|
||||||
|
public $perPage = 5;
|
||||||
|
public $page = 1;
|
||||||
|
public $renderKey = 0; // Dummy property to force re-render
|
||||||
|
|
||||||
// Modal Configuration
|
// Modal Configuration
|
||||||
public $showModal = false; // Whether the modal is visible
|
public $showModal = false;
|
||||||
public $modalMode = 'view'; // Modal mode: 'view' or 'edit'
|
public $modalMode = 'view';
|
||||||
public $modalData = []; // Data for the modal
|
public $modalData = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the table with given columns and rows
|
|
||||||
*/
|
|
||||||
public function mount($columns, $rows, $addRoute = null)
|
public function mount($columns, $rows, $addRoute = null)
|
||||||
{
|
{
|
||||||
$this->columns = $columns;
|
$this->columns = $columns;
|
||||||
$this->rows = $rows;
|
// Convert $rows to a plain array to ensure consistent pagination
|
||||||
|
$this->rows = collect($rows)->map(function ($row) {
|
||||||
|
return is_array($row) ? $row : (array) $row;
|
||||||
|
})->values()->toArray();
|
||||||
$this->addRoute = $addRoute;
|
$this->addRoute = $addRoute;
|
||||||
|
Log::info("Initial rows count: " . count($this->rows));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* View a row's details in the modal
|
|
||||||
*/
|
|
||||||
public function viewRow($id)
|
public function viewRow($id)
|
||||||
{
|
{
|
||||||
$this->modalData = collect($this->rows)->firstWhere('id', $id) ?? [];
|
$this->modalData = collect($this->rows)->firstWhere('id', $id) ?? [];
|
||||||
|
@ -53,9 +54,6 @@ class Table extends Component
|
||||||
$this->showModal = true;
|
$this->showModal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit a row's details in the modal
|
|
||||||
*/
|
|
||||||
public function editRow($id)
|
public function editRow($id)
|
||||||
{
|
{
|
||||||
$this->modalData = collect($this->rows)->firstWhere('id', $id) ?? [];
|
$this->modalData = collect($this->rows)->firstWhere('id', $id) ?? [];
|
||||||
|
@ -63,38 +61,41 @@ class Table extends Component
|
||||||
$this->showModal = true;
|
$this->showModal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the modal
|
|
||||||
*/
|
|
||||||
public function closeModal()
|
public function closeModal()
|
||||||
{
|
{
|
||||||
$this->showModal = false;
|
$this->showModal = false;
|
||||||
$this->modalData = [];
|
$this->modalData = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the selected rows based on search input
|
|
||||||
*/
|
|
||||||
public function updatedSearch()
|
public function updatedSearch()
|
||||||
{
|
{
|
||||||
$this->selected = [];
|
$this->selected = [];
|
||||||
|
$this->page = 1;
|
||||||
|
$this->forceRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedStartDate()
|
||||||
|
{
|
||||||
|
$this->page = 1;
|
||||||
|
$this->forceRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedEndDate()
|
||||||
|
{
|
||||||
|
$this->page = 1;
|
||||||
|
$this->forceRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Select or deselect all rows
|
|
||||||
*/
|
|
||||||
public function selectAllRows()
|
public function selectAllRows()
|
||||||
{
|
{
|
||||||
|
$currentRows = $this->getPaginatedRows();
|
||||||
if ($this->selectAll) {
|
if ($this->selectAll) {
|
||||||
$this->selected = collect($this->rows)->pluck('id')->toArray();
|
$this->selected = collect($currentRows)->pluck('id')->toArray();
|
||||||
} else {
|
} else {
|
||||||
$this->selected = [];
|
$this->selected = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Select or deselect a single row
|
|
||||||
*/
|
|
||||||
public function selectRow($rowId)
|
public function selectRow($rowId)
|
||||||
{
|
{
|
||||||
if (in_array($rowId, $this->selected)) {
|
if (in_array($rowId, $this->selected)) {
|
||||||
|
@ -104,9 +105,6 @@ class Table extends Component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort rows by a specific field
|
|
||||||
*/
|
|
||||||
public function sortBy($field)
|
public function sortBy($field)
|
||||||
{
|
{
|
||||||
if ($this->sortField === $field) {
|
if ($this->sortField === $field) {
|
||||||
|
@ -115,49 +113,97 @@ class Table extends Component
|
||||||
$this->sortField = $field;
|
$this->sortField = $field;
|
||||||
$this->sortDirection = 'asc';
|
$this->sortDirection = 'asc';
|
||||||
}
|
}
|
||||||
|
$this->page = 1;
|
||||||
$this->rows = collect($this->rows)->sortBy(function ($row) {
|
$this->forceRender();
|
||||||
return $row[$this->sortField] ?? null;
|
|
||||||
}, SORT_REGULAR, $this->sortDirection === 'desc')->values()->all(); // Use all() to return plain array again
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function gotoPage($page)
|
||||||
* Download the data from the tables
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function export()
|
|
||||||
{
|
{
|
||||||
$filename = 'export.csv';
|
$totalRows = $this->getFilteredRows()->count();
|
||||||
|
$lastPage = max(1, ceil($totalRows / $this->perPage));
|
||||||
return response()->streamDownload(function () {
|
$newPage = min(max(1, (int) $page), $lastPage);
|
||||||
// Open output stream
|
$this->page = $newPage;
|
||||||
$handle = fopen('php://output', 'w');
|
$this->forceRender();
|
||||||
|
Log::info("Page updated to: {$this->page}, Total Rows: {$totalRows}, Last Page: {$lastPage}");
|
||||||
// Write the CSV header
|
|
||||||
fputcsv($handle, collect($this->columns)->pluck('label')->toArray());
|
|
||||||
|
|
||||||
// Write the rows
|
|
||||||
foreach ($this->rows as $row) {
|
|
||||||
fputcsv($handle, collect($this->columns)->map(function ($column) use ($row) {
|
|
||||||
return $row[$column['field']] ?? '';
|
|
||||||
})->toArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the output stream
|
private function forceRender()
|
||||||
fclose($handle);
|
{
|
||||||
}, $filename, [
|
$this->renderKey++; // Increment to force Livewire to re-render
|
||||||
'Content-Type' => 'text/csv',
|
}
|
||||||
'Content-Disposition' => "attachment; filename={$filename}",
|
|
||||||
]);
|
private function getFilteredRows()
|
||||||
|
{
|
||||||
|
$filteredRows = collect($this->rows);
|
||||||
|
|
||||||
|
// Apply search filtering
|
||||||
|
if ($this->hasSearch && $this->search) {
|
||||||
|
$filteredRows = $filteredRows->filter(function ($row) {
|
||||||
|
foreach ($this->columns as $column) {
|
||||||
|
if (stripos($row[$column['field']] ?? '', $this->search) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply date filtering
|
||||||
|
if (!$this->hasSearch && ($this->startDate || $this->endDate)) {
|
||||||
|
$filteredRows = $filteredRows->filter(function ($row) {
|
||||||
|
$date = $row['created_at'] ?? null;
|
||||||
|
if ($date) {
|
||||||
|
$date = strtotime($date);
|
||||||
|
$start = $this->startDate ? strtotime($this->startDate) : null;
|
||||||
|
$end = $this->endDate ? strtotime($this->endDate) : null;
|
||||||
|
|
||||||
|
if ($start && $date < $start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($end && $date > $end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply sorting
|
||||||
|
if ($this->sortField) {
|
||||||
|
$filteredRows = $filteredRows->sortBy($this->sortField, SORT_REGULAR, $this->sortDirection === 'desc');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredRows->values();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPaginatedRows()
|
||||||
|
{
|
||||||
|
$filteredRows = $this->getFilteredRows();
|
||||||
|
$start = ($this->page - 1) * $this->perPage;
|
||||||
|
$paginated = $filteredRows->slice($start, $this->perPage)->values()->toArray();
|
||||||
|
// Force Livewire to detect the change by serializing and unserializing
|
||||||
|
$result = json_decode(json_encode($paginated), true);
|
||||||
|
Log::info("Paginated rows count: " . count($result) . ", Start: {$start}, Page: {$this->page}, First Username: " . ($result[0]['username'] ?? 'N/A'));
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the table view
|
|
||||||
*/
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
$filteredRows = $this->getFilteredRows();
|
||||||
|
$total = $filteredRows->count();
|
||||||
|
$lastPage = max(1, ceil($total / $this->perPage));
|
||||||
|
$this->page = min(max(1, $this->page), $lastPage);
|
||||||
|
|
||||||
|
$paginatedRows = $this->getPaginatedRows();
|
||||||
|
Log::info("Rendering - Total Rows: {$total}, Current Page: {$this->page}, Last Page: {$lastPage}, Displayed Rows: " . count($paginatedRows));
|
||||||
|
|
||||||
return view('livewire.components.table', [
|
return view('livewire.components.table', [
|
||||||
'rows' => $this->rows,
|
'rows' => $paginatedRows,
|
||||||
|
'totalRows' => $total,
|
||||||
|
'perPage' => $this->perPage,
|
||||||
|
'currentPage' => $this->page,
|
||||||
|
'lastPage' => $lastPage,
|
||||||
|
'renderKey' => $this->renderKey,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -41,8 +41,7 @@
|
||||||
Export CSV
|
Export CSV
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Table Header -->
|
<!-- Table Header -->
|
||||||
<table class="table-auto w-full">
|
<table class="table-auto w-full">
|
||||||
|
@ -57,16 +56,16 @@
|
||||||
|
|
||||||
<!-- Dynamic Columns (sortable) -->
|
<!-- Dynamic Columns (sortable) -->
|
||||||
@foreach ($columns as $column)
|
@foreach ($columns as $column)
|
||||||
<th class="px-4 py-2 text-left cursor-pointer " wire:click="sortBy('{{ $column['field'] }}')">
|
<th class="px-4 py-2 text-left cursor-pointer" wire:click="sortBy('{{ $column['field'] }}')">
|
||||||
{{ $column['label'] }}
|
{{ $column['label'] }}
|
||||||
@if ($sortField === $column['field'])
|
@if ($sortField === $column['field'])
|
||||||
@if ($sortDirection === 'asc')
|
@if ($sortDirection === 'asc')
|
||||||
<span class="ml-2">↑</span>
|
<span class="ml-2">↑</span>
|
||||||
@else
|
@else
|
||||||
<span class="ml-2">↓</span>
|
<span class="ml-2">↓</span>
|
||||||
@endif
|
@endif
|
||||||
@else
|
@else
|
||||||
<span class="ml-2">↕</span>
|
<span class="ml-2">↕</span>
|
||||||
@endif
|
@endif
|
||||||
</th>
|
</th>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
@ -80,8 +79,11 @@
|
||||||
|
|
||||||
<!-- Table Body -->
|
<!-- Table Body -->
|
||||||
<tbody>
|
<tbody>
|
||||||
@forelse ($rows as $row)
|
@php
|
||||||
<tr class="hover:bg-gray-50 border-b">
|
\Illuminate\Support\Facades\Log::info("Blade rows count: " . count($rows) . ", First Username: " . ($rows[0]['username'] ?? 'N/A'));
|
||||||
|
@endphp
|
||||||
|
@forelse ($rows as $index => $row)
|
||||||
|
<tr class="hover:bg-gray-50 border-b" wire:key="row-{{ $row['id'] }}-{{ $index }}-{{ $renderKey }}">
|
||||||
<!-- Checkbox for row selection (if enabled) -->
|
<!-- Checkbox for row selection (if enabled) -->
|
||||||
@if ($hasCheckbox)
|
@if ($hasCheckbox)
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
|
@ -125,15 +127,57 @@
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- Delete Button, Pagination, and Entries in a Single Row -->
|
||||||
|
<div class="mt-4 flex items-center justify-between gap-4">
|
||||||
<!-- Delete Button -->
|
<!-- Delete Button -->
|
||||||
@if($hasDelete)
|
@if($hasDelete)
|
||||||
<button wire:click="deleteSelected"
|
<button wire:click="deleteSelected"
|
||||||
class="mt-4 px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 cursor-pointer"
|
class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 cursor-pointer"
|
||||||
@if (empty($selected)) disabled @endif>
|
@if (empty($selected)) disabled @endif>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
|
@else
|
||||||
|
<div></div> <!-- Placeholder to maintain layout -->
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<!-- Previous Button -->
|
||||||
|
<button
|
||||||
|
wire:click="gotoPage({{ $currentPage - 1 }})"
|
||||||
|
class="px-3 py-1 border rounded {{ $currentPage <= 1 ? 'bg-gray-200 cursor-not-allowed' : 'bg-white hover:bg-gray-100' }}"
|
||||||
|
{{ $currentPage <= 1 ? 'disabled' : '' }}
|
||||||
|
>
|
||||||
|
Prev
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Page Numbers -->
|
||||||
|
@for ($i = 1; $i <= $lastPage; $i++)
|
||||||
|
<button
|
||||||
|
wire:click="gotoPage({{ $i }})"
|
||||||
|
class="px-3 py-1 border rounded {{ $currentPage === $i ? 'bg-orange-600 text-white' : 'bg-white hover:bg-gray-100' }}"
|
||||||
|
>
|
||||||
|
{{ $i }}
|
||||||
|
</button>
|
||||||
|
@endfor
|
||||||
|
|
||||||
|
<!-- Next Button -->
|
||||||
|
<button
|
||||||
|
wire:click="gotoPage({{ $currentPage + 1 }})"
|
||||||
|
class="px-3 py-1 border rounded {{ $currentPage >= $lastPage ? 'bg-gray-200 cursor-not-allowed' : 'bg-white hover:bg-gray-100' }}"
|
||||||
|
{{ $currentPage >= $lastPage ? 'disabled' : '' }}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Entries Text -->
|
||||||
|
<div class="text-gray-600">
|
||||||
|
Showing {{ ($currentPage - 1) * $perPage + 1 }} to {{ min($currentPage * $perPage, $totalRows) }} of {{ $totalRows }} entries
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal for Viewing / Editing Row -->
|
<!-- Modal for Viewing / Editing Row -->
|
||||||
@if ($showModal)
|
@if ($showModal)
|
||||||
<div class="fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
|
<div class="fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
|
||||||
|
@ -143,7 +187,7 @@
|
||||||
<h2 class="text-lg font-semibold">
|
<h2 class="text-lg font-semibold">
|
||||||
{{ $modalMode === 'view' ? 'View Details' : 'Edit Details' }}
|
{{ $modalMode === 'view' ? 'View Details' : 'Edit Details' }}
|
||||||
</h2>
|
</h2>
|
||||||
<button wire:click="closeModal" class="text-gray-600 hover:text-gray-800">×</button>
|
<button wire:click="closeModal" class="text-gray-600 hover:text-gray-800">×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal Body -->
|
<!-- Modal Body -->
|
||||||
|
|
Loading…
Reference in New Issue