From 905ceb0ba56337732a7ee79ee6f7560caf662a8f Mon Sep 17 00:00:00 2001 From: armiejean Date: Wed, 21 May 2025 23:41:40 +0800 Subject: [PATCH] top up batch delete works --- app/Http/Controllers/TopUpController.php | 149 +++++++------ .../components/top-up-component.blade.php | 199 +++++++++--------- routes/web.php | 3 +- 3 files changed, 185 insertions(+), 166 deletions(-) diff --git a/app/Http/Controllers/TopUpController.php b/app/Http/Controllers/TopUpController.php index 08d68eb..c0f34c8 100644 --- a/app/Http/Controllers/TopUpController.php +++ b/app/Http/Controllers/TopUpController.php @@ -11,62 +11,69 @@ class TopUpController extends Controller { protected $apiBaseUrl = 'http://192.168.100.6:8081/api'; - public function index(Request $request) -{ - try { - $user = Session::get('user'); - $accessToken = $user['access_token'] ?? null; + public function index(Request $request) + { + try { + $user = Session::get('user'); + $accessToken = $user['access_token'] ?? null; - if (!$accessToken) { - Log::info('No access token found, redirecting to login from top-up'); - return redirect()->route('login')->with('error', 'Please log in to view top-ups.'); - } + if (!$accessToken) { + Log::info('No access token found, redirecting to login from top-up'); + return redirect()->route('login')->with('error', 'Please log in to view top-ups.'); + } - // Get the requested page from the request (default to 1) - $page = $request->input('page', 1); - $perPage = 5; // Set to 5 items per page + $page = $request->input('page', 1); + $perPage = 5; - $response = Http::withHeaders([ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer ' . $accessToken, - ])->get("{$this->apiBaseUrl}/cms/topUp", [ - 'page' => $page, - 'per_page' => $perPage, - ]); - - if ($response->status() === 401 || $response->status() === 403) { - Log::warning('Unauthorized or Forbidden API response: ', $response->json()); - return redirect()->route('login')->with('error', 'Your session has expired. Please log in again.'); - } - - $json = $response->json(); - - Log::info("TopUp API Response (Page {$page}): ", $json); - - if ($response->successful() && isset($json['data']) && is_array($json['data'])) { - $topups = array_map(function ($topup) { - Log::info('Processing top-up record: ', $topup); - return [ - 'topup_uuid' => $topup['topup_uuid'] ?? $topup['id'] ?? null, - 'freeCode' => $topup['fee_code'] ?? 'N/A', - 'name' => $topup['name'] ?? 'Unnamed', - 'value' => $topup['amount'] ?? 0, - 'type' => $topup['type'] ?? 'Unknown', - ]; - }, $json['data']); - - // Pass pagination metadata to the view - $total = $json['meta']['total'] ?? count($topups); // Total items, adjust based on API response - $lastPage = $json['meta']['last_page'] ?? ceil($total / $perPage); // Calculate last page - - return view('pages.top-up', [ - 'topups' => $topups, - 'currentPage' => $page, - 'lastPage' => $lastPage, - 'total' => $total, + $response = Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer ' . $accessToken, + ])->get("{$this->apiBaseUrl}/cms/topUp", [ + 'page' => $page, + 'per_page' => $perPage, ]); - } else { - Log::warning('No top-up data found or invalid API response: ', $json); + + if ($response->status() === 401 || $response->status() === 403) { + Log::warning('Unauthorized or Forbidden API response: ', $response->json()); + return redirect()->route('login')->with('error', 'Your session has expired. Please log in again.'); + } + + $json = $response->json(); + + Log::info("TopUp API Response (Page {$page}): ", $json); + + if ($response->successful() && isset($json['data']) && is_array($json['data'])) { + $topups = array_map(function ($topup) { + Log::info('Processing top-up record: ', $topup); + return [ + 'topup_uuid' => $topup['topup_uuid'] ?? $topup['id'] ?? null, + 'freeCode' => $topup['fee_code'] ?? 'N/A', + 'name' => $topup['name'] ?? 'Unnamed', + 'value' => $topup['amount'] ?? 0, + 'type' => $topup['type'] ?? 'Unknown', + ]; + }, $json['data']); + + $total = $json['meta']['total'] ?? count($topups); + $lastPage = $json['meta']['last_page'] ?? ceil($total / $perPage); + + return view('pages.top-up', [ + 'topups' => $topups, + 'currentPage' => $page, + 'lastPage' => $lastPage, + 'total' => $total, + ]); + } else { + Log::warning('No top-up data found or invalid API response: ', $json); + return view('pages.top-up', [ + 'topups' => [], + 'currentPage' => 1, + 'lastPage' => 1, + 'total' => 0, + ]); + } + } catch (\Exception $e) { + Log::error('Error fetching top-up data: ' . $e->getMessage()); return view('pages.top-up', [ 'topups' => [], 'currentPage' => 1, @@ -74,16 +81,8 @@ class TopUpController extends Controller 'total' => 0, ]); } - } catch (\Exception $e) { - Log::error('Error fetching top-up data: ' . $e->getMessage()); - return view('pages.top-up', [ - 'topups' => [], - 'currentPage' => 1, - 'lastPage' => 1, - 'total' => 0, - ]); } -} + public function create() { try { @@ -321,37 +320,49 @@ class TopUpController extends Controller $accessToken = $user['access_token'] ?? null; if (!$accessToken) { + Log::info('No access token found, redirecting to login from batch delete'); return redirect()->route('login')->with('error', 'Please log in to delete top-ups.'); } $uuids = $request->input('topup_uuid', []); + if (is_string($uuids)) { + $uuids = json_decode($uuids, true); + if (json_last_error() !== JSON_ERROR_NONE) { + Log::warning('Invalid JSON format for topup_uuid', ['input' => $uuids]); + return redirect()->back()->with('error', 'Invalid top-up UUID format.'); + } + } - if (empty($uuids)) { + if (empty($uuids) || !is_array($uuids)) { + Log::warning('No valid topup_uuids provided for batch delete', ['uuids' => $uuids]); return redirect()->back()->with('error', 'No top-ups selected for deletion.'); } - Log::info('Batch delete UUIDs: ', $uuids); + Log::info('Attempting batch delete for UUIDs: ', ['uuids' => $uuids]); $response = Http::withHeaders([ 'Accept' => 'application/json', 'Authorization' => 'Bearer ' . $accessToken, + 'Content-Type' => 'application/json', ])->delete("{$this->apiBaseUrl}/cms/topUpBatchDelete", [ 'topup_uuid' => $uuids, ]); $json = $response->json(); - if ($response->successful()) { - Log::info('Batch delete successful for UUIDs: ', $uuids); + Log::info('Batch delete response: ', ['response' => $json, 'status' => $response->status(), 'headers' => $response->headers()]); + + if ($response->successful() && isset($json['status']) && $json['status'] === 'success') { + Log::info('Batch delete successful for UUIDs: ', ['uuids' => $uuids, 'response' => $json]); return redirect()->route('top-up') ->with('success', $json['message'] ?? 'Top-ups deleted successfully'); } else { - Log::error('Failed to batch delete top-ups: ', $json); - return redirect()->back()->with('error', $json['message'] ?? 'Failed to delete top-ups.'); + Log::error('Batch delete failed: ', ['response' => $json, 'status' => $response->status(), 'headers' => $response->headers()]); + return redirect()->back()->with('error', $json['message'] ?? 'Failed to delete top-ups. Status: ' . $response->status()); } } catch (\Exception $e) { - Log::error('Error in batch delete: ' . $e->getMessage()); - return redirect()->back()->with('error', 'An error occurred while deleting top-ups.'); + Log::error('Error in batch delete: ', ['error' => $e->getMessage(), 'uuids' => $uuids ?? [], 'trace' => $e->getTraceAsString()]); + return redirect()->back()->with('error', 'An error occurred while deleting top-ups: ' . $e->getMessage()); } } } \ No newline at end of file diff --git a/resources/views/components/top-up-component.blade.php b/resources/views/components/top-up-component.blade.php index ee4460b..c4b89b5 100644 --- a/resources/views/components/top-up-component.blade.php +++ b/resources/views/components/top-up-component.blade.php @@ -217,8 +217,8 @@ total: {{ $total }}, }; - const rowsPerPage = 5; // Fixed at 5 per page - let currentPage = tableConfig.currentPage; // Initialize with server-side page + const rowsPerPage = 5; + let currentPage = tableConfig.currentPage; let filteredRows = [...tableConfig.data]; let originalRows = [...tableConfig.data].map(row => ({ ...row })); let sortDirection = {}; @@ -228,54 +228,56 @@ if (!tableBody) return; tableBody.innerHTML = ''; - // Use the data passed from the server (already paginated to 5) - const paginatedRows = filteredRows; // No client-side slicing needed + const paginatedRows = filteredRows; - paginatedRows.forEach(row => { - const tr = document.createElement('tr'); - tr.setAttribute('data-id', row.topup_uuid || row.id); - tr.classList.add('clickable-row'); - let rowHtml = ''; + if (paginatedRows.length === 0) { + tableBody.innerHTML = `No data available`; + } else { + paginatedRows.forEach(row => { + const tr = document.createElement('tr'); + tr.setAttribute('data-id', row.topup_uuid || row.id); + tr.classList.add('clickable-row'); + let rowHtml = ''; - if (tableConfig.showCheckboxes) { - rowHtml += ``; - } + if (tableConfig.showCheckboxes) { + rowHtml += ``; + } - tableConfig.columns.forEach(col => { - let value = row[col.key] || ''; - rowHtml += `${value}`; - }); - - if (tableConfig.actions.length > 0) { - rowHtml += ``; - tableConfig.actions.forEach(action => { - if (action === 'edit') { - rowHtml += ` - - - `; - } else if (action === 'view') { - rowHtml += ` - - - `; - } else if (action === 'delete') { - rowHtml += ` - `; - } + tableConfig.columns.forEach(col => { + let value = row[col.key] || ''; + rowHtml += `${value}`; }); - rowHtml += ``; - } - tr.innerHTML = rowHtml; - tableBody.appendChild(tr); - }); + if (tableConfig.actions.length > 0) { + rowHtml += ``; + tableConfig.actions.forEach(action => { + if (action === 'edit') { + rowHtml += ` + + + `; + } else if (action === 'view') { + rowHtml += ` + + + `; + } else if (action === 'delete') { + rowHtml += ` + `; + } + }); + rowHtml += ``; + } + + tr.innerHTML = rowHtml; + tableBody.appendChild(tr); + }); + } attachEventListeners(); updateDeleteButtonState(); - updateNoDataMessage(); } function renderPagination() { @@ -317,15 +319,17 @@ pagination.appendChild(nextLi); } - function updateNoDataMessage() { - const noDataMessage = document.getElementById('no-data-message'); - if (noDataMessage) { - noDataMessage.style.display = filteredRows.length === 0 ? 'block' : 'none'; + function updateDeleteButtonState() { + if (tableConfig.showBatchDelete) { + const checkboxes = document.querySelectorAll('.rowCheckbox:checked'); + const deleteButton = document.getElementById('deleteSelected'); + if (deleteButton) { + deleteButton.disabled = checkboxes.length === 0; + } } } function attachEventListeners() { - // Search const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.addEventListener('input', function() { @@ -335,13 +339,12 @@ value && value.toString().toLowerCase().includes(searchTerm) ); }); - currentPage = 1; // Reset to first page on search + currentPage = 1; renderTable(); renderPagination(); }); } - // Sort document.querySelectorAll('.sortable').forEach(header => { header.addEventListener('click', function() { const columnIndex = parseInt(this.getAttribute('data-column')) - (tableConfig.showCheckboxes ? 1 : 0); @@ -365,13 +368,12 @@ return sortDirection[columnIndex] === 'asc' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); }); - currentPage = 1; // Reset to first page on sort + currentPage = 1; renderTable(); renderPagination(); }); }); - // Clear Filters const clearFilters = document.getElementById('clearFilters'); if (clearFilters) { clearFilters.addEventListener('click', function() { @@ -388,7 +390,6 @@ }); } - // Checkboxes if (tableConfig.showCheckboxes) { const selectAll = document.getElementById('selectAll'); if (selectAll) { @@ -406,40 +407,43 @@ }); } - // Batch Delete - if (tableConfig.showBatchDelete) { - const deleteSelected = document.getElementById('deleteSelected'); - if (deleteSelected) { - deleteSelected.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('.rowCheckbox:checked'); - const selectedIds = Array.from(checkboxes) - .map(cb => cb.closest('tr').getAttribute('data-id')); + if (tableConfig.showBatchDelete) { + const deleteSelected = document.getElementById('deleteSelected'); + if (deleteSelected) { + deleteSelected.addEventListener('click', function() { + const checkboxes = document.querySelectorAll('.rowCheckbox:checked'); + const selectedIds = Array.from(checkboxes).map(cb => cb.value); - if (selectedIds.length === 0) { - alert('Please select at least one top-up to delete.'); - return; - } - - if (confirm('Are you sure you want to delete the selected top-ups?')) { - axios.delete('{{ route('top-up.batchDelete') }}', { - headers: { - 'X-CSRF-TOKEN': tableConfig.csrfToken - }, - data: { - topup_uuid: selectedIds - } - }).then(response => { - window.location.reload(); // Reload to fetch new data - }).catch(error => { - console.error('Batch delete error:', error); - alert(error.response?.data?.message || 'Failed to delete top-ups.'); - }); - } - }); + if (selectedIds.length === 0) { + alert('Please select at least one top-up to delete.'); + return; } - } - // Delete + if (confirm('Are you sure you want to delete the selected top-ups?')) { + console.log('Submitting batch delete form with UUIDs:', selectedIds); + const form = document.createElement('form'); + form.method = 'POST'; + form.action = '{{ route('top-up.batchDelete') }}'; + form.style.display = 'none'; + + const csrfInput = document.createElement('input'); + csrfInput.type = 'hidden'; + csrfInput.name = '_token'; + csrfInput.value = tableConfig.csrfToken; + form.appendChild(csrfInput); + + const uuidInput = document.createElement('input'); + uuidInput.type = 'hidden'; + uuidInput.name = 'topup_uuid'; + uuidInput.value = JSON.stringify(selectedIds); + form.appendChild(uuidInput); + + document.body.appendChild(form); + form.submit(); + } + }); + } +} document.querySelectorAll('.delete-btn').forEach(button => { button.addEventListener('click', function(e) { e.stopPropagation(); @@ -450,7 +454,22 @@ 'X-CSRF-TOKEN': tableConfig.csrfToken } }).then(response => { - window.location.reload(); // Reload to fetch new data + if (response.data.status === 'success') { + filteredRows = filteredRows.filter(row => row.topup_uuid !== topupId); + tableConfig.data = tableConfig.data.filter(row => row.topup_uuid !== topupId); + tableConfig.total -= 1; + tableConfig.lastPage = Math.ceil(tableConfig.total / rowsPerPage); + + if (filteredRows.length === 0 && currentPage > 1) { + window.location.href = `{{ route('top-up') }}?page=${currentPage - 1}`; + } else { + renderTable(); + renderPagination(); + alert(response.data.message || 'Top-up deleted successfully'); + } + } else { + alert(response.data.message || 'Failed to delete top-up.'); + } }).catch(error => { console.error('Delete error:', error); alert(error.response?.data?.message || 'Failed to delete top-up.'); @@ -459,7 +478,6 @@ }); }); - // Row click to view document.querySelectorAll('.clickable-row').forEach(row => { row.addEventListener('click', function(e) { if (e.target.closest('.rowCheckbox, .edit-btn, .view-btn, .delete-btn')) { @@ -471,17 +489,6 @@ }); } - function updateDeleteButtonState() { - if (tableConfig.showBatchDelete) { - const checkboxes = document.querySelectorAll('.rowCheckbox'); - const checkedCount = Array.from(checkboxes).filter(cb => cb.checked).length; - const deleteButton = document.getElementById('deleteSelected'); - if (deleteButton) { - deleteButton.disabled = checkedCount < 1; - } - } - } - renderTable(); renderPagination(); diff --git a/routes/web.php b/routes/web.php index 6abad72..c1059f3 100644 --- a/routes/web.php +++ b/routes/web.php @@ -146,7 +146,8 @@ Route::get('/top-up/{uuid}', [TopUpController::class, 'show'])->name('top-up.sho Route::get('/top-up/{uuid}/edit', [TopUpController::class, 'edit'])->name('top-up.edit'); Route::put('/top-up/{uuid}', [TopUpController::class, 'update'])->name('top-up.update'); Route::delete('/top-up/{uuid}', [TopUpController::class, 'destroy'])->name('top-up.destroy'); -Route::delete('/top-up/batch', [TopUpController::class, 'batchDelete'])->name('top-up.batchDelete'); +Route::post('top-up/batchDelete', [TopUpController::class, 'batchDelete'])->name('top-up.batchDelete'); + //Photo Slider Route::get('/photo-slider', [PhotoSliderController::class, 'index'])->name('photo-slider');