diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 592038e..b85ca4f 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -1,6 +1,12 @@ { "ftp://host700513.hostido.net.pl@www@adspro.projectpro.pl": { "public_html": { + "AGENTS.md": { + "type": "-", + "size": 2540, + "lmtime": 0, + "modified": false + }, "ajax.php": { "type": "-", "size": 1208, @@ -23,32 +29,38 @@ }, "class.Api.php": { "type": "-", - "size": 11235, + "size": 19358, "lmtime": 1744498273470, - "modified": false + "modified": true }, "class.Campaigns.php": { "type": "-", - "size": 3809, + "size": 4683, "lmtime": 1769729268048, + "modified": true + }, + "class.CampaignTerms.php": { + "type": "-", + "size": 20891, + "lmtime": 1771441276763, "modified": false }, "class.Clients.php": { "type": "-", - "size": 1443, + "size": 1603, "lmtime": 0, - "modified": false + "modified": true }, "class.Cron.php": { "type": "-", - "size": 27380, - "lmtime": 1764273350638, - "modified": true + "size": 117327, + "lmtime": 1771446080190, + "modified": false }, "class.Products.php": { "type": "-", - "size": 18357, - "lmtime": 1771198824191, + "size": 39363, + "lmtime": 1771440055487, "modified": false }, "class.Site.php": { @@ -59,16 +71,22 @@ }, "class.Users.php": { "type": "-", - "size": 4713, + "size": 15829, "lmtime": 1771198781919, + "modified": true + }, + "class.XmlFiles.php": { + "type": "-", + "size": 818, + "lmtime": 0, "modified": false } }, "factory": { "class.Campaigns.php": { "type": "-", - "size": 1649, - "lmtime": 1769729268049, + "size": 10634, + "lmtime": 1771441276763, "modified": false }, "class.Clients.php": { @@ -85,35 +103,41 @@ }, "class.Products.php": { "type": "-", - "size": 7481, + "size": 27192, "lmtime": 1771170224109, - "modified": false + "modified": true }, "class.Users.php": { "type": "-", "size": 1709, "lmtime": 0, "modified": false + }, + "class.XmlFiles.php": { + "type": "-", + "size": 1410, + "lmtime": 0, + "modified": false } }, "services": { + "class.ClaudeApi.php": { + "type": "-", + "size": 12510, + "lmtime": 1771198088093, + "modified": true + }, "class.GoogleAdsApi.php": { "type": "-", - "size": 8751, - "lmtime": 0, + "size": 99181, + "lmtime": 1771444236566, "modified": false }, "class.OpenAiApi.php": { "type": "-", - "size": 12013, + "size": 18739, "lmtime": 1771171891986, - "modified": false - }, - "class.ClaudeApi.php": { - "type": "-", - "size": 10410, - "lmtime": 1771198088093, - "modified": false + "modified": true } } }, @@ -133,21 +157,21 @@ }, "config.php": { "type": "-", - "size": 357, - "lmtime": 1740740280282, + "size": 515, + "lmtime": 1771444244763, "modified": false }, "cron.php": { "type": "-", - "size": 1777, + "size": 1971, "lmtime": 0, - "modified": false + "modified": true }, "docs": { "database.sql": { "type": "-", - "size": 15919, - "lmtime": 0, + "size": 17320, + "lmtime": 1771440593718, "modified": false }, "google_ads_api_design_doc.doc": { @@ -162,19 +186,25 @@ "lmtime": 0, "modified": false }, + "memory.md": { + "type": "-", + "size": 2680, + "lmtime": 1771443431441, + "modified": false + }, "PLAN.md": { "type": "-", "size": 11544, "lmtime": 0, "modified": false - }, - "memory.md": { - "type": "-", - "size": 1697, - "lmtime": 1771368520970, - "modified": false } }, + ".gitignore": { + "type": "-", + "size": 16, + "lmtime": 1771368579889, + "modified": false + }, ".htaccess": { "type": "-", "size": 601, @@ -183,9 +213,9 @@ }, "index.php": { "type": "-", - "size": 3824, + "size": 3885, "lmtime": 1771198110809, - "modified": false + "modified": true }, "install.php": { "type": "-", @@ -202,32 +232,32 @@ }, "style.css": { "type": "-", - "size": 35676, - "lmtime": 1771367686453, + "size": 48422, + "lmtime": 1771441569261, "modified": false }, "style.css.map": { "type": "-", - "size": 8131, - "lmtime": 1771367686452, + "size": 12494, + "lmtime": 1771441569259, "modified": false }, "style-old.css": { "type": "-", - "size": 19795, + "size": 19791, "lmtime": 0, - "modified": false + "modified": true }, "style-old.scss": { "type": "-", - "size": 25910, + "size": 25906, "lmtime": 0, - "modified": false + "modified": true }, "style.scss": { "type": "-", - "size": 38739, - "lmtime": 1771367615645, + "size": 59257, + "lmtime": 1771441540574, "modified": false } }, @@ -305,21 +335,69 @@ "migrations": { "001_google_ads_settings.sql": { "type": "-", - "size": 889, + "size": 1407, "lmtime": 0, + "modified": true + }, + "002_products_data_url.sql": { + "type": "-", + "size": 467, + "lmtime": 1771171362268, + "modified": true + }, + "003_campaign_ad_groups_and_terms.sql": { + "type": "-", + "size": 3864, + "lmtime": 0, + "modified": false + }, + "004_campaigns_performance_max_flag.sql": { + "type": "-", + "size": 543, + "lmtime": 0, + "modified": false + }, + "005_drop_is_performance_max_column.sql": { + "type": "-", + "size": 505, + "lmtime": 0, + "modified": false + }, + "006_products_scope_dimensions.sql": { + "type": "-", + "size": 6350, + "lmtime": 0, + "modified": false + }, + "007_clients_merchant_account_id.sql": { + "type": "-", + "size": 123, + "lmtime": 0, + "modified": false + }, + "008_products_keyword_planner_terms.sql": { + "type": "-", + "size": 764, + "lmtime": 0, + "modified": false + }, + "009_products_merchant_sync_log.sql": { + "type": "-", + "size": 880, + "lmtime": 0, + "modified": false + }, + "010_campaign_keywords.sql": { + "type": "-", + "size": 1278, + "lmtime": 1771440593714, "modified": false }, "demo_data.sql": { "type": "-", - "size": 18951, + "size": 22351, "lmtime": 0, - "modified": false - }, - "002_products_data_url.sql": { - "type": "-", - "size": 86, - "lmtime": 1771171362268, - "modified": false + "modified": true } }, "robots.txt": { @@ -332,15 +410,15 @@ "products": { "main_view.php": { "type": "-", - "size": 23413, - "lmtime": 1771198812392, + "size": 49238, + "lmtime": 1771441425576, "modified": false }, "product_history.php": { "type": "-", - "size": 12423, + "size": 13214, "lmtime": 1769467103988, - "modified": false + "modified": true } }, "site": { @@ -388,22 +466,22 @@ "campaign_terms": { "main_view.php": { "type": "-", - "size": 25469, - "lmtime": 1771367807171, + "size": 81281, + "lmtime": 1771441788473, "modified": false } } }, "tmp": {}, + "TODO.md": { + "type": "-", + "size": 0, + "lmtime": 0, + "modified": false + }, "tools": {}, "upload": {}, - "xml": {}, - ".gitignore": { - "type": "-", - "size": 16, - "lmtime": 1771368579889, - "modified": false - } + "xml": {} } }, "$version": 1 diff --git a/autoload/controls/class.Campaigns.php b/autoload/controls/class.Campaigns.php index 6d49709..d8986b4 100644 --- a/autoload/controls/class.Campaigns.php +++ b/autoload/controls/class.Campaigns.php @@ -152,6 +152,31 @@ class Campaigns exit; } + static public function delete_campaigns() + { + $ids = \S::get( 'ids' ); + + if ( empty( $ids ) || !is_array( $ids ) ) + { + echo json_encode( [ 'success' => false, 'message' => 'Nie wybrano kampanii' ] ); + exit; + } + + $ids = array_map( 'intval', $ids ); + $ids = array_filter( $ids, function( $id ) { return $id > 0; } ); + + if ( empty( $ids ) ) + { + echo json_encode( [ 'success' => false, 'message' => 'Nie wybrano kampanii' ] ); + exit; + } + + $deleted = \factory\Campaigns::delete_campaigns( $ids ); + + echo json_encode( [ 'success' => true, 'deleted' => $deleted ] ); + exit; + } + static public function delete_history_entry() { $history_id = \S::get( 'history_id' ); diff --git a/autoload/factory/class.Campaigns.php b/autoload/factory/class.Campaigns.php index b21aa51..766edf2 100644 --- a/autoload/factory/class.Campaigns.php +++ b/autoload/factory/class.Campaigns.php @@ -324,6 +324,21 @@ class Campaigns return $mdb -> delete( 'campaigns', [ 'id' => $campaign_id ] ); } + static public function delete_campaigns( array $ids ) + { + global $mdb; + $deleted = 0; + + foreach ( $ids as $id ) + { + $mdb -> delete( 'campaigns_history', [ 'campaign_id' => $id ] ); + $result = $mdb -> delete( 'campaigns', [ 'id' => $id ] ); + if ( $result ) $deleted++; + } + + return $deleted; + } + static public function delete_history_entry( $history_id ) { global $mdb; diff --git a/layout/style.scss b/layout/style.scss index 21d40f5..83a2780 100644 --- a/layout/style.scss +++ b/layout/style.scss @@ -1304,6 +1304,113 @@ table { } } + .campaigns-list-panel { + background: $cWhite; + border-radius: 10px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); + margin-bottom: 20px; + overflow: hidden; + + .campaigns-list-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + border-bottom: 1px solid $cBorder; + gap: 12px; + + &-left { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: $cText; + + input[type="checkbox"] { + width: 16px; + height: 16px; + cursor: pointer; + } + + label { + cursor: pointer; + user-select: none; + margin: 0; + } + + .campaigns-selected-count { + margin-left: 12px; + color: #8899A6; + } + } + + &-right { + .campaigns-bulk-delete-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + border: none; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + background: #FFF5F5; + color: $cDanger; + transition: all 0.2s; + + &:hover:not(:disabled) { + background: $cDanger; + color: $cWhite; + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } + } + } + } + + .campaigns-list-items { + display: flex; + flex-wrap: wrap; + gap: 0; + padding: 8px 8px; + max-height: 220px; + overflow-y: auto; + + .campaigns-list-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + margin: 2px; + border-radius: 6px; + font-size: 13px; + color: $cTextDark; + cursor: pointer; + user-select: none; + transition: background 0.15s; + + &:hover { + background: #F0F4FF; + } + + input[type="checkbox"] { + width: 15px; + height: 15px; + cursor: pointer; + flex-shrink: 0; + } + + .campaigns-list-item-name { + white-space: nowrap; + } + } + } + } + .campaigns-chart-wrap { background: $cWhite; border-radius: 10px; diff --git a/templates/campaigns/main_view.php b/templates/campaigns/main_view.php index a76e70a..dcd265e 100644 --- a/templates/campaigns/main_view.php +++ b/templates/campaigns/main_view.php @@ -26,6 +26,22 @@ + +
@@ -162,6 +178,113 @@ function reloadChart() $( function() { + function updateCampaignsSelectedCount() + { + 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 = $( '' ); + 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(); + }); + + $( 'body' ).on( 'click', '#campaigns_bulk_delete', function() + { + var checked = $( '.campaigns-list-item-cb:checked' ); + var count = checked.length; + + if ( count === 0 ) return; + + var names = []; + checked.each( function() { names.push( $( this ).data( 'name' ) ); } ); + var namesList = ''; + + $.confirm({ + title: 'Potwierdzenie usuniecia', + content: 'Czy na pewno chcesz usunac ' + count + ' kampanii?' + namesList + '
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' } + } + }); + }); + $( 'body' ).on( 'change', '#client_id', function() { var client_id = $( this ).val(); @@ -176,7 +299,10 @@ $( function() campaigns_select.append( '' ); if ( !client_id ) + { + $( '#campaigns_list_panel' ).hide(); return; + } $.ajax({ url: '/campaigns/get_campaigns_list/client_id=' + client_id, @@ -201,6 +327,8 @@ $( function() campaigns_select.append( '' ); }); + buildCampaignsList( campaigns ); + if ( campaign_to_restore && campaigns_select.find( 'option[value="' + campaign_to_restore + '"]' ).length ) { campaigns_select.val( campaign_to_restore ).trigger( 'change' );