feat: bulk delete in product archive (v0.327)

- Add bulk_delete_permanent() endpoint (POST ids[], returns JSON)
- Checkbox column + bulk action bar with count label
- Select-all in table header, confirmation dialog before delete
- 2 new tests for bulk_delete_permanent method signature

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 20:37:22 +01:00
parent c59501603d
commit 0a14c92109
5 changed files with 201 additions and 0 deletions

View File

@@ -1,4 +1,26 @@
<style type="text/css">
.bulk-action-bar {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
margin-bottom: 10px;
background: #fff3cd;
border: 1px solid #ffc107;
border-radius: 4px;
}
.bulk-action-bar__info {
font-weight: 600;
color: #856404;
}
.table-col-bulk-check {
width: 36px;
padding-left: 10px !important;
padding-right: 10px !important;
}
.product-archive-thumb-wrap {
display: inline-block;
}
@@ -96,5 +118,119 @@
$popup.removeClass('is-visible');
$popupImage.attr('src', '');
});
// --- Bulk select ---
var $table = $('.table-list-table');
var $bar = $('#js-bulk-action-bar');
var $label = $bar.find('.js-bulk-count-label');
// Inject select-all checkbox into _checkbox column header
$table.find('thead th.table-col-bulk-check').html(
'<input type="checkbox" id="js-bulk-select-all" title="Zaznacz wszystkie">'
);
function updateBar() {
var count = $table.find('.js-bulk-check:checked').length;
if (count > 0) {
$label.text('Zaznaczono: ' + count);
$bar.show();
} else {
$bar.hide();
}
}
$(document).on('change.bulkSelect', '#js-bulk-select-all', function() {
var checked = $(this).is(':checked');
$table.find('.js-bulk-check').prop('checked', checked);
updateBar();
});
$(document).on('change.bulkSelect', '.js-bulk-check', function() {
var total = $table.find('.js-bulk-check').length;
var checked = $table.find('.js-bulk-check:checked').length;
$('#js-bulk-select-all').prop('indeterminate', checked > 0 && checked < total);
$('#js-bulk-select-all').prop('checked', checked === total && total > 0);
updateBar();
});
$(document).on('click.bulkDelete', '.js-bulk-delete-btn', function() {
var ids = [];
$table.find('.js-bulk-check:checked').each(function() {
ids.push($(this).val());
});
if (ids.length === 0) {
return;
}
var confirmMsg = 'UWAGA! Operacja nieodwracalna!\n\n'
+ 'Wybrane produkty (' + ids.length + ' szt.) zostaną trwale usunięte razem ze wszystkimi zdjęciami i załącznikami z serwera.\n\n'
+ 'Czy na pewno chcesz usunąć zaznaczone produkty?';
var doDelete = function() {
var $btn = $('.js-bulk-delete-btn');
$btn.prop('disabled', true).text('Usuwanie…');
var formData = [];
for (var i = 0; i < ids.length; i++) {
formData.push('ids%5B%5D=' + encodeURIComponent(ids[i]));
}
$.ajax({
url: '/admin/product_archive/bulk_delete_permanent/',
type: 'POST',
data: formData.join('&'),
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function(resp) {
if (resp && resp.deleted > 0) {
window.location.reload();
} else {
alert('Nie udało się usunąć produktów. Spróbuj ponownie.');
$btn.prop('disabled', false).html('<i class="fa fa-trash-o"></i> Usuń zaznaczone trwale');
}
},
error: function() {
alert('Błąd podczas usuwania produktów. Spróbuj ponownie.');
$btn.prop('disabled', false).html('<i class="fa fa-trash-o"></i> Usuń zaznaczone trwale');
}
});
};
if (typeof $.confirm === 'function') {
$.confirm({
title: 'Potwierdzenie',
content: confirmMsg,
type: 'red',
boxWidth: '560px',
useBootstrap: false,
animation: 'scale',
closeAnimation: 'scale',
backgroundDismissAnimation: 'shake',
container: 'body',
theme: 'modern',
columnClass: '',
typeAnimated: true,
lazyOpen: false,
draggable: false,
closeIcon: true,
containerFluid: true,
escapeKey: true,
backgroundDismiss: true,
buttons: {
cancel: {
text: 'Anuluj',
btnClass: 'btn-default'
},
confirm: {
text: 'Tak, usuń trwale',
btnClass: 'btn-danger',
action: doDelete
}
}
});
} else if (window.confirm(confirmMsg)) {
doDelete();
}
});
})(window.jQuery);
</script>

View File

@@ -1,3 +1,10 @@
<div id="js-bulk-action-bar" class="bulk-action-bar" style="display:none;">
<span class="bulk-action-bar__info js-bulk-count-label">Zaznaczono: 0</span>
<button type="button" class="btn btn-danger btn-sm js-bulk-delete-btn">
<i class="fa fa-trash-o"></i> Usuń zaznaczone trwale
</button>
</div>
<?= \Shared\Tpl\Tpl::view('components/table-list', ['list' => $this->viewModel]); ?>
<?php if (!empty($this->viewModel->customScriptView)): ?>