355 lines
10 KiB
PHP
355 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Components;
|
|
|
|
use Livewire\Component;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Session;
|
|
|
|
class Table extends Component
|
|
{
|
|
// Table Configuration
|
|
public $columns = [];
|
|
public $rows = [];
|
|
public $selected = [];
|
|
public $addRoute = null;
|
|
public $hasAddButton = true;
|
|
public $hasExportButton = true;
|
|
public $hasDelete = true;
|
|
public $selectAll = false;
|
|
public $hasCheckbox = true;
|
|
public $hasActions = false;
|
|
public $isViewPage = false;
|
|
public $search = '';
|
|
public $sortField = null;
|
|
public $sortDirection = 'asc';
|
|
public bool $hasSearch = true;
|
|
|
|
//Update and Delete endpoints
|
|
public $deleteEndpoint = '';
|
|
public $deleteAllEndpoint = '';
|
|
public bool $confirmingBulkDelete = false;
|
|
|
|
|
|
// Data identifier
|
|
public $rowKey = 'id';
|
|
|
|
public $startDate;
|
|
public $endDate;
|
|
|
|
// Pagination Configuration
|
|
public $perPage = 10;
|
|
public $page = 1;
|
|
public $renderKey = 0; // Dummy property to force re-render
|
|
|
|
// Modal Configuration
|
|
public $showModal = false;
|
|
public $modalMode = 'view';
|
|
public $modalData = [];
|
|
public $confirmingDeleteId = null;
|
|
|
|
public $updateRoute = null;
|
|
public $viewRoute = null;
|
|
|
|
|
|
/**
|
|
* LIVEWIRE LIFECYCLE METHOD - called once when the component is initialized or being rendered
|
|
* Initializes columns, rows (cast to array), add route link, and the row identifier key
|
|
*/
|
|
public function mount($columns, $rows, $addRoute = null, $rowKey = 'id')
|
|
{
|
|
$this->columns = $columns;
|
|
$this->rows = collect($rows)->map(function ($row) {
|
|
return is_array($row) ? $row : (array) $row;
|
|
})->values()->all();
|
|
$this->addRoute = $addRoute;
|
|
$this->rowKey = $rowKey;
|
|
}
|
|
|
|
/**
|
|
* ACTION FUNCTIONALITIES [READ, UPDATE, DELETE]
|
|
*/
|
|
|
|
//VIEW
|
|
public function viewRow($id)
|
|
{
|
|
// $this->modalData = collect($this->rows)->firstWhere($this->rowKey, $id) ?? [];
|
|
// $this->modalMode = 'view';
|
|
// $this->showModal = true;
|
|
return redirect()->to($this->viewRoute . '?uuid=' . $id);
|
|
}
|
|
|
|
//UPDATE || EDIT
|
|
public function editRow($id)
|
|
{
|
|
return redirect()->to($this->updateRoute . '?uuid=' . $id);
|
|
}
|
|
|
|
//DELETE, HANDLES DELETE ENDPOINT
|
|
public function deleteRow($id)
|
|
{
|
|
$token = Session::get('user')['access_token'] ?? null;
|
|
|
|
$url = rtrim(config('services.backend_api.url'), '/') . '/' . $this->deleteEndpoint . '/' . $id;
|
|
|
|
try {
|
|
$response = Http::withToken($token)->delete($url);
|
|
|
|
if ($response->successful()) {
|
|
$this->fetchData();
|
|
} else {
|
|
$errors = $response->json('errors') ?? [];
|
|
foreach ($errors as $field => $messages) {
|
|
$this->addError("modalData.$field", is_array($messages) ? $messages[0] : $messages);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
logger()->error('DeleteRow error: ' . $e->getMessage());
|
|
$this->addError('modalData', 'Something went wrong. Please try again.');
|
|
}
|
|
}
|
|
|
|
//SETS THE ID OF THE ITEM FOR DECISIONING
|
|
public function confirmDelete($id)
|
|
{
|
|
$this->confirmingDeleteId = $id;
|
|
}
|
|
|
|
//HANDLES DELETE CONFIRMATION AND CALLS deleteRow() FUNCTION
|
|
public function deleteConfirmed()
|
|
{
|
|
if (!$this->confirmingDeleteId) return;
|
|
|
|
$this->deleteRow($this->confirmingDeleteId);
|
|
|
|
// Reset the confirmation state
|
|
$this->confirmingDeleteId = null;
|
|
|
|
$this->forceRender();
|
|
}
|
|
|
|
/**
|
|
* EXPORT THE DATA FROM THE TABLE
|
|
*/
|
|
public function export()
|
|
{
|
|
$filename = 'export-data.csv';
|
|
$columns = collect($this->columns)->pluck('label')->toArray();
|
|
$rows = $this->getFilteredRows()->map(function ($row) {
|
|
return collect($this->columns)->map(function ($column) use ($row) {
|
|
return $row[$column['field']] ?? '';
|
|
})->toArray();
|
|
});
|
|
|
|
return response()->streamDownload(function () use ($columns, $rows) {
|
|
$handle = fopen('php://output', 'w');
|
|
fputcsv($handle, $columns);
|
|
foreach ($rows as $row) {
|
|
fputcsv($handle, $row);
|
|
}
|
|
fclose($handle);
|
|
}, $filename, [
|
|
'Content-Type' => 'text/csv',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* MODAL OPEN OR CLOSE FUNCTIONALITY
|
|
*/
|
|
public function closeModal()
|
|
{
|
|
$this->showModal = false;
|
|
$this->modalData = [];
|
|
}
|
|
|
|
/**
|
|
* SEARCH
|
|
*/
|
|
public function updatedSearch()
|
|
{
|
|
$this->selected = [];
|
|
$this->page = 1;
|
|
$this->forceRender();
|
|
}
|
|
|
|
/**
|
|
* SORT BY START AND END DATE
|
|
*/
|
|
public function updatedStartDate()
|
|
{
|
|
$this->page = 1;
|
|
$this->forceRender();
|
|
}
|
|
|
|
public function updatedEndDate()
|
|
{
|
|
$this->page = 1;
|
|
$this->forceRender();
|
|
}
|
|
|
|
/**
|
|
* SELECT ROW AND SELECT ALL ROWS
|
|
*/
|
|
public function selectRow($rowId)
|
|
{
|
|
if (in_array($rowId, $this->selected)) {
|
|
$this->selected = array_diff($this->selected, [$rowId]);
|
|
} else {
|
|
$this->selected[] = $rowId;
|
|
}
|
|
}
|
|
|
|
public function confirmBulkDelete()
|
|
{
|
|
if (empty($this->selected)) return;
|
|
$this->confirmingBulkDelete = true;
|
|
}
|
|
|
|
public function deleteAllSelected()
|
|
{
|
|
if (empty($this->selected)) return;
|
|
|
|
$token = Session::get('user')['access_token'] ?? null;
|
|
$url = rtrim(config('services.backend_api.url'), '/') . '/' . $this->deleteAllEndpoint;
|
|
|
|
try {
|
|
$payload = [
|
|
$this->rowKey => $this->selected,
|
|
];
|
|
|
|
$response = Http::withToken($token)->delete($url, $payload);
|
|
|
|
if ($response->successful()) {
|
|
$this->selected = [];
|
|
$this->selectAll = false;
|
|
$this->confirmingBulkDelete = false;
|
|
$this->fetchData();
|
|
} else {
|
|
$errors = $response->json('errors') ?? [];
|
|
foreach ($errors as $field => $messages) {
|
|
$this->addError("bulkDelete.$field", is_array($messages) ? $messages[0] : $messages);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
logger()->error('Bulk delete error: ' . $e->getMessage());
|
|
$this->addError('bulkDelete', 'Something went wrong while deleting selected rows.');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
public function selectAllRows()
|
|
{
|
|
$currentRows = $this->getPaginatedRows();
|
|
if ($this->selectAll) {
|
|
$this->selected = collect($currentRows)->pluck($this->rowKey)->toArray();
|
|
} else {
|
|
$this->selected = [];
|
|
}
|
|
}
|
|
|
|
public function sortBy($field)
|
|
{
|
|
if ($this->sortField === $field) {
|
|
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
|
} else {
|
|
$this->sortField = $field;
|
|
$this->sortDirection = 'asc';
|
|
}
|
|
$this->page = 1;
|
|
$this->forceRender();
|
|
}
|
|
|
|
public function gotoPage($page)
|
|
{
|
|
$totalRows = $this->getFilteredRows()->count();
|
|
$lastPage = max(1, ceil($totalRows / $this->perPage));
|
|
$newPage = min(max(1, (int) $page), $lastPage);
|
|
$this->page = $newPage;
|
|
$this->forceRender();
|
|
Log::info("Page updated to: {$this->page}, Total Rows: {$totalRows}, Last Page: {$lastPage}");
|
|
}
|
|
|
|
private function forceRender()
|
|
{
|
|
$this->renderKey++; // Increment to force Livewire to re-render
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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', [
|
|
'rows' => $paginatedRows,
|
|
'totalRows' => $total,
|
|
'perPage' => $this->perPage,
|
|
'currentPage' => $this->page,
|
|
'lastPage' => $lastPage,
|
|
'renderKey' => $this->renderKey,
|
|
]);
|
|
}
|
|
}
|