promotion page batch delete works

This commit is contained in:
armiejean 2025-05-21 12:14:58 +08:00
parent 6a4998e64e
commit 0d94f37c4b
3 changed files with 83 additions and 49 deletions

View File

@ -371,13 +371,17 @@ public function store(Request $request)
$accessToken = $user['access_token'] ?? null; $accessToken = $user['access_token'] ?? null;
if (!$accessToken) { if (!$accessToken) {
return redirect()->route('login')->with('error', 'Please log in to delete promotions.'); return response()->json([
'message' => 'Please log in to delete promotions.'
], 401);
} }
$uuids = $request->input('promotion_uuid', []); $uuids = $request->input('promotion_uuid', []);
if (empty($uuids)) { if (!is_array($uuids) || empty($uuids)) {
return redirect()->back()->with('error', 'No promotions selected for deletion.'); return response()->json([
'message' => 'No promotions selected for deletion.'
], 422);
} }
$response = Http::withHeaders([ $response = Http::withHeaders([
@ -389,14 +393,20 @@ public function store(Request $request)
if ($response->successful()) { if ($response->successful()) {
Log::info('Promotions batch deleted successfully: ', $uuids); Log::info('Promotions batch deleted successfully: ', $uuids);
return redirect()->route('promotions')->with('success', 'Selected promotions deleted successfully.'); return response()->json([
} else { 'message' => $response->json()['message'] ?? 'Selected promotions deleted successfully.'
Log::warning('Failed to batch delete promotions: ', $response->json()); ], 200);
return redirect()->back()->with('error', $response->json()['message'] ?? 'Failed to delete promotions. Please try again.');
} }
Log::warning('Failed to batch delete promotions: ', $response->json());
return response()->json([
'message' => $response->json()['message'] ?? 'Failed to delete promotions. Please try again.'
], $response->status());
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error('Error batch deleting promotions: ' . $e->getMessage()); Log::error('Error batch deleting promotions: ' . $e->getMessage());
return redirect()->back()->with('error', 'An error occurred while deleting the promotions.'); return response()->json([
'message' => 'An error occurred while deleting the promotions: ' . $e->getMessage()
], 500);
} }
} }
} }

View File

@ -25,6 +25,9 @@
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<!-- Alerts -->
<div id="alert-container"></div>
<!-- Search and Filters --> <!-- Search and Filters -->
<div class="row mb-3 align-items-center"> <div class="row mb-3 align-items-center">
<div class="col-12 col-md-6 mb-2 mb-md-0"> <div class="col-12 col-md-6 mb-2 mb-md-0">
@ -138,7 +141,6 @@
const rowsPerPage = 5; const rowsPerPage = 5;
let currentPage = tableConfig.currentPage; let currentPage = tableConfig.currentPage;
let filteredRows = [...tableConfig.data]; let filteredRows = [...tableConfig.data];
let originalRows = [...tableConfig.data].map(row => ({ ...row }));
let sortDirection = {}; let sortDirection = {};
function renderTable() { function renderTable() {
@ -200,8 +202,7 @@
if (!pagination) return; if (!pagination) return;
pagination.innerHTML = ''; pagination.innerHTML = '';
const totalPages = Math.ceil(filteredRows.length / rowsPerPage); const totalPages = Math.max(1, Math.ceil(filteredRows.length / rowsPerPage));
const routeName = tableConfig.pageTitle === 'Promotions' ? 'promotions.index' : '';
const prevLi = document.createElement('li'); const prevLi = document.createElement('li');
prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`; prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
@ -210,7 +211,8 @@
e.preventDefault(); e.preventDefault();
if (currentPage > 1) { if (currentPage > 1) {
currentPage--; currentPage--;
window.location.href = `/promotions?page=${currentPage}&_search=${tableConfig.search || ''}`; renderTable();
renderPagination();
} }
}); });
pagination.appendChild(prevLi); pagination.appendChild(prevLi);
@ -222,7 +224,8 @@
li.addEventListener('click', (e) => { li.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
currentPage = i; currentPage = i;
window.location.href = `/promotions?page=${currentPage}&_search=${tableConfig.search || ''}`; renderTable();
renderPagination();
}); });
pagination.appendChild(li); pagination.appendChild(li);
} }
@ -234,16 +237,17 @@
e.preventDefault(); e.preventDefault();
if (currentPage < totalPages) { if (currentPage < totalPages) {
currentPage++; currentPage++;
window.location.href = `/promotions?page=${currentPage}&_search=${tableConfig.search || ''}`; renderTable();
renderPagination();
} }
}); });
pagination.appendChild(nextLi); pagination.appendChild(nextLi);
} }
function updateNoDataMessage() { function updateNoDataMessage() {
const noDataMessage = document.getElementById('no-data-message'); const tableBody = document.getElementById('tableBody');
if (noDataMessage) { if (filteredRows.length === 0) {
noDataMessage.style.display = filteredRows.length === 0 ? 'block' : 'none'; tableBody.innerHTML = `<tr><td colspan="${tableConfig.columns.length + (tableConfig.showCheckboxes ? 1 : 0) + (tableConfig.actions.length > 0 ? 1 : 0)}" class="text-center">No data available</td></tr>`;
} }
} }
@ -260,13 +264,6 @@
currentPage = 1; currentPage = 1;
renderTable(); renderTable();
renderPagination(); renderPagination();
const url = new URL(window.location);
if (searchTerm) {
url.searchParams.set('_search', searchTerm);
} else {
url.searchParams.delete('_search');
}
window.history.pushState({}, '', url);
}); });
} }
@ -317,9 +314,6 @@
currentPage = 1; currentPage = 1;
renderTable(); renderTable();
renderPagination(); renderPagination();
const url = new URL(window.location);
url.searchParams.delete('_search');
window.history.pushState({}, '', url);
}); });
} }
@ -340,30 +334,45 @@
}); });
} }
if (deleteSelected) { if (tableConfig.showBatchDelete && deleteSelected) {
deleteSelected.addEventListener('click', function() { deleteSelected.addEventListener('click', function() {
const selectedUuids = Array.from(document.querySelectorAll('.rowCheckbox:checked')).map(cb => cb.value); const checkboxes = document.querySelectorAll('.rowCheckbox:checked');
if (selectedUuids.length > 0 && confirm('Are you sure you want to delete the selected promotions?')) { const selectedIds = Array.from(checkboxes)
fetch('/promotions/batch-delete', { .map(cb => cb.closest('tr').getAttribute('data-id'));
method: 'POST',
if (selectedIds.length === 0) {
showAlert('danger', 'Please select at least one promotion to delete.');
return;
}
if (confirm('Are you sure you want to delete the selected promotions?')) {
axios({
method: 'post',
url: '{{ route('promotions.batchDelete') }}',
headers: { headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}', 'X-CSRF-TOKEN': tableConfig.csrfToken,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json',
}, },
body: JSON.stringify({ promotion_uuid: selectedUuids }) data: {
}).then(response => response.json()) promotion_uuid: selectedIds
.then(data => { }
if (data.success) { }).then(response => {
location.reload(); // Remove deleted rows from filteredRows and tableConfig.data
} else { filteredRows = filteredRows.filter(row => !selectedIds.includes(row.id));
alert(data.message || 'Error deleting promotions'); tableConfig.data = tableConfig.data.filter(row => !selectedIds.includes(row.id));
} tableConfig.total -= selectedIds.length;
}) tableConfig.lastPage = Math.max(1, Math.ceil(tableConfig.total / rowsPerPage));
.catch(error => { if (currentPage > tableConfig.lastPage) {
console.error('Error:', error); currentPage = tableConfig.lastPage;
alert('An error occurred while deleting the promotions.'); }
}); renderTable();
renderPagination();
showAlert('success', response.data.message || 'Selected promotions deleted successfully.');
}).catch(error => {
console.error('Batch delete error:', error);
const errorMessage = error.response?.data?.message || 'Failed to delete promotions.';
showAlert('danger', errorMessage);
});
} }
}); });
} }
@ -379,10 +388,24 @@
}); });
} }
function showAlert(type, message) {
const alertContainer = document.getElementById('alert-container') || document.querySelector('.card-body');
const alert = document.createElement('div');
alert.className = `alert alert-${type} alert-dismissible fade show`;
alert.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;
alertContainer.prepend(alert);
setTimeout(() => alert.remove(), 5000);
}
function updateDeleteButton() { function updateDeleteButton() {
const deleteSelected = document.getElementById('deleteSelected'); const deleteSelected = document.getElementById('deleteSelected');
const checkedBoxes = document.querySelectorAll('.rowCheckbox:checked'); const checkedBoxes = document.querySelectorAll('.rowCheckbox:checked');
deleteSelected.disabled = checkedBoxes.length === 0; if (deleteSelected) {
deleteSelected.disabled = checkedBoxes.length === 0;
}
} }
renderTable(); renderTable();

View File

@ -157,6 +157,7 @@ Route::put('/photo-slider/{id}', [PhotoSliderController::class, 'update'])->name
Route::get('/photo-slider/{id}', [PhotoSliderController::class, 'show'])->name('photo-slider.show'); Route::get('/photo-slider/{id}', [PhotoSliderController::class, 'show'])->name('photo-slider.show');
Route::delete('/photo-slider/{id}', [PhotoSliderController::class, 'destroy'])->name('photo-slider.destroy'); Route::delete('/photo-slider/{id}', [PhotoSliderController::class, 'destroy'])->name('photo-slider.destroy');
Route::post('photo-slider/batchDelete', [App\Http\Controllers\PhotoSliderController::class, 'batchDelete'])->name('photo-slider.batchDelete'); Route::post('photo-slider/batchDelete', [App\Http\Controllers\PhotoSliderController::class, 'batchDelete'])->name('photo-slider.batchDelete');
//Notification //Notification
Route::get('/notification', [NotificationController::class, 'index'])->name('notification'); Route::get('/notification', [NotificationController::class, 'index'])->name('notification');
Route::get('/notification/create', [NotificationController::class, 'create'])->name('notification.create'); Route::get('/notification/create', [NotificationController::class, 'create'])->name('notification.create');
@ -177,7 +178,7 @@ Route::get('/promotions/{uuid}', [PromotionController::class, 'show'])->name('pr
Route::get('/promotions/{uuid}/edit', [PromotionController::class, 'edit'])->name('promotions.edit'); Route::get('/promotions/{uuid}/edit', [PromotionController::class, 'edit'])->name('promotions.edit');
Route::put('/promotions/{uuid}', [PromotionController::class, 'update'])->name('promotions.update'); Route::put('/promotions/{uuid}', [PromotionController::class, 'update'])->name('promotions.update');
Route::delete('/promotions/{uuid}', [PromotionController::class, 'destroy'])->name('promotions.destroy'); Route::delete('/promotions/{uuid}', [PromotionController::class, 'destroy'])->name('promotions.destroy');
Route::post('/promotions/batch-delete', [PromotionController::class, 'batchDelete'])->name('promotions.batch-delete'); Route::post('/promotions/batch-delete', [PromotionController::class, 'batchDelete'])->name('promotions.batchDelete');
//Terms-and-Privacy //Terms-and-Privacy
Route::get('/terms-and-privacy', [TermsAndPrivacyController::class, 'index'])->name('terms-and-privacy'); Route::get('/terms-and-privacy', [TermsAndPrivacyController::class, 'index'])->name('terms-and-privacy');