feat: Implement campaign synchronization feature with dropdown UI
- Updated SCSS styles for new campaign sync buttons and dropdowns. - Refactored main_view.php to replace the single select for campaigns with a multi-select dropdown. - Added JavaScript functions to handle dropdown interactions and sync status updates. - Introduced sync status bars for clients in main_view.php. - Created new database migrations for client sync flags and cron sync status tracking.
This commit is contained in:
@@ -13,35 +13,24 @@
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="campaign_id"><i class="fa-solid fa-bullhorn"></i> Kampania</label>
|
||||
<div class="filter-group filter-group-campaign-multi">
|
||||
<label><i class="fa-solid fa-bullhorn"></i> Kampania</label>
|
||||
<div class="filter-with-action">
|
||||
<select id="campaign_id" name="campaign_id" class="form-control">
|
||||
<option value="">- wybierz kampanie -</option>
|
||||
</select>
|
||||
<button type="button" id="delete_campaign" class="btn-icon btn-icon-delete" title="Usun kampanie">
|
||||
<select id="campaign_id" name="campaign_id[]" multiple="multiple" style="display:none"></select>
|
||||
<div class="campaign-dropdown" id="campaign_dropdown">
|
||||
<div class="campaign-dropdown-trigger" id="campaign_dropdown_trigger">
|
||||
<span class="campaign-dropdown-text">- wybierz kampanie -</span>
|
||||
<i class="fa-solid fa-chevron-down campaign-dropdown-arrow"></i>
|
||||
</div>
|
||||
<div class="campaign-dropdown-menu" id="campaign_dropdown_menu"></div>
|
||||
</div>
|
||||
<button type="button" id="delete_campaign" class="btn-icon btn-icon-delete" title="Usun zaznaczone kampanie">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="campaigns-list-panel" id="campaigns_list_panel" style="display: none;">
|
||||
<div class="campaigns-list-toolbar">
|
||||
<div class="campaigns-list-toolbar-left">
|
||||
<input type="checkbox" id="campaigns_select_all" title="Zaznacz wszystkie">
|
||||
<label for="campaigns_select_all">Zaznacz wszystkie</label>
|
||||
<span class="campaigns-selected-count">Zaznaczone: <strong id="campaigns_selected_count">0</strong></span>
|
||||
</div>
|
||||
<div class="campaigns-list-toolbar-right">
|
||||
<button type="button" id="campaigns_bulk_delete" class="campaigns-bulk-delete-btn" disabled>
|
||||
<i class="fa-solid fa-trash"></i> Usun zaznaczone
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="campaigns-list-items" id="campaigns_list_items"></div>
|
||||
</div>
|
||||
|
||||
<div class="campaigns-chart-wrap">
|
||||
<div id="container"></div>
|
||||
</div>
|
||||
@@ -95,10 +84,72 @@ function storage_get( key )
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildCampaignDropdown()
|
||||
{
|
||||
var menu = $( '#campaign_dropdown_menu' );
|
||||
menu.empty();
|
||||
|
||||
$( '#campaign_id option' ).each( function()
|
||||
{
|
||||
var val = $( this ).val();
|
||||
var text = $( this ).text();
|
||||
var item = $( '<div class="campaign-dropdown-item"></div>' );
|
||||
var checkbox = $( '<input type="checkbox">' ).val( val );
|
||||
item.append( checkbox ).append( $( '<span></span>' ).text( text ) );
|
||||
menu.append( item );
|
||||
});
|
||||
|
||||
updateCheckboxState();
|
||||
updateDropdownDisplay();
|
||||
}
|
||||
|
||||
function syncDropdownToSelect()
|
||||
{
|
||||
var selected = [];
|
||||
$( '#campaign_dropdown_menu input:checked' ).each( function() {
|
||||
selected.push( $( this ).val() );
|
||||
});
|
||||
$( '#campaign_id' ).val( selected ).trigger( 'change' );
|
||||
updateDropdownDisplay();
|
||||
}
|
||||
|
||||
function updateCheckboxState()
|
||||
{
|
||||
var selected = $( '#campaign_id' ).val() || [];
|
||||
$( '#campaign_dropdown_menu input[type="checkbox"]' ).each( function()
|
||||
{
|
||||
var checked = selected.indexOf( $( this ).val() ) !== -1;
|
||||
$( this ).prop( 'checked', checked );
|
||||
$( this ).closest( '.campaign-dropdown-item' ).toggleClass( 'is-checked', checked );
|
||||
});
|
||||
}
|
||||
|
||||
function updateDropdownDisplay()
|
||||
{
|
||||
var selected = $( '#campaign_id' ).val() || [];
|
||||
var textEl = $( '#campaign_dropdown .campaign-dropdown-text' );
|
||||
|
||||
if ( selected.length === 0 )
|
||||
{
|
||||
textEl.text( '- wybierz kampanie -' ).addClass( 'is-placeholder' );
|
||||
}
|
||||
else if ( selected.length === 1 )
|
||||
{
|
||||
var name = $( '#campaign_id option[value="' + selected[0] + '"]' ).text();
|
||||
textEl.text( name ).removeClass( 'is-placeholder' );
|
||||
}
|
||||
else
|
||||
{
|
||||
var first = $( '#campaign_id option[value="' + selected[0] + '"]' ).text();
|
||||
textEl.text( first + ' (+' + ( selected.length - 1 ) + ')' ).removeClass( 'is-placeholder' );
|
||||
}
|
||||
}
|
||||
|
||||
function reloadChart()
|
||||
{
|
||||
var campaign_id = $( '#campaign_id' ).val();
|
||||
if ( !campaign_id ) return;
|
||||
var vals = $( '#campaign_id' ).val() || [];
|
||||
if ( vals.length !== 1 ) return;
|
||||
var campaign_id = vals[0];
|
||||
|
||||
$.ajax({
|
||||
url: '/campaigns/get_campaign_history_data_table_chart/',
|
||||
@@ -178,111 +229,36 @@ function reloadChart()
|
||||
|
||||
$( function()
|
||||
{
|
||||
function updateCampaignsSelectedCount()
|
||||
$( 'body' ).on( 'click', '#campaign_dropdown_trigger', function( e )
|
||||
{
|
||||
var count = $( '.campaigns-list-item-cb:checked' ).length;
|
||||
$( '#campaigns_selected_count' ).text( count );
|
||||
$( '#campaigns_bulk_delete' ).prop( 'disabled', count === 0 );
|
||||
$( '#campaigns_select_all' ).prop( 'checked', count > 0 && count === $( '.campaigns-list-item-cb' ).length );
|
||||
}
|
||||
|
||||
function buildCampaignsList( campaigns )
|
||||
{
|
||||
var panel = $( '#campaigns_list_panel' );
|
||||
var container = $( '#campaigns_list_items' );
|
||||
container.empty();
|
||||
$( '#campaigns_select_all' ).prop( 'checked', false );
|
||||
updateCampaignsSelectedCount();
|
||||
|
||||
if ( !campaigns.length )
|
||||
{
|
||||
panel.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
campaigns.forEach( function( pair ) {
|
||||
var c = pair[1];
|
||||
var item = $( '<label class="campaigns-list-item">' +
|
||||
'<input type="checkbox" class="campaigns-list-item-cb" value="' + c.id + '" data-name="' + $( '<span>' ).text( c.campaign_name ).html() + '"> ' +
|
||||
'<span class="campaigns-list-item-name">' + $( '<span>' ).text( c.campaign_name ).html() + '</span>' +
|
||||
'</label>' );
|
||||
container.append( item );
|
||||
});
|
||||
|
||||
panel.show();
|
||||
}
|
||||
|
||||
$( 'body' ).on( 'change', '.campaigns-list-item-cb', updateCampaignsSelectedCount );
|
||||
|
||||
$( 'body' ).on( 'change', '#campaigns_select_all', function()
|
||||
{
|
||||
$( '.campaigns-list-item-cb' ).prop( 'checked', $( this ).is( ':checked' ) );
|
||||
updateCampaignsSelectedCount();
|
||||
e.stopPropagation();
|
||||
$( '#campaign_dropdown' ).toggleClass( 'is-open' );
|
||||
});
|
||||
|
||||
$( 'body' ).on( 'click', '#campaigns_bulk_delete', function()
|
||||
$( 'body' ).on( 'change', '#campaign_dropdown_menu input[type="checkbox"]', function()
|
||||
{
|
||||
var checked = $( '.campaigns-list-item-cb:checked' );
|
||||
var count = checked.length;
|
||||
$( this ).closest( '.campaign-dropdown-item' ).toggleClass( 'is-checked', this.checked );
|
||||
syncDropdownToSelect();
|
||||
});
|
||||
|
||||
if ( count === 0 ) return;
|
||||
$( 'body' ).on( 'click', '#campaign_dropdown_menu .campaign-dropdown-item span', function( e )
|
||||
{
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var val = $( this ).siblings( 'input[type="checkbox"]' ).val();
|
||||
$( '#campaign_dropdown_menu input[type="checkbox"]' ).prop( 'checked', false );
|
||||
$( '#campaign_dropdown_menu .campaign-dropdown-item' ).removeClass( 'is-checked' );
|
||||
$( this ).siblings( 'input[type="checkbox"]' ).prop( 'checked', true );
|
||||
$( this ).closest( '.campaign-dropdown-item' ).addClass( 'is-checked' );
|
||||
$( '#campaign_id' ).val( [ val ] ).trigger( 'change' );
|
||||
updateDropdownDisplay();
|
||||
$( '#campaign_dropdown' ).removeClass( 'is-open' );
|
||||
});
|
||||
|
||||
var names = [];
|
||||
checked.each( function() { names.push( $( this ).data( 'name' ) ); } );
|
||||
var namesList = '<ul style="text-align:left; max-height:200px; overflow:auto; margin-top:10px;">';
|
||||
names.forEach( function( n ) { namesList += '<li>' + n + '</li>'; } );
|
||||
namesList += '</ul>';
|
||||
|
||||
$.confirm({
|
||||
title: 'Potwierdzenie usuniecia',
|
||||
content: 'Czy na pewno chcesz usunac <strong>' + count + '</strong> kampanii?' + namesList + '<br>Ta operacja jest nieodwracalna i usunie rowniez cala historie tych kampanii.',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: 'Usun (' + count + ')',
|
||||
btnClass: 'btn-red',
|
||||
action: function()
|
||||
{
|
||||
var ids = [];
|
||||
checked.each( function() { ids.push( $( this ).val() ); } );
|
||||
|
||||
$.ajax({
|
||||
url: '/campaigns/delete_campaigns/',
|
||||
type: 'POST',
|
||||
data: { ids: ids },
|
||||
success: function( response )
|
||||
{
|
||||
var data = JSON.parse( response );
|
||||
if ( data.success )
|
||||
{
|
||||
$.alert({
|
||||
title: 'Sukces',
|
||||
content: 'Usunieto ' + data.deleted + ' kampanii.',
|
||||
type: 'green',
|
||||
autoClose: 'ok|2000'
|
||||
});
|
||||
|
||||
var current_campaign = $( '#campaign_id' ).val();
|
||||
if ( ids.indexOf( current_campaign ) !== -1 )
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, '' );
|
||||
|
||||
$( '#client_id' ).trigger( 'change' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$.alert({
|
||||
title: 'Blad',
|
||||
content: data.message || 'Nie udalo sie usunac kampanii.',
|
||||
type: 'red'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
cancel: { text: 'Anuluj' }
|
||||
}
|
||||
});
|
||||
$( document ).on( 'click', function( e )
|
||||
{
|
||||
if ( !$( e.target ).closest( '#campaign_dropdown' ).length )
|
||||
$( '#campaign_dropdown' ).removeClass( 'is-open' );
|
||||
});
|
||||
|
||||
$( 'body' ).on( 'change', '#client_id', function()
|
||||
@@ -295,14 +271,11 @@ $( function()
|
||||
if ( !campaign_to_restore )
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, '' );
|
||||
|
||||
campaigns_select.empty();
|
||||
campaigns_select.append( '<option value="">- wybierz kampanie -</option>' );
|
||||
campaigns_select.empty().trigger( 'change' );
|
||||
rebuildCampaignDropdown();
|
||||
|
||||
if ( !client_id )
|
||||
{
|
||||
$( '#campaigns_list_panel' ).hide();
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/campaigns/get_campaigns_list/client_id=' + client_id,
|
||||
@@ -324,14 +297,14 @@ $( function()
|
||||
|
||||
campaigns.forEach( function( pair ) {
|
||||
var value = pair[1];
|
||||
campaigns_select.append( '<option value="' + value.id + '">' + value.campaign_name + '</option>' );
|
||||
campaigns_select.append( new Option( value.campaign_name, value.id, false, false ) );
|
||||
});
|
||||
|
||||
buildCampaignsList( campaigns );
|
||||
rebuildCampaignDropdown();
|
||||
|
||||
if ( campaign_to_restore && campaigns_select.find( 'option[value="' + campaign_to_restore + '"]' ).length )
|
||||
if ( campaign_to_restore )
|
||||
{
|
||||
campaigns_select.val( campaign_to_restore ).trigger( 'change' );
|
||||
campaigns_select.val( [ campaign_to_restore ] ).trigger( 'change' );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -340,9 +313,11 @@ $( function()
|
||||
} ).first();
|
||||
|
||||
if ( account_option.length )
|
||||
campaigns_select.val( account_option.val() ).trigger( 'change' );
|
||||
campaigns_select.val( [ account_option.val() ] ).trigger( 'change' );
|
||||
}
|
||||
|
||||
updateCheckboxState();
|
||||
updateDropdownDisplay();
|
||||
restore_campaign_after_client_load = '';
|
||||
}
|
||||
});
|
||||
@@ -350,10 +325,9 @@ $( function()
|
||||
|
||||
$( 'body' ).on( 'click', '#delete_campaign', function()
|
||||
{
|
||||
var campaign_id = $( '#campaign_id' ).val();
|
||||
var campaign_name = $( '#campaign_id option:selected' ).text();
|
||||
var selected = $( '#campaign_id' ).val() || [];
|
||||
|
||||
if ( !campaign_id )
|
||||
if ( !selected.length )
|
||||
{
|
||||
$.alert({
|
||||
title: 'Uwaga',
|
||||
@@ -363,31 +337,54 @@ $( function()
|
||||
return;
|
||||
}
|
||||
|
||||
var names = [];
|
||||
selected.forEach( function( id ) {
|
||||
var text = $( '#campaign_id option[value="' + id + '"]' ).text();
|
||||
names.push( text );
|
||||
});
|
||||
|
||||
var count = selected.length;
|
||||
var msg = '';
|
||||
|
||||
if ( count === 1 )
|
||||
{
|
||||
msg = 'Czy na pewno chcesz usunac kampanie <strong>' + names[0] + '</strong>?';
|
||||
}
|
||||
else
|
||||
{
|
||||
var namesList = '<ul style="text-align:left; max-height:200px; overflow:auto; margin-top:10px;">';
|
||||
names.forEach( function( n ) { namesList += '<li>' + n + '</li>'; } );
|
||||
namesList += '</ul>';
|
||||
msg = 'Czy na pewno chcesz usunac <strong>' + count + '</strong> kampanii?' + namesList;
|
||||
}
|
||||
|
||||
msg += '<br>Ta operacja jest nieodwracalna i usunie rowniez cala historie kampanii.';
|
||||
|
||||
$.confirm({
|
||||
title: 'Potwierdzenie usuniecia',
|
||||
content: 'Czy na pewno chcesz usunac kampanie <strong>' + campaign_name + '</strong>?<br><br>Ta operacja jest nieodwracalna i usunie rowniez cala historie kampanii.',
|
||||
content: msg,
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: 'Usun',
|
||||
text: count === 1 ? 'Usun' : 'Usun (' + count + ')',
|
||||
btnClass: 'btn-red',
|
||||
keys: ['enter'],
|
||||
action: function()
|
||||
{
|
||||
$.ajax({
|
||||
url: '/campaigns/delete_campaign/campaign_id=' + campaign_id,
|
||||
url: '/campaigns/delete_campaigns/',
|
||||
type: 'POST',
|
||||
data: { ids: selected },
|
||||
success: function( response )
|
||||
{
|
||||
var data = JSON.parse( response );
|
||||
if ( data.success )
|
||||
{
|
||||
if ( storage_get( STORAGE_CAMPAIGN_KEY ) === String( campaign_id ) )
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, '' );
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, '' );
|
||||
|
||||
$.alert({
|
||||
title: 'Sukces',
|
||||
content: 'Kampania zostala usunieta.',
|
||||
content: data.deleted === 1 ? 'Kampania zostala usunieta.' : 'Usunieto ' + data.deleted + ' kampanii.',
|
||||
type: 'green',
|
||||
autoClose: 'ok|2000'
|
||||
});
|
||||
@@ -464,8 +461,7 @@ $( function()
|
||||
|
||||
$( 'body' ).on( 'change', '#campaign_id', function()
|
||||
{
|
||||
var campaign_id = $( this ).val();
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, campaign_id );
|
||||
var vals = $( this ).val() || [];
|
||||
|
||||
if ( $.fn.DataTable.isDataTable( '#products' ) )
|
||||
{
|
||||
@@ -473,46 +469,53 @@ $( function()
|
||||
$( '#products tbody' ).empty();
|
||||
}
|
||||
|
||||
if ( !campaign_id )
|
||||
return;
|
||||
if ( vals.length === 1 )
|
||||
{
|
||||
var campaign_id = vals[0];
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, campaign_id );
|
||||
|
||||
new DataTable( '#products', {
|
||||
ajax: {
|
||||
type: 'POST',
|
||||
url: '/campaigns/get_campaign_history_data_table/campaign_id=' + campaign_id,
|
||||
},
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
searching: false,
|
||||
lengthChange: false,
|
||||
pageLength: 15,
|
||||
columns: [
|
||||
{ width: '130px', name: 'date', orderable: false, className: "nowrap" },
|
||||
{ width: '120px', name: 'roas30', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '120px', name: 'roas_all_time', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '180px', name: 'conversion_value', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '140px', name: 'spend30', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: 'auto', name: 'comment', orderable: false },
|
||||
{ width: 'auto', name: 'bidding_strategy', orderable: false },
|
||||
{ width: '100px', name: 'budget', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '60px', name: 'actions', orderable: false, className: "dt-center" }
|
||||
],
|
||||
language: {
|
||||
processing: 'Ladowanie...',
|
||||
emptyTable: 'Brak danych do wyswietlenia',
|
||||
info: 'Wpisy _START_ - _END_ z _TOTAL_',
|
||||
infoEmpty: '',
|
||||
lengthMenu: 'Pokaz _MENU_ wpisow',
|
||||
paginate: {
|
||||
first: 'Pierwsza',
|
||||
last: 'Ostatnia',
|
||||
next: 'Dalej',
|
||||
previous: 'Wstecz'
|
||||
new DataTable( '#products', {
|
||||
ajax: {
|
||||
type: 'POST',
|
||||
url: '/campaigns/get_campaign_history_data_table/campaign_id=' + campaign_id,
|
||||
},
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
searching: false,
|
||||
lengthChange: false,
|
||||
pageLength: 15,
|
||||
columns: [
|
||||
{ width: '130px', name: 'date', orderable: false, className: "nowrap" },
|
||||
{ width: '120px', name: 'roas30', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '120px', name: 'roas_all_time', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '180px', name: 'conversion_value', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '140px', name: 'spend30', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: 'auto', name: 'comment', orderable: false },
|
||||
{ width: 'auto', name: 'bidding_strategy', orderable: false },
|
||||
{ width: '100px', name: 'budget', orderable: false, className: "dt-type-numeric" },
|
||||
{ width: '60px', name: 'actions', orderable: false, className: "dt-center" }
|
||||
],
|
||||
language: {
|
||||
processing: 'Ladowanie...',
|
||||
emptyTable: 'Brak danych do wyswietlenia',
|
||||
info: 'Wpisy _START_ - _END_ z _TOTAL_',
|
||||
infoEmpty: '',
|
||||
lengthMenu: 'Pokaz _MENU_ wpisow',
|
||||
paginate: {
|
||||
first: 'Pierwsza',
|
||||
last: 'Ostatnia',
|
||||
next: 'Dalej',
|
||||
previous: 'Wstecz'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
reloadChart();
|
||||
reloadChart();
|
||||
}
|
||||
else
|
||||
{
|
||||
storage_set( STORAGE_CAMPAIGN_KEY, '' );
|
||||
}
|
||||
});
|
||||
|
||||
var saved_client_id = storage_get( STORAGE_CLIENT_KEY );
|
||||
@@ -525,4 +528,3 @@ $( function()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user