mirror of
https://github.com/WenPai-org/wptag.git
synced 2025-08-04 21:05:17 +08:00
Add initial WPTag admin and core plugin files
Introduce the main admin controllers, AJAX handlers, UI partials, and core classes for the WPTag plugin. This includes admin interface components, dashboard, settings, snippet management, asset files, and localization support. These files establish the foundation for managing code snippets, templates, and plugin settings within the WordPress admin.
This commit is contained in:
parent
6524694731
commit
840a779a84
20 changed files with 4389 additions and 0 deletions
374
assets/admin.css
Normal file
374
assets/admin.css
Normal file
|
@ -0,0 +1,374 @@
|
|||
.wptag-admin-wrap {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.wptag-header {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
border-bottom: 0;
|
||||
padding: 20px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wptag-header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.wptag-header .page-title-action {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.wptag-content {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.wptag-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.wptag-stat-card {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e2e4e7;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wptag-stat-card h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wptag-stat-card .stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.wptag-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.wptag-table th,
|
||||
.wptag-table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e4e7;
|
||||
}
|
||||
|
||||
.wptag-table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.wptag-table tbody tr:hover {
|
||||
background: #f6f7f7;
|
||||
}
|
||||
|
||||
.wptag-status-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wptag-status-badge.active {
|
||||
background: #d4f4dd;
|
||||
color: #00a32a;
|
||||
}
|
||||
|
||||
.wptag-status-badge.inactive {
|
||||
background: #f5e6e6;
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
.wptag-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wptag-action-link {
|
||||
color: #2271b1;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wptag-action-link:hover {
|
||||
color: #135e96;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.wptag-action-link.delete {
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
.wptag-action-link.delete:hover {
|
||||
color: #a02222;
|
||||
}
|
||||
|
||||
.wptag-form-table {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.wptag-form-table th {
|
||||
width: 200px;
|
||||
padding: 20px 10px 20px 0;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wptag-form-table td {
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
.wptag-form-table input[type="text"],
|
||||
.wptag-form-table input[type="number"],
|
||||
.wptag-form-table select,
|
||||
.wptag-form-table textarea {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.wptag-code-editor {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wptag-conditions-builder {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e2e4e7;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.wptag-condition-group {
|
||||
background: #fff;
|
||||
border: 1px solid #e2e4e7;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.wptag-condition-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.wptag-condition-row select,
|
||||
.wptag-condition-row input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wptag-templates-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.wptag-template-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e2e4e7;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.wptag-template-card:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.wptag-template-card h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 16px;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.wptag-template-card p {
|
||||
color: #666;
|
||||
margin: 0 0 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wptag-template-card .button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wptag-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
.wptag-modal-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.wptag-modal-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e2e4e7;
|
||||
}
|
||||
|
||||
.wptag-modal-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.wptag-modal-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.wptag-modal-footer {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #e2e4e7;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.wptag-filters {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wptag-filter-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.wptag-filter-item label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.wptag-empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.wptag-empty-state svg {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.wptag-empty-state h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.wptag-empty-state p {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.wptag-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e2e4e7;
|
||||
margin: -20px -20px 20px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.wptag-tab {
|
||||
padding: 15px 20px;
|
||||
border-bottom: 2px solid transparent;
|
||||
text-decoration: none;
|
||||
color: #2c3338;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.wptag-tab:hover {
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.wptag-tab.active {
|
||||
color: #2271b1;
|
||||
border-bottom-color: #2271b1;
|
||||
}
|
||||
|
||||
.wptag-notice {
|
||||
padding: 12px 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wptag-notice.info {
|
||||
background: #e5f5fa;
|
||||
color: #0073aa;
|
||||
}
|
||||
|
||||
.wptag-notice.success {
|
||||
background: #d4f4dd;
|
||||
color: #00a32a;
|
||||
}
|
||||
|
||||
.wptag-notice.warning {
|
||||
background: #fcf9e8;
|
||||
color: #996800;
|
||||
}
|
||||
|
||||
.wptag-notice.error {
|
||||
background: #f5e6e6;
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
.wptag-form-table th {
|
||||
width: auto;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.wptag-filters {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wptag-condition-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
322
assets/admin.js
Normal file
322
assets/admin.js
Normal file
|
@ -0,0 +1,322 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
const WPTagAdmin = {
|
||||
init: function() {
|
||||
this.bindEvents();
|
||||
this.initCodeEditor();
|
||||
this.initConditionsBuilder();
|
||||
},
|
||||
|
||||
bindEvents: function() {
|
||||
$(document).on('click', '.wptag-toggle-status', this.toggleStatus);
|
||||
$(document).on('click', '.wptag-delete-snippet', this.deleteSnippet);
|
||||
$(document).on('change', '.wptag-filter', this.filterSnippets);
|
||||
$(document).on('click', '.wptag-template-use', this.useTemplate);
|
||||
$(document).on('submit', '.wptag-snippet-form', this.validateForm);
|
||||
$(document).on('click', '.wptag-add-condition', this.addCondition);
|
||||
$(document).on('click', '.wptag-remove-condition', this.removeCondition);
|
||||
$(document).on('change', '#code_type', this.updateCodeEditor);
|
||||
},
|
||||
|
||||
toggleStatus: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $link = $(this);
|
||||
const snippetId = $link.data('snippet-id');
|
||||
|
||||
$.ajax({
|
||||
url: wptagAdmin.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wptag_toggle_snippet',
|
||||
snippet_id: snippetId,
|
||||
nonce: wptagAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message || wptagAdmin.strings.error);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert(wptagAdmin.strings.error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
deleteSnippet: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!confirm(wptagAdmin.strings.confirmDelete)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $link = $(this);
|
||||
const snippetId = $link.data('snippet-id');
|
||||
|
||||
$.ajax({
|
||||
url: wptagAdmin.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wptag_delete_snippet',
|
||||
snippet_id: snippetId,
|
||||
nonce: wptagAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$link.closest('tr').fadeOut(400, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
} else {
|
||||
alert(response.data.message || wptagAdmin.strings.error);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert(wptagAdmin.strings.error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
filterSnippets: function() {
|
||||
const filters = {
|
||||
search: $('#filter-search').val(),
|
||||
category: $('#filter-category').val(),
|
||||
position: $('#filter-position').val(),
|
||||
status: $('#filter-status').val()
|
||||
};
|
||||
|
||||
const params = new URLSearchParams(filters);
|
||||
params.delete('page');
|
||||
|
||||
Object.keys(filters).forEach(key => {
|
||||
if (!filters[key]) {
|
||||
params.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
window.location.href = window.location.pathname + '?page=wptag-snippets&' + params.toString();
|
||||
},
|
||||
|
||||
initCodeEditor: function() {
|
||||
const $codeTextarea = $('#snippet-code');
|
||||
|
||||
if ($codeTextarea.length && typeof wp !== 'undefined' && wp.codeEditor) {
|
||||
const editorSettings = wp.codeEditor.defaultSettings || {};
|
||||
const codeType = $('#code_type').val();
|
||||
|
||||
editorSettings.codemirror = {
|
||||
...editorSettings.codemirror,
|
||||
mode: this.getCodeMirrorMode(codeType),
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
indentUnit: 2,
|
||||
tabSize: 2
|
||||
};
|
||||
|
||||
this.codeEditor = wp.codeEditor.initialize($codeTextarea, editorSettings);
|
||||
}
|
||||
},
|
||||
|
||||
getCodeMirrorMode: function(codeType) {
|
||||
const modes = {
|
||||
'html': 'htmlmixed',
|
||||
'javascript': 'javascript',
|
||||
'css': 'css'
|
||||
};
|
||||
|
||||
return modes[codeType] || 'htmlmixed';
|
||||
},
|
||||
|
||||
updateCodeEditor: function() {
|
||||
const codeType = $(this).val();
|
||||
|
||||
if (WPTagAdmin.codeEditor && WPTagAdmin.codeEditor.codemirror) {
|
||||
WPTagAdmin.codeEditor.codemirror.setOption('mode', WPTagAdmin.getCodeMirrorMode(codeType));
|
||||
}
|
||||
},
|
||||
|
||||
initConditionsBuilder: function() {
|
||||
const $builder = $('.wptag-conditions-builder');
|
||||
|
||||
if (!$builder.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadConditionTypes();
|
||||
},
|
||||
|
||||
loadConditionTypes: function() {
|
||||
$.ajax({
|
||||
url: wptagAdmin.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wptag_get_condition_types',
|
||||
nonce: wptagAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
WPTagAdmin.conditionTypes = response.data.types;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addCondition: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $group = $(this).closest('.wptag-condition-group');
|
||||
const $newRow = WPTagAdmin.createConditionRow();
|
||||
|
||||
$group.find('.wptag-conditions-list').append($newRow);
|
||||
},
|
||||
|
||||
removeCondition: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
$(this).closest('.wptag-condition-row').fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
},
|
||||
|
||||
createConditionRow: function() {
|
||||
const html = `
|
||||
<div class="wptag-condition-row">
|
||||
<select name="conditions[rules][][type]" class="condition-type">
|
||||
<option value="">Select Type</option>
|
||||
<option value="page_type">Page Type</option>
|
||||
<option value="user_status">User Status</option>
|
||||
<option value="device_type">Device Type</option>
|
||||
</select>
|
||||
<select name="conditions[rules][][operator]" class="condition-operator">
|
||||
<option value="equals">Equals</option>
|
||||
<option value="not_equals">Not Equals</option>
|
||||
</select>
|
||||
<input type="text" name="conditions[rules][][value]" class="condition-value" placeholder="Value">
|
||||
<button type="button" class="button wptag-remove-condition">Remove</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return $(html);
|
||||
},
|
||||
|
||||
useTemplate: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $button = $(this);
|
||||
const serviceType = $button.data('service-type');
|
||||
|
||||
$.ajax({
|
||||
url: wptagAdmin.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wptag_get_template',
|
||||
service_type: serviceType,
|
||||
nonce: wptagAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
WPTagAdmin.showTemplateModal(response.data.template);
|
||||
} else {
|
||||
alert(response.data.message || wptagAdmin.strings.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
showTemplateModal: function(template) {
|
||||
const fields = template.config_fields.map(field => {
|
||||
return `
|
||||
<div class="wptag-form-field">
|
||||
<label for="${field.name}">${field.label}</label>
|
||||
<input type="${field.type || 'text'}"
|
||||
id="${field.name}"
|
||||
name="${field.name}"
|
||||
${field.required ? 'required' : ''}>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
const modalHtml = `
|
||||
<div class="wptag-modal" id="template-config-modal">
|
||||
<div class="wptag-modal-content">
|
||||
<div class="wptag-modal-header">
|
||||
<h2>${template.service_name} Configuration</h2>
|
||||
</div>
|
||||
<form id="template-config-form">
|
||||
<div class="wptag-modal-body">
|
||||
${fields}
|
||||
</div>
|
||||
<div class="wptag-modal-footer">
|
||||
<button type="button" class="button" onclick="WPTagAdmin.closeModal()">Cancel</button>
|
||||
<button type="submit" class="button button-primary">Create Snippet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('body').append(modalHtml);
|
||||
$('#template-config-modal').fadeIn();
|
||||
|
||||
$('#template-config-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
WPTagAdmin.processTemplate(template.service_type, $(this).serialize());
|
||||
});
|
||||
},
|
||||
|
||||
processTemplate: function(serviceType, formData) {
|
||||
$.ajax({
|
||||
url: wptagAdmin.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wptag_process_template',
|
||||
service_type: serviceType,
|
||||
config: formData,
|
||||
nonce: wptagAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
window.location.href = `admin.php?page=wptag-snippets&action=edit&snippet_id=${response.data.snippet_id}`;
|
||||
} else {
|
||||
alert(response.data.message || wptagAdmin.strings.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
closeModal: function() {
|
||||
$('.wptag-modal').fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
},
|
||||
|
||||
validateForm: function(e) {
|
||||
const $form = $(this);
|
||||
const name = $form.find('#snippet-name').val();
|
||||
const code = WPTagAdmin.codeEditor ?
|
||||
WPTagAdmin.codeEditor.codemirror.getValue() :
|
||||
$form.find('#snippet-code').val();
|
||||
|
||||
if (!name.trim()) {
|
||||
e.preventDefault();
|
||||
alert('Please enter a snippet name');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!code.trim()) {
|
||||
e.preventDefault();
|
||||
alert('Please enter some code');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
WPTagAdmin.init();
|
||||
});
|
||||
|
||||
})(jQuery);
|
Loading…
Add table
Add a link
Reference in a new issue