feat: Enhance user settings with cron URL plan display
- Added a new field to display the cron URL plan in user settings. - Updated JavaScript to handle the new plan data. refactor: Unify product model and migrate data - Migrated product data from `products_data` to `products` table. - Added new columns to `products` for better data organization. - Created `products_aggregate` table for storing aggregated product metrics. chore: Drop deprecated products_data table - Removed `products_data` table as data is now stored in `products`. feat: Add merchant URL flags to products - Introduced flags for tracking merchant URL status in `products` table. - Normalized product URLs to handle empty or invalid values. feat: Link campaign alerts to specific products - Added `product_id` column to `campaign_alerts` table for better tracking. - Created an index for efficient querying of alerts by product. chore: Add debug scripts for client data inspection - Created debug scripts to inspect client data from local and remote databases. - Included error handling and output formatting for better readability.
This commit is contained in:
@@ -22,9 +22,14 @@
|
||||
</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 class="ad-group-filter-actions">
|
||||
<select id="products_ad_group_id" name="products_ad_group_id" class="form-control">
|
||||
<option value="">- wszystkie grupy -</option>
|
||||
</select>
|
||||
<button type="button" id="delete-products-ad-group" class="btn-icon btn-icon-delete" title="Usun wybrana grupe reklam" disabled>
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-group filter-group-roas">
|
||||
<label for="bestseller_min_roas"><i class="fa-solid fa-star"></i> Bestseller min ROAS</label>
|
||||
@@ -39,6 +44,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details id="products_scope_alerts_box" class="products-scope-alerts hide">
|
||||
<summary>
|
||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||
Alerty dla wybranej kampanii (i opcjonalnie grupy reklam)
|
||||
(<span id="products_scope_alerts_count">0</span>)
|
||||
</summary>
|
||||
<div class="products-scope-alerts-list" id="products_scope_alerts_list"></div>
|
||||
</details>
|
||||
|
||||
<details id="products_zero_impressions_box" class="products-scope-alerts hide">
|
||||
<summary>
|
||||
<i class="fa-solid fa-magnifying-glass"></i>
|
||||
Produkty do sprawdzenia (0 wyswietlen w ostatnich 30 dniach)
|
||||
(<span id="products_zero_impressions_count">0</span>)
|
||||
</summary>
|
||||
<div class="products-scope-alerts-list">
|
||||
<select id="products_zero_impressions_select" class="form-control" size="8"></select>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Akcje bulk -->
|
||||
<div class="products-actions">
|
||||
<button type="button" class="btn btn-danger btn-sm" id="delete-selected-products" disabled>
|
||||
@@ -351,6 +376,151 @@ $( function()
|
||||
products_table.ajax.reload( null, false );
|
||||
}
|
||||
|
||||
function submit_delete_campaign_ad_group( campaign_id, ad_group_id, delete_scope )
|
||||
{
|
||||
function parse_json_loose( raw )
|
||||
{
|
||||
if ( typeof raw !== 'string' || !raw )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var text = $.trim( raw );
|
||||
if ( !text )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JSON.parse( text );
|
||||
}
|
||||
catch ( e )
|
||||
{
|
||||
var start = text.indexOf( '{' );
|
||||
var end = text.lastIndexOf( '}' );
|
||||
|
||||
if ( start !== -1 && end > start )
|
||||
{
|
||||
try
|
||||
{
|
||||
return JSON.parse( text.substring( start, end + 1 ) );
|
||||
}
|
||||
catch ( e2 ) {}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function handle_success( message )
|
||||
{
|
||||
show_toast( message || 'Grupa reklam zostala usunieta.', 'success' );
|
||||
localStorage.removeItem( 'products_ad_group_id' );
|
||||
load_products_ad_groups( campaign_id, '' ).done( function() {
|
||||
$.when( load_scope_alerts(), load_zero_impressions_products() ).always( function() {
|
||||
reload_products_table();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
function check_if_ad_group_still_exists()
|
||||
{
|
||||
var deferred = $.Deferred();
|
||||
|
||||
if ( !campaign_id || !ad_group_id )
|
||||
{
|
||||
deferred.resolve( true );
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/products/get_campaign_ad_groups/campaign_id=' + campaign_id,
|
||||
type: 'GET',
|
||||
dataType: 'json'
|
||||
}).done( function( res ) {
|
||||
var still_exists = false;
|
||||
|
||||
( res.ad_groups || [] ).forEach( function( row ) {
|
||||
if ( String( row.id || '' ) === String( ad_group_id ) )
|
||||
{
|
||||
still_exists = true;
|
||||
}
|
||||
} );
|
||||
|
||||
deferred.resolve( still_exists );
|
||||
}).fail( function() {
|
||||
deferred.resolve( true );
|
||||
} );
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
var request_data = {
|
||||
campaign_id: campaign_id,
|
||||
ad_group_id: ad_group_id,
|
||||
delete_scope: delete_scope
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/products/delete_campaign_ad_group/',
|
||||
type: 'POST',
|
||||
data: request_data,
|
||||
success: function( response )
|
||||
{
|
||||
var res = ( typeof response === 'object' && response !== null )
|
||||
? response
|
||||
: parse_json_loose( response );
|
||||
|
||||
if ( res && res.status === 'ok' )
|
||||
{
|
||||
handle_success( res.message );
|
||||
}
|
||||
else
|
||||
{
|
||||
check_if_ad_group_still_exists().done( function( still_exists ) {
|
||||
if ( !still_exists )
|
||||
{
|
||||
handle_success( ( res && res.message ) ? res.message : 'Grupa reklam zostala usunieta.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert({
|
||||
title: 'Blad',
|
||||
content: ( res && res.message ) ? res.message : 'Nie udalo sie usunac grupy reklam.',
|
||||
type: 'red'
|
||||
});
|
||||
} );
|
||||
}
|
||||
},
|
||||
error: function( jqXHR )
|
||||
{
|
||||
var res = parse_json_loose( jqXHR && jqXHR.responseText ? jqXHR.responseText : '' );
|
||||
|
||||
if ( res && res.status === 'ok' )
|
||||
{
|
||||
handle_success( res.message );
|
||||
return;
|
||||
}
|
||||
check_if_ad_group_still_exists().done( function( still_exists ) {
|
||||
if ( !still_exists )
|
||||
{
|
||||
handle_success( ( res && res.message ) ? res.message : 'Grupa reklam zostala usunieta.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert({
|
||||
title: 'Blad',
|
||||
content: ( res && res.message ) ? res.message : 'Wystapil blad podczas usuwania grupy reklam.',
|
||||
type: 'red'
|
||||
});
|
||||
} );
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function load_client_bestseller_min_roas( client_id )
|
||||
{
|
||||
if ( !client_id )
|
||||
@@ -386,16 +556,35 @@ $( function()
|
||||
dataType: 'json'
|
||||
}).done( function( res ) {
|
||||
( res.campaigns || [] ).forEach( function( row ) {
|
||||
$campaign.append( '<option value="' + row.id + '">' + row.campaign_name + '</option>' );
|
||||
var channel_type = String( row.advertising_channel_type || '' ).toUpperCase();
|
||||
$campaign.append(
|
||||
'<option value="' + row.id + '" data-channel-type="' + channel_type + '">' + escape_html( row.campaign_name || '' ) + '</option>'
|
||||
);
|
||||
} );
|
||||
|
||||
if ( selected_campaign_id && $campaign.find( 'option[value="' + selected_campaign_id + '"]' ).length )
|
||||
{
|
||||
$campaign.val( selected_campaign_id );
|
||||
}
|
||||
|
||||
update_delete_ad_group_button_state();
|
||||
} );
|
||||
}
|
||||
|
||||
function get_selected_products_campaign_channel_type()
|
||||
{
|
||||
var campaign_id = $( '#products_campaign_id' ).val() || '';
|
||||
|
||||
if ( !campaign_id )
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return String(
|
||||
$( '#products_campaign_id option[value="' + campaign_id + '"]' ).attr( 'data-channel-type' ) || ''
|
||||
).toUpperCase();
|
||||
}
|
||||
|
||||
function load_products_ad_groups( campaign_id, selected_ad_group_id )
|
||||
{
|
||||
var $ad_group = $( '#products_ad_group_id' );
|
||||
@@ -403,6 +592,7 @@ $( function()
|
||||
|
||||
if ( !campaign_id )
|
||||
{
|
||||
update_delete_ad_group_button_state();
|
||||
return $.Deferred().resolve().promise();
|
||||
}
|
||||
|
||||
@@ -419,6 +609,168 @@ $( function()
|
||||
{
|
||||
$ad_group.val( selected_ad_group_id );
|
||||
}
|
||||
|
||||
update_delete_ad_group_button_state();
|
||||
} );
|
||||
}
|
||||
|
||||
function update_delete_ad_group_button_state()
|
||||
{
|
||||
var campaign_channel_type = get_selected_products_campaign_channel_type();
|
||||
var ad_group_id = $( '#products_ad_group_id' ).val() || '';
|
||||
var is_shopping_campaign = campaign_channel_type === 'SHOPPING';
|
||||
var can_delete = is_shopping_campaign && ad_group_id !== '';
|
||||
var $btn = $( '#delete-products-ad-group' );
|
||||
|
||||
$btn.prop( 'disabled', !can_delete );
|
||||
|
||||
if ( !is_shopping_campaign )
|
||||
{
|
||||
$btn.attr( 'title', 'Opcja dostepna tylko dla kampanii produktowych (Shopping)' );
|
||||
}
|
||||
else if ( ad_group_id === '' )
|
||||
{
|
||||
$btn.attr( 'title', 'Wybierz grupe reklam do usuniecia' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$btn.attr( 'title', 'Usun wybrana grupe reklam' );
|
||||
}
|
||||
}
|
||||
|
||||
function render_scope_alerts_box( alerts )
|
||||
{
|
||||
var $box = $( '#products_scope_alerts_box' );
|
||||
var $count = $( '#products_scope_alerts_count' );
|
||||
var $list = $( '#products_scope_alerts_list' );
|
||||
var rows = Array.isArray( alerts ) ? alerts : [];
|
||||
|
||||
if ( !rows.length )
|
||||
{
|
||||
$count.text( '0' );
|
||||
$list.empty();
|
||||
$box.addClass( 'hide' ).removeAttr( 'open' );
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
|
||||
rows.forEach( function( row ) {
|
||||
var date_text = row.date_detected || row.date_add || '';
|
||||
var type_text = row.alert_type ? String( row.alert_type ) : '';
|
||||
var message_text = row.message ? String( row.message ) : '';
|
||||
|
||||
html += ''
|
||||
+ '<div class="products-scope-alert-item">'
|
||||
+ '<div class="products-scope-alert-meta">'
|
||||
+ '<span class="products-scope-alert-date">' + escape_html( date_text ) + '</span>'
|
||||
+ ( type_text ? '<span class="products-scope-alert-type">' + escape_html( type_text ) + '</span>' : '' )
|
||||
+ '</div>'
|
||||
+ '<div class="products-scope-alert-message">' + escape_html( message_text ) + '</div>'
|
||||
+ '</div>';
|
||||
} );
|
||||
|
||||
$count.text( rows.length );
|
||||
$list.html( html );
|
||||
$box.removeClass( 'hide' ).attr( 'open', 'open' );
|
||||
}
|
||||
|
||||
function load_scope_alerts()
|
||||
{
|
||||
var client_id = $( '#client_id' ).val() || '';
|
||||
var campaign_id = $( '#products_campaign_id' ).val() || '';
|
||||
var ad_group_id = $( '#products_ad_group_id' ).val() || '';
|
||||
|
||||
if ( !client_id || !campaign_id )
|
||||
{
|
||||
render_scope_alerts_box( [] );
|
||||
return $.Deferred().resolve().promise();
|
||||
}
|
||||
|
||||
return $.ajax({
|
||||
url: '/products/get_scope_alerts/',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
client_id: client_id,
|
||||
campaign_id: campaign_id,
|
||||
ad_group_id: ad_group_id
|
||||
}
|
||||
}).done( function( res ) {
|
||||
if ( res && res.status === 'ok' )
|
||||
{
|
||||
render_scope_alerts_box( res.alerts || [] );
|
||||
}
|
||||
else
|
||||
{
|
||||
render_scope_alerts_box( [] );
|
||||
}
|
||||
}).fail( function() {
|
||||
render_scope_alerts_box( [] );
|
||||
} );
|
||||
}
|
||||
|
||||
function render_zero_impressions_box( products )
|
||||
{
|
||||
var $box = $( '#products_zero_impressions_box' );
|
||||
var $count = $( '#products_zero_impressions_count' );
|
||||
var $select = $( '#products_zero_impressions_select' );
|
||||
var rows = Array.isArray( products ) ? products : [];
|
||||
|
||||
$select.empty();
|
||||
|
||||
if ( !rows.length )
|
||||
{
|
||||
$count.text( '0' );
|
||||
$box.addClass( 'hide' ).removeAttr( 'open' );
|
||||
return;
|
||||
}
|
||||
|
||||
rows.forEach( function( row ) {
|
||||
var product_id = parseInt( row.product_id || 0, 10 );
|
||||
var offer_id = row.offer_id ? String( row.offer_id ) : '';
|
||||
var product_name = row.name ? String( row.name ) : '';
|
||||
var label = '#' + product_id + ( offer_id ? ' | ' + offer_id : '' ) + ' | ' + product_name;
|
||||
|
||||
$select.append( '<option value="' + product_id + '">' + escape_html( label ) + '</option>' );
|
||||
} );
|
||||
|
||||
$count.text( rows.length );
|
||||
$box.removeClass( 'hide' );
|
||||
}
|
||||
|
||||
function load_zero_impressions_products()
|
||||
{
|
||||
var client_id = $( '#client_id' ).val() || '';
|
||||
var campaign_id = $( '#products_campaign_id' ).val() || '';
|
||||
var ad_group_id = $( '#products_ad_group_id' ).val() || '';
|
||||
|
||||
if ( !client_id || !campaign_id )
|
||||
{
|
||||
render_zero_impressions_box( [] );
|
||||
return $.Deferred().resolve().promise();
|
||||
}
|
||||
|
||||
return $.ajax({
|
||||
url: '/products/get_products_without_impressions_30/',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
client_id: client_id,
|
||||
campaign_id: campaign_id,
|
||||
ad_group_id: ad_group_id
|
||||
}
|
||||
}).done( function( res ) {
|
||||
if ( res && res.status === 'ok' )
|
||||
{
|
||||
render_zero_impressions_box( res.products || [] );
|
||||
}
|
||||
else
|
||||
{
|
||||
render_zero_impressions_box( [] );
|
||||
}
|
||||
}).fail( function() {
|
||||
render_zero_impressions_box( [] );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -730,11 +1082,15 @@ $( function()
|
||||
localStorage.setItem( 'products_client_id', client_id );
|
||||
localStorage.removeItem( 'products_campaign_id' );
|
||||
localStorage.removeItem( 'products_ad_group_id' );
|
||||
update_delete_ad_group_button_state();
|
||||
|
||||
load_client_bestseller_min_roas( client_id );
|
||||
load_products_campaigns( client_id, '' ).done( function() {
|
||||
load_products_ad_groups( '', '' ).done( function() {
|
||||
reload_products_table();
|
||||
update_delete_ad_group_button_state();
|
||||
$.when( load_scope_alerts(), load_zero_impressions_products() ).always( function() {
|
||||
reload_products_table();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
});
|
||||
@@ -744,9 +1100,12 @@ $( function()
|
||||
var campaign_id = $( this ).val() || '';
|
||||
localStorage.setItem( 'products_campaign_id', campaign_id );
|
||||
localStorage.removeItem( 'products_ad_group_id' );
|
||||
update_delete_ad_group_button_state();
|
||||
|
||||
load_products_ad_groups( campaign_id, '' ).done( function() {
|
||||
reload_products_table();
|
||||
$.when( load_scope_alerts(), load_zero_impressions_products() ).always( function() {
|
||||
reload_products_table();
|
||||
} );
|
||||
} );
|
||||
});
|
||||
|
||||
@@ -754,7 +1113,71 @@ $( function()
|
||||
{
|
||||
var ad_group_id = $( this ).val() || '';
|
||||
localStorage.setItem( 'products_ad_group_id', ad_group_id );
|
||||
reload_products_table();
|
||||
update_delete_ad_group_button_state();
|
||||
$.when( load_scope_alerts(), load_zero_impressions_products() ).always( function() {
|
||||
reload_products_table();
|
||||
} );
|
||||
});
|
||||
|
||||
$( 'body' ).on( 'click', '#delete-products-ad-group', function( e )
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
var campaign_id = $( '#products_campaign_id' ).val() || '';
|
||||
var ad_group_id = $( '#products_ad_group_id' ).val() || '';
|
||||
var campaign_channel_type = get_selected_products_campaign_channel_type();
|
||||
|
||||
if ( campaign_channel_type !== 'SHOPPING' )
|
||||
{
|
||||
$.alert({
|
||||
title: 'Niedostepne',
|
||||
content: 'Usuwanie grup reklam jest dostepne tylko dla kampanii produktowych (Shopping).',
|
||||
type: 'orange'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !campaign_id || !ad_group_id )
|
||||
{
|
||||
$.alert({
|
||||
title: 'Brak wyboru',
|
||||
content: 'Wybierz kampanie i grupe reklam do usuniecia.',
|
||||
type: 'orange'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var ad_group_name = $.trim( $( '#products_ad_group_id option:selected' ).text() || '' );
|
||||
var campaign_name = $.trim( $( '#products_campaign_id option:selected' ).text() || '' );
|
||||
|
||||
$.confirm({
|
||||
title: 'Usuwanie grupy reklam',
|
||||
content:
|
||||
'Jak chcesz usunac grupe reklam <strong>' + escape_html( ad_group_name ) + '</strong> z kampanii <strong>' + escape_html( campaign_name ) + '</strong>?'
|
||||
+ '<br><br><small>Opcja API usunie grupe reklam rowniez w Google Ads.</small>',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
local: {
|
||||
text: 'Tylko lokalnie',
|
||||
btnClass: 'btn-default',
|
||||
action: function()
|
||||
{
|
||||
return submit_delete_campaign_ad_group( campaign_id, ad_group_id, 'local' );
|
||||
}
|
||||
},
|
||||
google: {
|
||||
text: 'Lokalnie + Google Ads',
|
||||
btnClass: 'btn-red',
|
||||
action: function()
|
||||
{
|
||||
return submit_delete_campaign_ad_group( campaign_id, ad_group_id, 'google' );
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: 'Anuluj'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var savedClient = localStorage.getItem( 'products_client_id' ) || '';
|
||||
@@ -770,7 +1193,10 @@ $( function()
|
||||
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();
|
||||
update_delete_ad_group_button_state();
|
||||
$.when( load_scope_alerts(), load_zero_impressions_products() ).always( function() {
|
||||
reload_products_table();
|
||||
} );
|
||||
} );
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user