feat: Dodaj moduł grup i fraz, oznaczanie wykluczonych na czerwono, CLAUDE.md
- Nowy moduł CampaignTerms z widokiem grup reklam, fraz wyszukiwanych i fraz wykluczających - Frazy wyszukiwane dodane do wykluczonych oznaczane czerwonym kolorem w tabeli - Instalator migracji (install.php) z obsługą schema_migrations - Migracja 003 dla tabel campaign_ad_groups, campaign_search_terms, campaign_negative_keywords - CLAUDE.md z dokumentacją architektury projektu - Aktualizacja layoutu, stylów i konfiguracji Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
351
libraries/adspro-dialog.js
Normal file
351
libraries/adspro-dialog.js
Normal file
@@ -0,0 +1,351 @@
|
||||
/**
|
||||
* AdsProDialog - własny system dialogów (zamiennik jquery-confirm)
|
||||
* API kompatybilne z jquery-confirm: $.confirm(options), $.alert(options)
|
||||
*/
|
||||
(function( $ )
|
||||
{
|
||||
'use strict';
|
||||
|
||||
var dialogCounter = 0;
|
||||
var activeDialogs = [];
|
||||
|
||||
function AdsProDialog( options )
|
||||
{
|
||||
this.id = ++dialogCounter;
|
||||
this.options = $.extend( {}, AdsProDialog.defaults, options );
|
||||
this.$el = null;
|
||||
this.$box = null;
|
||||
this.$content = null;
|
||||
this._closed = false;
|
||||
this._autoCloseTimer = null;
|
||||
this._init();
|
||||
}
|
||||
|
||||
AdsProDialog.defaults = {
|
||||
title: '',
|
||||
content: '',
|
||||
type: '',
|
||||
theme: '',
|
||||
buttons: {},
|
||||
closeIcon: false,
|
||||
closeIconClass: 'fa-solid fa-xmark',
|
||||
columnClass: '',
|
||||
boxWidth: '',
|
||||
useBootstrap: true,
|
||||
draggable: false,
|
||||
autoClose: '',
|
||||
onContentReady: null,
|
||||
onClose: null,
|
||||
onOpen: null
|
||||
};
|
||||
|
||||
AdsProDialog.prototype = {
|
||||
|
||||
_init: function()
|
||||
{
|
||||
this._buildDOM();
|
||||
this._bindEvents();
|
||||
this._appendToBody();
|
||||
this._applyAutoClose();
|
||||
this._triggerContentReady();
|
||||
activeDialogs.push( this );
|
||||
},
|
||||
|
||||
_buildDOM: function()
|
||||
{
|
||||
var o = this.options;
|
||||
var typeClass = o.type ? ' adspro-dialog-type-' + o.type : '';
|
||||
|
||||
var sizeStyle = '';
|
||||
var sizeClass = '';
|
||||
if ( !o.useBootstrap && o.boxWidth )
|
||||
{
|
||||
sizeStyle = 'max-width:' + o.boxWidth + ';width:100%;';
|
||||
}
|
||||
else if ( o.columnClass )
|
||||
{
|
||||
sizeClass = ' ' + o.columnClass.replace( /col-md-offset-\d+/g, '' ).trim();
|
||||
}
|
||||
|
||||
var html =
|
||||
'<div class="adspro-dialog" data-dialog-id="' + this.id + '">' +
|
||||
'<div class="adspro-dialog-bg"></div>' +
|
||||
'<div class="adspro-dialog-scrollpane">' +
|
||||
'<div class="adspro-dialog-center' + sizeClass + '">' +
|
||||
'<div class="adspro-dialog-box jconfirm-box' + typeClass + '" style="' + sizeStyle + '">' +
|
||||
this._buildCloseIcon( o ) +
|
||||
this._buildHeader( o ) +
|
||||
'<div class="adspro-dialog-content-pane">' +
|
||||
'<div class="adspro-dialog-content">' + o.content + '</div>' +
|
||||
'</div>' +
|
||||
this._buildButtons( o ) +
|
||||
'<div class="adspro-dialog-loading" style="display:none;">' +
|
||||
'<div class="adspro-dialog-spinner"></div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
this.$el = $( html );
|
||||
this.$box = this.$el.find( '.adspro-dialog-box' );
|
||||
this.$content = this.$el.find( '.adspro-dialog-content' );
|
||||
},
|
||||
|
||||
_buildCloseIcon: function( o )
|
||||
{
|
||||
if ( !o.closeIcon ) return '';
|
||||
var iconClass = o.closeIconClass || 'fa-solid fa-xmark';
|
||||
return '<div class="adspro-dialog-close-icon"><i class="' + iconClass + '"></i></div>';
|
||||
},
|
||||
|
||||
_buildHeader: function( o )
|
||||
{
|
||||
if ( !o.title ) return '';
|
||||
return '<div class="adspro-dialog-title-c">' +
|
||||
'<span class="adspro-dialog-title">' + o.title + '</span>' +
|
||||
'</div>';
|
||||
},
|
||||
|
||||
_buildButtons: function( o )
|
||||
{
|
||||
if ( !o.buttons || $.isEmptyObject( o.buttons ) ) return '';
|
||||
|
||||
var html = '<div class="adspro-dialog-buttons">';
|
||||
var self = this;
|
||||
|
||||
$.each( o.buttons, function( key, btnDef )
|
||||
{
|
||||
if ( typeof btnDef === 'function' )
|
||||
{
|
||||
btnDef = { action: btnDef };
|
||||
o.buttons[ key ] = btnDef;
|
||||
}
|
||||
|
||||
var text = btnDef.text || key.charAt( 0 ).toUpperCase() + key.slice( 1 );
|
||||
var btnClass = btnDef.btnClass || 'btn-default';
|
||||
var isEnter = ( btnDef.keys && btnDef.keys.indexOf( 'enter' ) !== -1 );
|
||||
|
||||
html += '<button type="button" class="adspro-dialog-btn btn ' + btnClass + '"' +
|
||||
' data-btn-key="' + key + '"' +
|
||||
( isEnter ? ' data-enter-key="true"' : '' ) +
|
||||
'>' + text + '</button>';
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
return html;
|
||||
},
|
||||
|
||||
_bindEvents: function()
|
||||
{
|
||||
var self = this;
|
||||
var o = this.options;
|
||||
|
||||
// Backdrop click
|
||||
this.$el.find( '.adspro-dialog-bg' ).on( 'click', function()
|
||||
{
|
||||
self.close();
|
||||
});
|
||||
|
||||
// Close icon
|
||||
this.$el.find( '.adspro-dialog-close-icon' ).on( 'click', function()
|
||||
{
|
||||
self.close();
|
||||
});
|
||||
|
||||
// Buttons
|
||||
this.$el.find( '.adspro-dialog-btn' ).each( function()
|
||||
{
|
||||
var $btn = $( this );
|
||||
var key = $btn.data( 'btn-key' );
|
||||
var btnDef = o.buttons[ key ];
|
||||
|
||||
// Referencje do buttonów (kompatybilność z $$formSubmit itp.)
|
||||
self[ '$$' + key ] = $btn;
|
||||
|
||||
$btn.on( 'click', function()
|
||||
{
|
||||
if ( typeof btnDef === 'function' )
|
||||
{
|
||||
btnDef.call( self );
|
||||
self.close();
|
||||
return;
|
||||
}
|
||||
if ( btnDef && typeof btnDef.action === 'function' )
|
||||
{
|
||||
var result = btnDef.action.call( self );
|
||||
if ( result !== false )
|
||||
{
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Keyboard
|
||||
$( document ).on( 'keydown.adspro-dialog-' + this.id, function( e )
|
||||
{
|
||||
if ( self._closed ) return;
|
||||
if ( activeDialogs[ activeDialogs.length - 1 ] !== self ) return;
|
||||
|
||||
if ( e.key === 'Escape' )
|
||||
{
|
||||
e.preventDefault();
|
||||
self.close();
|
||||
}
|
||||
if ( e.key === 'Enter' )
|
||||
{
|
||||
if ( $( e.target ).is( 'textarea, select' ) ) return;
|
||||
var $enterBtn = self.$el.find( '.adspro-dialog-btn[data-enter-key="true"]' );
|
||||
if ( $enterBtn.length )
|
||||
{
|
||||
e.preventDefault();
|
||||
$enterBtn.trigger( 'click' );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Draggable
|
||||
if ( o.draggable && $.fn.draggable )
|
||||
{
|
||||
this.$box.draggable({
|
||||
handle: '.adspro-dialog-title-c',
|
||||
cursor: 'move'
|
||||
});
|
||||
this.$el.find( '.adspro-dialog-title-c' ).css( 'cursor', 'move' );
|
||||
}
|
||||
},
|
||||
|
||||
_appendToBody: function()
|
||||
{
|
||||
var baseZIndex = 99000 + ( this.id * 10 );
|
||||
this.$el.css( 'z-index', baseZIndex );
|
||||
$( 'body' ).append( this.$el );
|
||||
|
||||
var self = this;
|
||||
requestAnimationFrame( function()
|
||||
{
|
||||
self.$el.addClass( 'adspro-dialog-open' );
|
||||
});
|
||||
|
||||
if ( typeof this.options.onOpen === 'function' )
|
||||
{
|
||||
this.options.onOpen.call( this );
|
||||
}
|
||||
},
|
||||
|
||||
_applyAutoClose: function()
|
||||
{
|
||||
var ac = this.options.autoClose;
|
||||
if ( !ac ) return;
|
||||
|
||||
var parts = ac.split( '|' );
|
||||
if ( parts.length !== 2 ) return;
|
||||
|
||||
var ms = parseInt( parts[ 1 ], 10 );
|
||||
var self = this;
|
||||
this._autoCloseTimer = setTimeout( function()
|
||||
{
|
||||
if ( !self._closed ) self.close();
|
||||
}, ms );
|
||||
},
|
||||
|
||||
_triggerContentReady: function()
|
||||
{
|
||||
if ( typeof this.options.onContentReady === 'function' )
|
||||
{
|
||||
this.options.onContentReady.call( this );
|
||||
}
|
||||
},
|
||||
|
||||
// --- Metody publiczne ---
|
||||
|
||||
close: function()
|
||||
{
|
||||
if ( this._closed ) return;
|
||||
this._closed = true;
|
||||
|
||||
if ( this._autoCloseTimer ) clearTimeout( this._autoCloseTimer );
|
||||
$( document ).off( 'keydown.adspro-dialog-' + this.id );
|
||||
|
||||
var self = this;
|
||||
this.$el.removeClass( 'adspro-dialog-open' );
|
||||
this.$el.addClass( 'adspro-dialog-closing' );
|
||||
|
||||
setTimeout( function()
|
||||
{
|
||||
self.$el.remove();
|
||||
var idx = activeDialogs.indexOf( self );
|
||||
if ( idx > -1 ) activeDialogs.splice( idx, 1 );
|
||||
if ( typeof self.options.onClose === 'function' )
|
||||
{
|
||||
self.options.onClose.call( self );
|
||||
}
|
||||
}, 250 );
|
||||
},
|
||||
|
||||
showLoading: function( showContent )
|
||||
{
|
||||
this.$el.find( '.adspro-dialog-loading' ).show();
|
||||
if ( !showContent )
|
||||
{
|
||||
this.$el.find( '.adspro-dialog-content-pane' ).css( 'opacity', '0.3' );
|
||||
}
|
||||
this.$el.find( '.adspro-dialog-buttons' ).css({
|
||||
'pointer-events': 'none',
|
||||
'opacity': '0.5'
|
||||
});
|
||||
},
|
||||
|
||||
hideLoading: function()
|
||||
{
|
||||
this.$el.find( '.adspro-dialog-loading' ).hide();
|
||||
this.$el.find( '.adspro-dialog-content-pane' ).css( 'opacity', '' );
|
||||
this.$el.find( '.adspro-dialog-buttons' ).css({
|
||||
'pointer-events': '',
|
||||
'opacity': ''
|
||||
});
|
||||
},
|
||||
|
||||
setContent: function( html )
|
||||
{
|
||||
this.$content.html( html );
|
||||
},
|
||||
|
||||
setTitle: function( title )
|
||||
{
|
||||
this.$el.find( '.adspro-dialog-title' ).html( title );
|
||||
}
|
||||
};
|
||||
|
||||
// --- Rejestracja globalna ---
|
||||
|
||||
$.confirm = function( options )
|
||||
{
|
||||
return new AdsProDialog( options );
|
||||
};
|
||||
|
||||
$.alert = function( options )
|
||||
{
|
||||
if ( typeof options === 'string' )
|
||||
{
|
||||
options = {
|
||||
title: '',
|
||||
content: options,
|
||||
buttons: { ok: { text: 'OK', btnClass: 'btn-blue' } }
|
||||
};
|
||||
}
|
||||
|
||||
if ( !options.buttons )
|
||||
{
|
||||
options.buttons = { ok: { text: 'OK', btnClass: 'btn-blue' } };
|
||||
}
|
||||
|
||||
return new AdsProDialog( options );
|
||||
};
|
||||
|
||||
})( jQuery );
|
||||
Reference in New Issue
Block a user