Files
adsPRO/templates/products/main_view.php
Jacek Pyziak 4635cefcbb feat: update font to Roboto across templates and add campaign/ad group filters in product views
- Changed font from Open Sans to Roboto in layout files.
- Added campaign and ad group filters in products main view.
- Enhanced product history to include campaign and ad group IDs.
- Updated migrations to support new campaign and ad group dimensions in product statistics.
- Introduced new migration files for managing campaign types and dropping obsolete columns.
2026-02-18 01:21:22 +01:00

702 lines
26 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="products-page">
<div class="products-header">
<h2><i class="fa-solid fa-box-open"></i> Produkty</h2>
</div>
<!-- Filtry -->
<div class="products-filters">
<div class="filter-group filter-group-client">
<label for="client_id"><i class="fa-solid fa-building"></i> Klient</label>
<select id="client_id" name="client_id" class="form-control">
<option value="">— wybierz klienta —</option>
<?php foreach ( $this -> clients as $client ): ?>
<option value="<?= $client['id']; ?>"><?= htmlspecialchars( $client['name'] ); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="filter-group filter-group-campaign">
<label for="products_campaign_id"><i class="fa-solid fa-bullhorn"></i> Kampania</label>
<select id="products_campaign_id" name="products_campaign_id" class="form-control">
<option value="">- wszystkie kampanie -</option>
</select>
</div>
<div class="filter-group filter-group-ad-group">
<label for="products_ad_group_id"><i class="fa-solid fa-layer-group"></i> Grupa reklam</label>
<select id="products_ad_group_id" name="products_ad_group_id" class="form-control">
<option value="">- wszystkie grupy -</option>
</select>
</div>
<div class="filter-group filter-group-roas">
<label for="bestseller_min_roas"><i class="fa-solid fa-star"></i> Bestseller min ROAS</label>
<input type="text" id="bestseller_min_roas" name="bestseller_min_roas" class="form-control" placeholder="np. 500" value="" />
</div>
</div>
<!-- Akcje bulk -->
<div class="products-actions">
<button type="button" class="btn btn-danger btn-sm" id="delete-selected-products" disabled>
<i class="fa-solid fa-trash"></i> Usuń zaznaczone (<span id="selected-count">0</span>)
</button>
</div>
<!-- Tabela produktów -->
<div class="products-table-wrap">
<table class="table table-sm" id="products">
<thead>
<tr>
<th><input type="checkbox" id="select-all-products" title="Zaznacz wszystkie" /></th>
<th>Id</th>
<th>Id oferty</th>
<th>Kampania</th>
<th>Grupa reklam</th>
<th>Nazwa produktu</th>
<th>WyÅw.</th>
<th>WyÅw. (30d)</th>
<th>Klik.</th>
<th>Klik. (30d)</th>
<th>CTR</th>
<th>Koszt</th>
<th>CPC</th>
<th>Konw.</th>
<th>Wart. konw.</th>
<th>ROAS</th>
<th>Min. ROAS</th>
<th>CL3</th>
<th>CL4</th>
<th>Akcje</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<?php
$openai_enabled = \services\GoogleAdsApi::get_setting( 'openai_enabled' ) !== '0';
$claude_enabled = \services\GoogleAdsApi::get_setting( 'claude_enabled' ) !== '0';
?>
<script type="text/javascript">
var AI_OPENAI_ENABLED = <?= $openai_enabled ? 'true' : 'false'; ?>;
var AI_CLAUDE_ENABLED = <?= $claude_enabled ? 'true' : 'false'; ?>;
function show_toast( message, type )
{
var bg = type === 'error' ? '#dc3545' : '#28a745';
var icon = type === 'error' ? 'fa-circle-xmark' : 'fa-circle-check';
var $toast = $( '<div class="app-toast"><i class="fa-solid ' + icon + '"></i> ' + message + '</div>' );
$toast.css({
position: 'fixed', bottom: '30px', right: '30px', zIndex: 99999,
background: bg, color: '#fff', padding: '12px 24px', borderRadius: '8px',
boxShadow: '0 4px 16px rgba(0,0,0,.25)', fontSize: '14px', fontWeight: 500,
opacity: 0, transform: 'translateY(20px)', transition: 'all .3s ease'
});
$( 'body' ).append( $toast );
setTimeout( function() { $toast.css({ opacity: 1, transform: 'translateY(0)' }); }, 10 );
setTimeout( function() {
$toast.css({ opacity: 0, transform: 'translateY(20px)' });
setTimeout( function() { $toast.remove(); }, 300 );
}, 3000 );
}
var GOOGLE_TAXONOMY_ENDPOINT = '/tools/google-taxonomy.php';
var googleCategories = [];
function loadGoogleCategories( callback )
{
if ( googleCategories.length ) {
callback( googleCategories );
return;
}
$.ajax({
url: GOOGLE_TAXONOMY_ENDPOINT,
dataType: 'json',
success: function( res ) {
if ( res.status === 'ok' ) {
googleCategories = res.categories || [];
callback( googleCategories );
} else {
callback( [] );
}
},
error: function() { callback( [] ); }
});
}
$( function()
{
var products_table = new DataTable( '#products', {
ajax: {
type: 'POST',
url: '/products/get_products/',
data: function( d ) {
d.client_id = $( '#client_id' ).val() || '';
d.campaign_id = $( '#products_campaign_id' ).val() || '';
d.ad_group_id = $( '#products_ad_group_id' ).val() || '';
}
},
processing: true,
serverSide: true,
autoWidth: false,
searching: false,
lengthChange: false,
pageLength: 25,
columns: [
{ width: '30px', orderable: false, className: 'select-checkbox', render: function( data, type, row ) {
return '<input type="checkbox" class="product-checkbox" value="' + row[1] + '" />';
}
},
{ width: '50px', orderable: false },
{ width: '80px', name: 'offer_id' },
{ width: '200px', name: 'campaign_name' },
{ width: '200px', name: 'ad_group_name' },
{ name: 'name' },
{ width: '50px', name: 'impressions' },
{ width: '80px', name: 'impressions_30' },
{ width: '50px', name: 'clicks' },
{ width: '80px', name: 'clicks_30' },
{ width: '50px', name: 'ctr' },
{ width: '80px', name: 'cost', className: "dt-type-numeric" },
{ width: '50px', name: 'cpc', className: "dt-type-numeric" },
{ width: '50px', name: 'conversions' },
{ width: '90px', name: 'conversions_value', className: "dt-type-numeric" },
{ width: '60px', name: 'roas' },
{ width: '70px', name: 'min_roas' },
{ width: '50px', name: 'cl3', orderable: false },
{ width: '120px', orderable: false },
{ width: '50px', orderable: false, className: 'dt-center' }
],
order: [ [ 8, 'desc' ] ],
language: {
processing: '£adowanie...',
emptyTable: 'Brak produktów do wyœwietlenia',
info: 'Produkty _START_ - _END_ z _TOTAL_',
infoEmpty: '',
paginate: {
first: 'Pierwsza',
last: 'Ostatnia',
next: 'Dalej',
previous: 'Wstecz'
}
}
});
function reload_products_table()
{
products_table.ajax.reload( null, false );
}
function load_client_bestseller_min_roas( client_id )
{
if ( !client_id )
{
$( '#bestseller_min_roas' ).val( '' );
return;
}
$.ajax({
url: '/products/get_client_bestseller_min_roas/',
type: 'POST',
data: { client_id: client_id },
success: function( response ) {
var data = JSON.parse( response );
$( '#bestseller_min_roas' ).val( data.status == 'ok' ? data.min_roas : '' );
}
});
}
function load_products_campaigns( client_id, selected_campaign_id )
{
var $campaign = $( '#products_campaign_id' );
$campaign.empty().append( '<option value="">- wszystkie kampanie -</option>' );
if ( !client_id )
{
return $.Deferred().resolve().promise();
}
return $.ajax({
url: '/products/get_campaigns_list/client_id=' + client_id,
type: 'GET',
dataType: 'json'
}).done( function( res ) {
( res.campaigns || [] ).forEach( function( row ) {
$campaign.append( '<option value="' + row.id + '">' + row.campaign_name + '</option>' );
} );
if ( selected_campaign_id && $campaign.find( 'option[value="' + selected_campaign_id + '"]' ).length )
{
$campaign.val( selected_campaign_id );
}
} );
}
function load_products_ad_groups( campaign_id, selected_ad_group_id )
{
var $ad_group = $( '#products_ad_group_id' );
$ad_group.empty().append( '<option value="">- wszystkie grupy -</option>' );
if ( !campaign_id )
{
return $.Deferred().resolve().promise();
}
return $.ajax({
url: '/products/get_campaign_ad_groups/campaign_id=' + campaign_id,
type: 'GET',
dataType: 'json'
}).done( function( res ) {
( res.ad_groups || [] ).forEach( function( row ) {
$ad_group.append( '<option value="' + row.id + '">' + row.ad_group_name + '</option>' );
} );
if ( selected_ad_group_id && $ad_group.find( 'option[value="' + selected_ad_group_id + '"]' ).length )
{
$ad_group.val( selected_ad_group_id );
}
} );
}
$( 'body' ).on( 'change', '#client_id', function()
{
var client_id = $( this ).val() || '';
localStorage.setItem( 'products_client_id', client_id );
localStorage.removeItem( 'products_campaign_id' );
localStorage.removeItem( 'products_ad_group_id' );
load_client_bestseller_min_roas( client_id );
load_products_campaigns( client_id, '' ).done( function() {
load_products_ad_groups( '', '' ).done( function() {
reload_products_table();
} );
} );
});
$( 'body' ).on( 'change', '#products_campaign_id', function()
{
var campaign_id = $( this ).val() || '';
localStorage.setItem( 'products_campaign_id', campaign_id );
localStorage.removeItem( 'products_ad_group_id' );
load_products_ad_groups( campaign_id, '' ).done( function() {
reload_products_table();
} );
});
$( 'body' ).on( 'change', '#products_ad_group_id', function()
{
var ad_group_id = $( this ).val() || '';
localStorage.setItem( 'products_ad_group_id', ad_group_id );
reload_products_table();
});
var savedClient = localStorage.getItem( 'products_client_id' ) || '';
var savedCampaign = localStorage.getItem( 'products_campaign_id' ) || '';
var savedAdGroup = localStorage.getItem( 'products_ad_group_id' ) || '';
if ( savedClient && $( '#client_id option[value="' + savedClient + '"]' ).length )
{
$( '#client_id' ).val( savedClient );
}
load_client_bestseller_min_roas( $( '#client_id' ).val() || '' );
load_products_campaigns( $( '#client_id' ).val() || '', savedCampaign ).done( function() {
var selected_campaign_id = $( '#products_campaign_id' ).val() || '';
load_products_ad_groups( selected_campaign_id, savedAdGroup ).done( function() {
reload_products_table();
} );
});
// Usuwanie produktu
$( 'body' ).on( 'click', '.delete-product', function( e )
{
e.preventDefault();
var product_id = $( this ).attr( 'product_id' );
var row = $( this ).closest( 'tr' );
$.confirm({
title: 'Potwierdzenie',
content: 'Czy na pewno chcesz usunąć ten produkt?',
type: 'red',
buttons: {
confirm: {
text: 'Usuń',
btnClass: 'btn-red',
keys: ['enter'],
action: function() {
$.ajax({
url: '/products/delete_product/',
type: 'POST',
data: { product_id: product_id },
success: function( response ) {
data = JSON.parse( response );
if ( data.status == 'ok' ) {
$.alert({
title: 'Sukces',
content: 'Produkt zostaŠusunięty.',
type: 'green',
autoClose: 'ok|2000',
buttons: { ok: function() {} }
});
var table = $( '#products' ).DataTable();
table.row( row ).remove().draw( false );
} else {
$.alert({ title: 'BÅÄ…d', content: response, type: 'red' });
}
},
error: function() {
$.alert({ title: 'BÅÄ…d', content: 'WystÄ…piÅÄ…d podczas usuwania produktu.', type: 'red' });
}
});
}
},
cancel: { text: 'Anuluj' }
}
});
});
// Zapis min ROAS produktu
$( 'body' ).on( 'change', '.min_roas', function()
{
var product_id = $( this ).attr( 'product_id' );
var min_roas = $( this ).val();
$.ajax({
url: '/products/save_min_roas/',
type: 'POST',
data: { product_id: product_id, min_roas: min_roas }
});
});
// Zapis custom_label_4
$( 'body' ).on( 'change', '.custom_label_4', function()
{
var product_id = $( this ).attr( 'product_id' );
var custom_label_4 = $( this ).val();
$.ajax({
url: '/products/save_custom_label_4/',
type: 'POST',
data: { product_id: product_id, custom_label_4: custom_label_4 }
});
});
// Edycja produktu (tytuÅ, opis, kategoria Google)
$( 'body' ).on( 'click', '.edit-product-title', function( e )
{
$.confirm({
title: 'Edytuj produkt',
content: '' +
'<form action="" class="formName">' +
'<div class="form-group">' +
'<label>TytuÅ produktu</label>' +
'<div class="input-with-ai">' +
'<input type="text" value="" product_id="' + $( this ).attr( 'product_id' ) + '" placeholder="TytuÅ produktu" class="name form-control" required />' +
( AI_OPENAI_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest" data-field="title" data-provider="openai" title="Zaproponuj tytuÅ przez ChatGPT"><i class="fa-solid fa-wand-magic-sparkles"></i> GPT</button>' : '' ) +
( AI_CLAUDE_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest btn-ai-claude" data-field="title" data-provider="claude" title="Zaproponuj tytuÅ przez Claude"><i class="fa-solid fa-brain"></i> Claude</button>' : '' ) +
'</div>' +
'<small>0/150 znaków</small>' +
'</div>' +
'<div class="form-group">' +
'<label>URL strony produktu <small class="text-muted">(opcjonalnie, dla lepszego kontekstu AI)</small></label>' +
'<input type="url" class="form-control product-url" placeholder="https://sklep.pl/produkt/..." />' +
'</div>' +
'<div class="form-group">' +
'<div class="desc-header">' +
'<label>Opis produktu</label>' +
'<div class="desc-tabs">' +
'<button type="button" class="desc-tab active" data-tab="edit"><i class="fa-solid fa-code"></i> Edycja</button>' +
'<button type="button" class="desc-tab" data-tab="preview"><i class="fa-solid fa-eye"></i> PodglÄ…d</button>' +
'</div>' +
'</div>' +
'<div class="input-with-ai">' +
'<div class="desc-wrap">' +
'<textarea class="form-control description" style="height:220px;resize:vertical" placeholder="Opis produktu (opcjonalnie)"></textarea>' +
'<div class="desc-preview" style="display:none;height:220px;overflow-y:auto;padding:10px 12px;border:1px solid #ddd;border-radius:4px;background:#fff;font-size:13px;line-height:1.6"></div>' +
'</div>' +
( AI_OPENAI_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest" data-field="description" data-provider="openai" title="Zaproponuj opis przez ChatGPT"><i class="fa-solid fa-wand-magic-sparkles"></i> GPT</button>' : '' ) +
( AI_CLAUDE_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest btn-ai-claude" data-field="description" data-provider="claude" title="Zaproponuj opis przez Claude"><i class="fa-solid fa-brain"></i> Claude</button>' : '' ) +
'</div>' +
'</div>' +
'<div class="form-group">' +
'<label>Kategoria Google</label>' +
'<div class="input-with-ai">' +
'<select class="form-control google-category" id="google_category" style="width: calc(100% - 60px)">' +
'<option value="">— wybierz kategorię —</option>' +
'</select>' +
( AI_OPENAI_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest" data-field="category" data-provider="openai" title="Zaproponuj kategoriÄ™ przez ChatGPT"><i class="fa-solid fa-wand-magic-sparkles"></i> GPT</button>' : '' ) +
( AI_CLAUDE_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest btn-ai-claude" data-field="category" data-provider="claude" title="Zaproponuj kategoriÄ™ przez Claude"><i class="fa-solid fa-brain"></i> Claude</button>' : '' ) +
'</div>' +
'</div>' +
'</form>',
useBootstrap: false,
boxWidth: '1280px',
theme: 'modern',
draggable: true,
buttons: {
formSubmit: {
text: 'Zapisz',
btnClass: 'btn-blue',
action: function() {
var jc = this;
var product_id = this.$content.find( '.name' ).attr( 'product_id' );
var customTitle = this.$content.find( '.name' ).val();
var googleProductCategory = this.$content.find( '.google-category' ).val();
if ( customTitle && customTitle.length > 150 ) {
$.alert( 'Pole tytuŠnie może przekraczać 150 znaków!' );
this.$content.find( '.name' ).addClass( 'is-invalid' );
return false;
}
jc.showLoading( true );
$.ajax({
url: '/products/save_product_data/',
type: 'POST',
data: {
product_id: product_id,
custom_title: customTitle,
custom_description: this.$content.find( '.description' ).val(),
google_product_category: googleProductCategory,
product_url: this.$content.find( '.product-url' ).val()
},
success: function( response ) {
data = JSON.parse( response );
jc.hideLoading();
if ( data.status == 'ok' ) {
jc.close();
show_toast( 'Dane produktu zostaÅy zapisane.', 'success' );
} else {
show_toast( 'BÅÄ…d: ' + response, 'error' );
}
},
error: function() {
jc.hideLoading();
show_toast( 'WystÄ…piÅÄ…d podczas zapisywania. Spróbuj ponownie.', 'error' );
}
});
}
},
cancel: { text: 'Anuluj', btnClass: 'btn-red' }
},
onContentReady: function() {
var jc = this;
var $form = this.$content.find( 'form' );
var $inputField = this.$content.find( '.name' );
var $charCount = this.$content.find( 'small' ).first();
var $description = this.$content.find( '.description' );
var $productUrl = this.$content.find( '.product-url' );
var $googleCategory = this.$content.find( '.google-category' );
var product_id = $inputField.attr( 'product_id' );
$.ajax({
url: '/products/get_product_data/',
type: 'POST',
data: { product_id: product_id },
success: function( response ) {
var data = JSON.parse( response );
if ( data.status == 'ok' ) {
if ( data.product_details.title ) {
$inputField.val( data.product_details.title );
$charCount.text( data.product_details.title.length + '/150 znaków' );
}
if ( data.product_details.description ) {
$description.val( data.product_details.description );
}
if ( data.product_details.product_url ) {
$productUrl.val( data.product_details.product_url );
}
jc.preselectedGoogleCategory = data.product_details.google_product_category || "";
}
}
});
loadGoogleCategories( function( cats ) {
jc.googleCategoriesData = cats;
if ( typeof $.fn.select2 !== 'undefined' ) {
$googleCategory.select2({
placeholder: 'Wpisz fragment nazwy kategorii...',
allowClear: true,
data: cats,
dropdownParent: jc.$content.closest( '.jconfirm-box' )
});
if ( jc.preselectedGoogleCategory ) {
$googleCategory.val( jc.preselectedGoogleCategory ).trigger( 'change' );
}
}
});
$inputField.on( 'input', function() {
var len = $( this ).val().length;
$charCount.text( len + '/150 znaków' );
$( this ).toggleClass( 'is-invalid', len > 150 );
});
// Opis — przeÅÄ…czanie zakÅadek Edycja / PodglÄ…d
var $descPreview = this.$content.find( '.desc-preview' );
this.$content.on( 'click', '.desc-tab', function() {
var tab = $( this ).data( 'tab' );
jc.$content.find( '.desc-tab' ).removeClass( 'active' );
$( this ).addClass( 'active' );
if ( tab === 'preview' ) {
$descPreview.html( $description.val() || '<span style="color:#999">Brak opisu</span>' ).show();
$description.hide();
} else {
$description.show();
$descPreview.hide();
}
});
// AI suggest buttons
this.$content.on( 'click', '.btn-ai-suggest', function() {
var $btn = $( this );
var field = $btn.data( 'field' );
var provider = $btn.data( 'provider' ) || 'openai';
var originalHtml = $btn.html();
var providerLabel = provider === 'claude' ? 'Claude' : 'ChatGPT';
$btn.prop( 'disabled', true ).html( '<i class="fa-solid fa-spinner fa-spin"></i>' );
$.ajax({
url: '/products/ai_suggest/',
type: 'POST',
data: { product_id: product_id, field: field, product_url: $productUrl.val(), provider: provider },
success: function( response ) {
var data = JSON.parse( response );
if ( data.status == 'ok' ) {
if ( field == 'title' ) {
$inputField.val( data.suggestion );
var len = data.suggestion.length;
$charCount.text( len + '/150 znaków' );
$inputField.toggleClass( 'is-invalid', len > 150 );
} else if ( field == 'description' ) {
$description.val( data.suggestion );
} else if ( field == 'category' ) {
var catId = data.suggestion.trim();
if ( $googleCategory.find( 'option[value="' + catId + '"]' ).length ) {
$googleCategory.val( catId ).trigger( 'change' );
} else {
$.alert({ title: 'AI sugestia (' + providerLabel + ')', content: 'Sugerowana kategoria: ' + catId, type: 'blue' });
}
}
if ( data.warning ) {
show_toast( providerLabel + ': ' + data.warning, 'error' );
} else if ( data.page_fetched ) {
show_toast( providerLabel + ': Sugestia wygenerowana z treÅciÄ… strony produktu', 'success' );
} else {
show_toast( providerLabel + ': Sugestia wygenerowana', 'success' );
}
} else {
show_toast( providerLabel + ': ' + ( data.message || 'WystÄ…piÅÄ…d AI.' ), 'error' );
}
},
error: function() {
$.alert({ title: 'BÅÄ…d', content: 'Nie udaÅo siÄ™ poÅÄ…czyć z API ' + providerLabel + '.', type: 'red' });
},
complete: function() {
$btn.prop( 'disabled', false ).html( originalHtml );
}
});
});
$form.on( 'submit', function( e ) {
e.preventDefault();
jc.$$formSubmit.trigger( 'click' );
});
}
});
});
// Zapis min ROAS klienta (bestseller)
$( 'body' ).on( 'blur', '#bestseller_min_roas', function()
{
var min_roas = $( this ).val();
var client_id = $( '#client_id' ).val();
$.ajax({
url: '/products/save_client_bestseller_min_roas/',
type: 'POST',
data: { client_id: client_id, min_roas: min_roas },
success: function( response ) {
data = JSON.parse( response );
if ( data.status == 'ok' ) {
$.alert({ title: 'Zapisano', content: 'Minimalny ROAS bestsellerów zostaŠzapisany.', type: 'green', autoClose: 'ok|2000', buttons: { ok: function() {} } });
}
}
});
});
// Checkbox: zaznacz/odznacz wszystkie
function updateSelectedCount() {
var count = $( '.product-checkbox:checked' ).length;
$( '#selected-count' ).text( count );
$( '#delete-selected-products' ).prop( 'disabled', count === 0 );
}
$( 'body' ).on( 'change', '#select-all-products', function() {
$( '.product-checkbox' ).prop( 'checked', $( this ).is( ':checked' ) );
updateSelectedCount();
});
$( 'body' ).on( 'change', '.product-checkbox', function() {
updateSelectedCount();
var allChecked = $( '.product-checkbox' ).length === $( '.product-checkbox:checked' ).length;
$( '#select-all-products' ).prop( 'checked', allChecked );
});
$( '#products' ).on( 'draw.dt', function() {
$( '#select-all-products' ).prop( 'checked', false );
updateSelectedCount();
});
// Usuwanie zaznaczonych produktów
$( 'body' ).on( 'click', '#delete-selected-products', function()
{
var selectedIds = [];
$( '.product-checkbox:checked' ).each( function() { selectedIds.push( $( this ).val() ); });
if ( selectedIds.length === 0 ) {
$.alert( 'Nie zaznaczono żadnych produktów.' );
return;
}
$.confirm({
title: 'Potwierdzenie',
content: 'Czy na pewno chcesz usunąć ' + selectedIds.length + ' zaznaczonych produktów?',
type: 'red',
buttons: {
confirm: {
text: 'Usuń',
btnClass: 'btn-red',
keys: ['enter'],
action: function() {
$.ajax({
url: '/products/delete_products/',
type: 'POST',
data: { product_ids: selectedIds },
success: function( response ) {
var data = JSON.parse( response );
if ( data.status == 'ok' ) {
$.alert({
title: 'Sukces',
content: 'Usunięto ' + selectedIds.length + ' produktów.',
type: 'green',
autoClose: 'ok|2000',
buttons: { ok: function() {} }
});
$( '#products' ).DataTable().ajax.reload( null, false );
}
}
});
}
},
cancel: { text: 'Anuluj' }
}
});
});
});
</script>