From 30e040134db973346d4d0697a138d9a5cc5bc826 Mon Sep 17 00:00:00 2001 From: feibisi Date: Sat, 26 Jul 2025 05:47:24 +0800 Subject: [PATCH] Enhance admin UI and UX for WPTag plugin Improves the admin interface with refined CSS for better layout, accessibility, and responsiveness. Adds robust JS logic for disabling tracking scripts, change tracking, validation, preview, import/export, reset, tooltips, and modal handling. These changes provide a more user-friendly experience, prevent accidental data loss, and ensure tracking scripts do not run in the admin area. --- assets/admin.css | 804 +++++++++++++++++++++--------- assets/admin.js | 484 +++++++++++++----- includes/class-admin.php | 325 +++++++++--- includes/class-config.php | 469 ++++++++++++++--- includes/class-frontend.php | 392 ++++++++++++++- includes/class-loader.php | 406 ++++++++++++++- includes/class-output-manager.php | 238 +++++++-- includes/class-validator.php | 547 ++++++++++++++++---- uninstall.php | 297 ++++++++++- wptag.php | 13 +- 10 files changed, 3290 insertions(+), 685 deletions(-) diff --git a/assets/admin.css b/assets/admin.css index 4304974..e427ff0 100644 --- a/assets/admin.css +++ b/assets/admin.css @@ -16,17 +16,38 @@ .wptag-header-info p { margin: 0; color: #666; + font-size: 14px; + line-height: 1.5; } .wptag-header-actions { display: flex; gap: 10px; + flex-shrink: 0; +} + +.wptag-header-actions .button { + display: flex; + align-items: center; + gap: 6px; } .nav-tab-wrapper { margin-bottom: 0; } +.nav-tab { + display: flex; + align-items: center; + gap: 6px; +} + +.nav-tab .dashicons { + font-size: 16px; + width: 16px; + height: 16px; +} + .nav-tab .count { background: #72aee6; color: #fff; @@ -47,17 +68,36 @@ border-top: none; padding: 20px; border-radius: 0 0 4px 4px; + min-height: 400px; } .wptag-no-services { text-align: center; - padding: 40px 20px; + padding: 60px 20px; color: #666; } +.wptag-no-services-icon { + margin-bottom: 20px; +} + +.wptag-no-services-icon .dashicons { + font-size: 64px; + width: 64px; + height: 64px; + color: #ccc; +} + +.wptag-no-services h3 { + margin: 0 0 10px 0; + color: #333; + font-size: 18px; +} + .wptag-no-services p { - font-size: 16px; + font-size: 14px; margin: 0; + line-height: 1.6; } .wptag-services-grid { @@ -73,14 +113,33 @@ border-radius: 8px; overflow: hidden; transition: all 0.3s ease; + position: relative; } .wptag-service-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.1); + transform: translateY(-1px); +} + +.wptag-service-card.enabled { + border-color: #46b450; +} + +.wptag-service-card.enabled .wptag-service-header { + background: linear-gradient(135deg, #46b450 0%, #5cbf60 100%); + color: #fff; +} + +.wptag-service-card.enabled .wptag-service-icon .dashicons { + color: #fff; +} + +.wptag-service-card.enabled .wptag-service-title h3 { + color: #fff; } .wptag-service-card.disabled { - opacity: 0.6; + opacity: 0.7; } .wptag-service-card.disabled .wptag-service-content { @@ -90,13 +149,15 @@ .wptag-service-header { display: flex; align-items: center; - padding: 20px; + padding: 15px 20px; background: #fff; border-bottom: 1px solid #e1e1e1; + transition: all 0.3s ease; } .wptag-service-icon { margin-right: 15px; + flex-shrink: 0; } .wptag-service-icon .dashicons { @@ -107,14 +168,6 @@ transition: color 0.3s ease; } -.wptag-service-card:hover .wptag-service-icon .dashicons { - color: #2271b1; -} - -.wptag-service-item:hover .wptag-service-icon .dashicons { - color: #2271b1; -} - .wptag-service-title { flex: 1; display: flex; @@ -127,6 +180,7 @@ font-size: 16px; font-weight: 600; color: #23282d; + transition: color 0.3s ease; } .wptag-switch { @@ -164,37 +218,147 @@ background-color: white; transition: .4s; border-radius: 50%; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); } input:checked + .wptag-slider { background-color: #2271b1; } +input:checked + .wptag-slider:before { + transform: translateX(26px); +} + +input:focus + .wptag-slider { + box-shadow: 0 0 1px #2271b1; +} + .wptag-slider.disabled { pointer-events: none; opacity: 0.5; } +.wptag-service-content { + padding: 20px; +} + +.wptag-form-row { + margin-bottom: 20px; +} + +.wptag-form-label { + display: block; + margin-bottom: 8px; + font-weight: 400; + color: #23282d; + font-size: 12px; +} + +.wptag-form-label .required { + color: #dc3232; + margin-left: 2px; +} + +.wptag-radio-group { + display: flex; + gap: 25px; + background: #f8f9fa; + padding: 12px 16px; + border-radius: 6px; + border: 1px solid #e1e1e1; +} + +.wptag-radio-group label { + display: flex; + align-items: center; + font-weight: 400; + font-size: 12px; + cursor: pointer; + color: #555; + transition: color 0.2s ease; +} + +.wptag-radio-group label:hover { + color: #2271b1; +} + +.wptag-radio-group input[type="radio"] { + margin-right: 8px; + margin-top: 0; +} + +.wptag-input-group { + display: flex; + gap: 10px; + align-items: flex-start; +} + +.wptag-input, +.wptag-textarea, +.wptag-select { + flex: 1; + padding: 10px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; + transition: all 0.3s ease; + background: #fff; +} + +.wptag-input:focus, +.wptag-textarea:focus, +.wptag-select:focus { + border-color: #2271b1; + outline: none; + box-shadow: 0 0 0 1px #2271b1; +} + +.wptag-input:disabled, +.wptag-textarea:disabled, +.wptag-select:disabled { + background-color: #f0f0f0; + color: #666; + cursor: not-allowed; +} + +.wptag-input-small { + max-width: 100px; + flex: none; +} + .wptag-code-editor-wrapper { position: relative; border: 1px solid #ddd; - border-radius: 4px; + border-radius: 6px; background: #f8f9fa; - margin-bottom: 10px; + overflow: hidden; +} + +.wptag-code-editor-wrapper::before { + content: 'JavaScript Supported'; + position: absolute; + top: 6px; + left: 10px; + background: #28a745; + color: white; + font-size: 10px; + padding: 2px 6px; + border-radius: 3px; + z-index: 2; + font-weight: 500; + letter-spacing: 0.5px; } .wptag-code-editor { width: 100%; - padding: 15px; + padding: 26px 16px 16px 16px; border: none; - border-radius: 4px; - font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; font-size: 13px; - line-height: 1.5; + line-height: 1.6; background: #fff; color: #333; resize: none; - overflow-y: auto; min-height: 120px; max-height: 400px; box-sizing: border-box; @@ -203,8 +367,7 @@ input:checked + .wptag-slider { .wptag-code-editor:focus { outline: none; - border-color: #2271b1; - box-shadow: 0 0 0 1px #2271b1; + box-shadow: inset 0 0 0 1px #2271b1; } .wptag-code-editor:disabled { @@ -215,11 +378,11 @@ input:checked + .wptag-slider { .wptag-code-editor-toolbar { position: absolute; - top: 8px; - right: 8px; + top: 26px; + right: 10px; display: flex; - gap: 5px; - opacity: 0.7; + gap: 6px; + opacity: 0; transition: opacity 0.3s ease; } @@ -228,36 +391,120 @@ input:checked + .wptag-slider { } .wptag-code-editor-toolbar .button { - padding: 4px 8px; + padding: 6px 10px; min-height: auto; - background: rgba(255, 255, 255, 0.9); + background: rgba(255, 255, 255, 0.95); border: 1px solid #ddd; border-radius: 3px; font-size: 12px; + backdrop-filter: blur(10px); + transition: all 0.2s ease; } .wptag-code-editor-toolbar .button:hover { background: #f0f0f0; + transform: translateY(-1px); +} + +.wptag-validation-result { + margin-top: 10px; + padding: 8px 12px; + border-radius: 4px; + font-size: 13px; + font-weight: 500; + display: none; +} + +.wptag-validation-result.valid { + background: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; + display: block; +} + +.wptag-validation-result.invalid { + background: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; + display: block; +} + +.wptag-validation-result.loading { + background: #d1ecf1; + color: #0c5460; + border: 1px solid #bee5eb; + display: block; +} + +.wptag-field-help { + margin-top: 6px; +} + +.wptag-field-help small { + color: #666; + font-size: 12px; + line-height: 1.4; + display: block; + margin-bottom: 4px; +} + +.wptag-field-help small:last-child { + margin-bottom: 0; +} + +.wptag-field-help a { + color: #b2b2b2; + text-decoration: none; + font-weight: 400; +} + +.wptag-field-help a:hover { + color: #135e96; + text-decoration: underline; +} + +.wptag-field-help a:after { + content: " ↗"; + margin-right: 2px; +} + +.wptag-template-fields, +.wptag-custom-fields { + transition: all 0.3s ease; +} + +.wptag-advanced-settings { + margin-top: 20px; + padding-top: 20px; + border-top: 1px solid #e1e1e1; + display: none; +} + +.wptag-advanced-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); + gap: 15px; } .wptag-advanced-toggle { border-top: 1px solid #e1e1e1; padding-top: 15px; - margin-top: 15px; + margin-top: 20px; } .wptag-toggle-advanced { display: flex; align-items: center; - gap: 5px; + gap: 6px; color: #666; text-decoration: none; - font-size: 13px; - padding: 5px 0; + font-size: 12px; + padding: 8px 0; border: none; background: none; cursor: pointer; transition: color 0.2s ease; + font-weight: 400; } .wptag-toggle-advanced:hover { @@ -274,137 +521,21 @@ input:checked + .wptag-slider { font-size: 16px; width: 16px; height: 16px; - transition: transform 0.2s ease; -} - -.wptag-advanced-settings { - margin-top: 15px; - padding-top: 15px; - border-top: 1px solid #e1e1e1; -} - -.wptag-service-card.disabled .wptag-code-editor-wrapper { - opacity: 0.6; - pointer-events: none; -} - -.wptag-service-card.disabled .wptag-code-editor-toolbar { - opacity: 0.5; - pointer-events: none; -} - -.wptag-service-card.disabled .wptag-toggle-advanced { - opacity: 0.6; - pointer-events: none; -} - -.wptag-service-content { - padding: 20px; -} - -.wptag-form-row { - margin-bottom: 20px; -} - -.wptag-form-label { - display: block; - margin-bottom: 5px; - font-weight: 600; - color: #23282d; -} - -.wptag-radio-group { - display: flex; - gap: 20px; -} - -.wptag-radio-group label { - display: flex; - align-items: center; - font-weight: normal; - cursor: pointer; -} - -.wptag-radio-group input[type="radio"] { - margin-right: 8px; -} - -.wptag-input-group { - display: flex; - gap: 10px; - align-items: flex-start; -} - -.wptag-input, -.wptag-textarea, -.wptag-select { - flex: 1; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-size: 14px; - transition: border-color 0.3s ease; -} - -.wptag-input:focus, -.wptag-textarea:focus, -.wptag-select:focus { - border-color: #2271b1; - outline: none; - box-shadow: 0 0 0 1px #2271b1; -} - -.wptag-input-small { - max-width: 100px; -} - -.wptag-textarea { - font-family: Consolas, Monaco, monospace; - font-size: 13px; - min-height: 120px; - resize: vertical; -} - -.wptag-validation-result { - margin-top: 8px; - font-size: 13px; - font-weight: 600; -} - -.wptag-validation-result.valid { - color: #46b450; -} - -.wptag-validation-result.invalid { - color: #dc3232; -} - -.wptag-validation-result.loading { - color: #0073aa; -} - -.wptag-template-fields, -.wptag-custom-fields { - transition: all 0.3s ease; -} - -.wptag-advanced-settings { - margin-top: 15px; - padding-top: 15px; - border-top: 1px solid #e1e1e1; - display: none; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 15px; + transition: transform 0.3s ease; } .wptag-form-actions { text-align: right; padding-top: 20px; border-top: 1px solid #ddd; + margin-top: 30px; } .wptag-validate-btn, .wptag-preview-btn { + display: flex; + align-items: center; + gap: 6px; background: #0073aa; color: #fff; border: none; @@ -412,18 +543,22 @@ input:checked + .wptag-slider { border-radius: 4px; cursor: pointer; font-size: 13px; - transition: background-color 0.3s ease; + font-weight: 500; + transition: all 0.3s ease; + flex-shrink: 0; } .wptag-validate-btn:hover, .wptag-preview-btn:hover { background: #005a87; + transform: translateY(-1px); } .wptag-validate-btn:disabled, .wptag-preview-btn:disabled { background: #ccc; cursor: not-allowed; + transform: none; } .wptag-preview-btn { @@ -449,14 +584,72 @@ input:checked + .wptag-slider { border-radius: 4px; } -.wptag-services-header p { +.wptag-services-info p { margin: 0; color: #666; + font-size: 14px; +} + +.wptag-services-info p small { + color: #28a745; + font-weight: 500; +} + +.wptag-services-info p small strong { + color: #155724; + background: #d4edda; + padding: 2px 4px; + border-radius: 3px; +} + +.wptag-field-help details { + margin-top: 8px; +} + +.wptag-field-help summary { + cursor: pointer; + font-size: 11px; + color: #666; + user-select: none; +} + +.wptag-field-help summary:hover { + color: #2271b1; +} + +.wptag-field-help pre { + background: #f8f9fa; + padding: 8px; + border-radius: 4px; + font-size: 11px; + margin-top: 4px; + overflow-x: auto; + border: 1px solid #e1e1e1; +} + +body.wp-admin script[src*="googletagmanager"], +body.wp-admin script[src*="google-analytics"], +body.wp-admin script[src*="facebook"], +body.wp-admin script[src*="clarity.ms"], +body.wp-admin script[src*="hotjar"], +body.wp-admin script[src*="tiktok"], +body.wp-admin script[src*="linkedin"], +body.wp-admin script[src*="twitter"], +body.wp-admin script[src*="pinterest"], +body.wp-admin script[src*="snapchat"] { + display: none !important; +} + +body.wp-admin noscript img[src*="facebook"], +body.wp-admin noscript img[src*="linkedin"], +body.wp-admin noscript img[src*="pinterest"] { + display: none !important; } .wptag-services-actions { display: flex; gap: 10px; + flex-shrink: 0; } .wptag-service-category { @@ -470,6 +663,9 @@ input:checked + .wptag-slider { font-weight: 600; border-bottom: 2px solid #2271b1; padding-bottom: 10px; + display: flex; + align-items: center; + gap: 10px; } .wptag-service-item { @@ -486,6 +682,12 @@ input:checked + .wptag-slider { .wptag-service-item:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.1); + transform: translateY(-1px); +} + +.wptag-service-item.enabled { + border-color: #46b450; + background: linear-gradient(135deg, #f8fff8 0%, #f0f9f0 100%); } .wptag-service-item.disabled { @@ -507,39 +709,83 @@ input:checked + .wptag-slider { .wptag-service-details p { margin: 0; - color: #666; - font-size: 14px; + color: #b2b2b2; + font-size: 12px; + line-height: 1.4; } -#wptag-preview-modal { +.wptag-service-details p:not(:last-child) { + margin-bottom: 8px; +} + +.wptag-service-details p small a { + color: #2271b1; + text-decoration: none; + font-weight: 500; +} + +.wptag-service-details p small a:hover { + color: #135e96; + text-decoration: underline; +} + +.wptag-service-details p small a:before { + content: "📚 "; + margin-right: 2px; +} + +.wptag-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; - background: rgba(0,0,0,0.5); z-index: 100000; display: flex; align-items: center; justify-content: center; } +.wptag-modal-backdrop { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.6); + backdrop-filter: blur(3px); +} + .wptag-modal-content { background: #fff; border-radius: 8px; - max-width: 80%; + max-width: 90%; max-height: 80%; overflow: hidden; display: flex; flex-direction: column; - box-shadow: 0 10px 40px rgba(0,0,0,0.3); + box-shadow: 0 20px 60px rgba(0,0,0,0.3); + position: relative; + z-index: 1; + animation: modalSlideIn 0.3s ease; +} + +@keyframes modalSlideIn { + from { + opacity: 0; + transform: scale(0.9) translateY(-20px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } } .wptag-modal-header { display: flex; justify-content: space-between; align-items: center; - padding: 20px; + padding: 20px 24px; border-bottom: 1px solid #e1e1e1; background: #f9f9f9; } @@ -548,6 +794,7 @@ input:checked + .wptag-slider { margin: 0; font-size: 18px; color: #23282d; + font-weight: 600; } .wptag-modal-close { @@ -557,8 +804,8 @@ input:checked + .wptag-slider { cursor: pointer; color: #666; padding: 0; - width: 30px; - height: 30px; + width: 32px; + height: 32px; display: flex; align-items: center; justify-content: center; @@ -572,92 +819,30 @@ input:checked + .wptag-slider { } .wptag-modal-body { - padding: 20px; + padding: 24px; overflow: auto; + flex: 1; } #wptag-preview-code { background: #f4f4f4; border: 1px solid #ddd; - border-radius: 4px; - padding: 15px; - font-family: Consolas, Monaco, monospace; + border-radius: 6px; + padding: 20px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; font-size: 13px; - line-height: 1.5; + line-height: 1.6; white-space: pre-wrap; word-wrap: break-word; margin: 0; min-height: 200px; color: #333; -} - -@media screen and (max-width: 1200px) { - .wptag-services-grid { - grid-template-columns: 1fr; - } -} - -@media screen and (max-width: 782px) { - .wptag-header { - flex-direction: column; - gap: 15px; - } - - .wptag-header-actions { - width: 100%; - justify-content: center; - } - - .wptag-services-header { - flex-direction: column; - gap: 15px; - } - - .wptag-services-actions { - width: 100%; - justify-content: center; - } - - .wptag-input-group { - flex-direction: column; - } - - .wptag-input-group button { - align-self: flex-start; - } - - .wptag-advanced-settings { - grid-template-columns: 1fr; - } - - .wptag-radio-group { - flex-direction: column; - gap: 10px; - } - - .wptag-modal-content { - max-width: 95%; - max-height: 95%; - } - - .wptag-service-item { - flex-direction: column; - gap: 15px; - } - - .wptag-service-info { - text-align: center; - } - - .wptag-service-toggle { - align-self: center; - } + max-height: 500px; + overflow: auto; } .wptag-loading { position: relative; - opacity: 0.6; - pointer-events: none; } .wptag-loading::after { @@ -672,6 +857,7 @@ input:checked + .wptag-slider { border-radius: 50%; animation: spin 1s linear infinite; transform: translate(-50%, -50%); + z-index: 1; } @keyframes spin { @@ -679,28 +865,41 @@ input:checked + .wptag-slider { 100% { transform: translate(-50%, -50%) rotate(360deg); } } -.wptag-text-center { - text-align: center; +.wptag-button-loading { + opacity: 0.7; + pointer-events: none; } -.wptag-text-muted { - color: #666; +.wptag-button-loading::after { + content: ''; + display: inline-block; + width: 12px; + height: 12px; + margin-left: 8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; } -.wptag-mb-0 { - margin-bottom: 0; -} - -.wptag-mt-20 { - margin-top: 20px; -} - -.wptag-hidden { - display: none; +#wptag-save-btn.wptag-button-loading { + opacity: 0.7; } .notice { margin: 5px 0 15px 0; + animation: noticeSlideIn 0.3s ease; +} + +@keyframes noticeSlideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } } .notice.notice-success { @@ -709,4 +908,115 @@ input:checked + .wptag-slider { .notice.notice-error { border-left-color: #dc3232; +} + +@media screen and (max-width: 1200px) { + .wptag-services-grid { + grid-template-columns: 1fr; + } + + .wptag-advanced-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media screen and (max-width: 782px) { + .wptag-header { + flex-direction: column; + gap: 15px; + text-align: center; + } + + .wptag-header-actions { + width: 100%; + justify-content: center; + flex-wrap: wrap; + } + + .wptag-services-header { + flex-direction: column; + gap: 15px; + text-align: center; + } + + .wptag-services-actions { + width: 100%; + justify-content: center; + } + + .wptag-input-group { + flex-direction: column; + gap: 8px; + } + + .wptag-input-group button { + align-self: flex-start; + width: auto; + } + + .wptag-advanced-grid { + grid-template-columns: 1fr; + } + + .wptag-radio-group { + flex-direction: column; + gap: 12px; + } + + .wptag-modal-content { + max-width: 95%; + max-height: 95%; + margin: 20px; + } + + .wptag-service-item { + flex-direction: column; + gap: 15px; + text-align: center; + } + + .wptag-service-info { + justify-content: center; + text-align: center; + } + + .wptag-service-toggle { + align-self: center; + } + + .nav-tab { + font-size: 13px; + padding: 8px 12px; + } + + .wptag-services-grid { + gap: 15px; + } +} + +@media screen and (max-width: 480px) { + .wptag-header-actions { + flex-direction: column; + width: 100%; + } + + .wptag-header-actions .button { + justify-content: center; + } + + .wptag-services-actions { + flex-direction: column; + width: 100%; + } + + .wptag-modal-content { + max-width: 100%; + max-height: 100%; + margin: 0; + border-radius: 0; + } + + .nav-tab .count { + display: none; + } } \ No newline at end of file diff --git a/assets/admin.js b/assets/admin.js index 35a59fa..cee4303 100644 --- a/assets/admin.js +++ b/assets/admin.js @@ -1,11 +1,41 @@ jQuery(document).ready(function($) { + if (typeof window.gtag !== 'undefined') { + delete window.gtag; + } + if (typeof window.fbq !== 'undefined') { + delete window.fbq; + } + if (typeof window.dataLayer !== 'undefined') { + delete window.dataLayer; + } + + window.gtag = function() { + console.log('WPTag: gtag() calls are disabled in admin area'); + }; + window.fbq = function() { + console.log('WPTag: fbq() calls are disabled in admin area'); + }; + WPTagAdmin.init(); + + $(window).on('load', function() { + setTimeout(function() { + WPTagAdmin.initializing = false; + WPTagAdmin.changeTrackingEnabled = true; + }, 500); + }); }); var WPTagAdmin = { + formChanged: false, + changeTrackingEnabled: false, + initializing: true, + init: function() { var $ = jQuery; + var self = this; + this.handleServiceToggle(); this.handleCodeTypeToggle(); this.handleValidation(); @@ -18,39 +48,72 @@ var WPTagAdmin = { this.handleTabSwitching(); this.handleAdvancedToggle(); this.handleCodeEditor(); + this.initTooltips(); + + setTimeout(function() { + self.initializing = false; + self.handleFormChangeTracking(); + }, 1000); }, handleServiceToggle: function() { var $ = jQuery; + var self = this; + $('.wptag-service-card input[name*="[enabled]"]').on('change', function() { var $card = $(this).closest('.wptag-service-card'); var isEnabled = $(this).is(':checked'); + self.changeTrackingEnabled = false; + if (isEnabled) { - $card.removeClass('disabled'); + $card.removeClass('disabled').addClass('enabled'); $card.find('.wptag-service-content input, .wptag-service-content select, .wptag-service-content textarea, .wptag-service-content button').prop('disabled', false); } else { - $card.addClass('disabled'); + $card.removeClass('enabled').addClass('disabled'); $card.find('.wptag-service-content input, .wptag-service-content select, .wptag-service-content textarea, .wptag-service-content button').prop('disabled', true); + $card.find('.wptag-validation-result').hide(); } + + setTimeout(function() { + self.changeTrackingEnabled = true; + if (!self.initializing) { + self.formChanged = true; + } + }, 100); }); - $('.wptag-service-card input[name*="[enabled]"]').trigger('change'); + $('.wptag-service-card input[name*="[enabled]"]').each(function() { + $(this).trigger('change'); + }); }, handleCodeTypeToggle: function() { var $ = jQuery; + var self = this; + $('input[name*="[use_template]"]').on('change', function() { var $card = $(this).closest('.wptag-service-card'); var useTemplate = $(this).val() === '1' && $(this).is(':checked'); + self.changeTrackingEnabled = false; + if (useTemplate) { - $card.find('.wptag-template-fields').show(); - $card.find('.wptag-custom-fields').hide(); + $card.find('.wptag-template-fields').slideDown(300); + $card.find('.wptag-custom-fields').slideUp(300); } else { - $card.find('.wptag-template-fields').hide(); - $card.find('.wptag-custom-fields').show(); + $card.find('.wptag-template-fields').slideUp(300); + $card.find('.wptag-custom-fields').slideDown(300); } + + $card.find('.wptag-validation-result').hide(); + + setTimeout(function() { + self.changeTrackingEnabled = true; + if (!self.initializing) { + self.formChanged = true; + } + }, 100); }); $('input[name*="[use_template]"]:checked').trigger('change'); @@ -58,18 +121,31 @@ var WPTagAdmin = { handleValidation: function() { var $ = jQuery; - $('.wptag-validate-btn').on('click', function() { + + $('.wptag-validate-btn').on('click', function(e) { + e.preventDefault(); + var $btn = $(this); var $card = $btn.closest('.wptag-service-card'); var service = $card.data('service'); var $result = $card.find('.wptag-validation-result'); var useTemplate = $card.find('input[name*="[use_template]"]:checked').val() === '1'; - var idValue = $card.find('input[type="text"]').first().val(); - var customCode = $card.find('textarea').val(); + var idValue = $card.find('input[type="text"]').first().val().trim(); + var customCode = $card.find('textarea').val().trim(); - $btn.prop('disabled', true).text(wptagAdmin.strings.validating); - $result.removeClass('valid invalid').addClass('loading').text(wptagAdmin.strings.validating); + if (useTemplate && !idValue) { + WPTagAdmin.showValidationResult($result, 'invalid', 'Please enter an ID value first'); + return; + } + + if (!useTemplate && !customCode) { + WPTagAdmin.showValidationResult($result, 'invalid', 'Please enter custom code first'); + return; + } + + $btn.addClass('wptag-button-loading').prop('disabled', true); + WPTagAdmin.showValidationResult($result, 'loading', wptagAdmin.strings.validating); $.ajax({ url: wptagAdmin.ajax_url, @@ -84,42 +160,54 @@ var WPTagAdmin = { }, success: function(response) { if (response.success) { - $result.removeClass('loading invalid').addClass('valid') - .text('✓ ' + response.data.message); + WPTagAdmin.showValidationResult($result, 'valid', '✓ ' + response.data.message); } else { - $result.removeClass('loading valid').addClass('invalid') - .text('✗ ' + response.data.message); + WPTagAdmin.showValidationResult($result, 'invalid', '✗ ' + response.data.message); } }, - error: function() { - $result.removeClass('loading valid').addClass('invalid') - .text('✗ Validation failed'); + error: function(xhr, status, error) { + console.error('Validation error:', error); + WPTagAdmin.showValidationResult($result, 'invalid', '✗ Validation failed. Please try again.'); }, complete: function() { - $btn.prop('disabled', false).text('Validate'); + $btn.removeClass('wptag-button-loading').prop('disabled', false); } }); }); $('input[type="text"], textarea').on('input', function() { - $(this).closest('.wptag-service-card').find('.wptag-validation-result').text(''); + $(this).closest('.wptag-service-card').find('.wptag-validation-result').hide(); + if (WPTagAdmin.changeTrackingEnabled && !WPTagAdmin.initializing) { + WPTagAdmin.formChanged = true; + } }); }, + showValidationResult: function($result, type, message) { + $result.removeClass('valid invalid loading') + .addClass(type) + .text(message) + .show(); + }, + handlePreview: function() { var $ = jQuery; - $('.wptag-preview-btn').on('click', function() { + + $('.wptag-preview-btn').on('click', function(e) { + e.preventDefault(); + var $btn = $(this); var $card = $btn.closest('.wptag-service-card'); var service = $card.data('service'); - var idValue = $card.find('input[type="text"]').first().val(); + var idValue = $card.find('input[type="text"]').first().val().trim(); - if (!idValue.trim()) { + if (!idValue) { alert('Please enter an ID value to preview'); + $card.find('input[type="text"]').first().focus(); return; } - $btn.prop('disabled', true).text(wptagAdmin.strings.loading); + $btn.addClass('wptag-button-loading').prop('disabled', true); $.ajax({ url: wptagAdmin.ajax_url, @@ -132,17 +220,20 @@ var WPTagAdmin = { }, success: function(response) { if (response.success) { - $('#wptag-preview-code').text(response.data.preview); - $('#wptag-preview-modal').show(); + var previewCode = response.data.preview; + $('#wptag-preview-code').text(previewCode); + $('#wptag-preview-modal').fadeIn(300); + $('body').addClass('modal-open'); } else { - alert('Error: ' + response.data.message); + WPTagAdmin.showNotice('error', 'Preview Error: ' + response.data.message); } }, - error: function() { - alert('Failed to generate preview'); + error: function(xhr, status, error) { + console.error('Preview error:', error); + WPTagAdmin.showNotice('error', 'Failed to generate preview. Please try again.'); }, complete: function() { - $btn.prop('disabled', false).text(wptagAdmin.strings.preview); + $btn.removeClass('wptag-button-loading').prop('disabled', false); } }); }); @@ -150,10 +241,12 @@ var WPTagAdmin = { handleExportImport: function() { var $ = jQuery; - $('#wptag-export-btn').on('click', function() { + + $('#wptag-export-btn').on('click', function(e) { + e.preventDefault(); + var $btn = $(this); - var originalText = $btn.text(); - $btn.prop('disabled', true).text(wptagAdmin.strings.loading); + $btn.addClass('wptag-button-loading').prop('disabled', true); $.ajax({ url: wptagAdmin.ajax_url, @@ -167,19 +260,21 @@ var WPTagAdmin = { WPTagAdmin.downloadFile(response.data.data, response.data.filename); WPTagAdmin.showNotice('success', wptagAdmin.strings.export_success); } else { - alert('Export failed: ' + response.data.message); + WPTagAdmin.showNotice('error', 'Export failed: ' + response.data.message); } }, - error: function() { - alert('Export failed'); + error: function(xhr, status, error) { + console.error('Export error:', error); + WPTagAdmin.showNotice('error', 'Export failed. Please try again.'); }, complete: function() { - $btn.prop('disabled', false).text(originalText); + $btn.removeClass('wptag-button-loading').prop('disabled', false); } }); }); - $('#wptag-import-btn').on('click', function() { + $('#wptag-import-btn').on('click', function(e) { + e.preventDefault(); $('#wptag-import-file').click(); }); @@ -187,7 +282,14 @@ var WPTagAdmin = { var file = this.files[0]; if (!file) return; + if (!file.name.endsWith('.json')) { + WPTagAdmin.showNotice('error', 'Please select a valid JSON file.'); + this.value = ''; + return; + } + if (!confirm(wptagAdmin.strings.confirm_import)) { + this.value = ''; return; } @@ -206,34 +308,43 @@ var WPTagAdmin = { success: function(response) { if (response.success) { WPTagAdmin.showNotice('success', wptagAdmin.strings.import_success); + WPTagAdmin.formChanged = false; + WPTagAdmin.initializing = true; setTimeout(function() { location.reload(); - }, 1000); + }, 1500); } else { - alert('Import failed: ' + response.data.message); + WPTagAdmin.showNotice('error', 'Import failed: ' + response.data.message); } }, - error: function() { - alert('Import failed'); + error: function(xhr, status, error) { + console.error('Import error:', error); + WPTagAdmin.showNotice('error', 'Import failed. Please try again.'); } }); }; - reader.readAsText(file); + reader.onerror = function() { + WPTagAdmin.showNotice('error', 'Failed to read the file.'); + }; + + reader.readAsText(file); this.value = ''; }); }, handleReset: function() { var $ = jQuery; - $('#wptag-reset-btn').on('click', function() { + + $('#wptag-reset-btn').on('click', function(e) { + e.preventDefault(); + if (!confirm(wptagAdmin.strings.confirm_reset)) { return; } var $btn = $(this); - var originalText = $btn.text(); - $btn.prop('disabled', true).text(wptagAdmin.strings.loading); + $btn.addClass('wptag-button-loading').prop('disabled', true); $.ajax({ url: wptagAdmin.ajax_url, @@ -245,18 +356,21 @@ var WPTagAdmin = { success: function(response) { if (response.success) { WPTagAdmin.showNotice('success', wptagAdmin.strings.reset_success); + WPTagAdmin.formChanged = false; + WPTagAdmin.initializing = true; setTimeout(function() { location.reload(); - }, 1000); + }, 1500); } else { - alert('Reset failed: ' + response.data.message); + WPTagAdmin.showNotice('error', 'Reset failed: ' + response.data.message); } }, - error: function() { - alert('Reset failed'); + error: function(xhr, status, error) { + console.error('Reset error:', error); + WPTagAdmin.showNotice('error', 'Reset failed. Please try again.'); }, complete: function() { - $btn.prop('disabled', false).text(originalText); + $btn.removeClass('wptag-button-loading').prop('disabled', false); } }); }); @@ -264,14 +378,26 @@ var WPTagAdmin = { handleServicesManagement: function() { var $ = jQuery; - $('#wptag-enable-all').on('click', function() { - $('input[name="enabled_services[]"]').prop('checked', true); - $('.wptag-service-item').removeClass('disabled'); + var self = this; + + $('#wptag-enable-all').on('click', function(e) { + e.preventDefault(); + self.changeTrackingEnabled = false; + $('input[name="enabled_services[]"]').prop('checked', true).trigger('change'); + setTimeout(function() { + self.changeTrackingEnabled = true; + self.formChanged = true; + }, 100); }); - $('#wptag-disable-all').on('click', function() { - $('input[name="enabled_services[]"]').prop('checked', false); - $('.wptag-service-item').addClass('disabled'); + $('#wptag-disable-all').on('click', function(e) { + e.preventDefault(); + self.changeTrackingEnabled = false; + $('input[name="enabled_services[]"]').prop('checked', false).trigger('change'); + setTimeout(function() { + self.changeTrackingEnabled = true; + self.formChanged = true; + }, 100); }); $('.wptag-service-item input[type="checkbox"]').on('change', function() { @@ -279,9 +405,13 @@ var WPTagAdmin = { var isChecked = $(this).is(':checked'); if (isChecked) { - $item.removeClass('disabled'); + $item.removeClass('disabled').addClass('enabled'); } else { - $item.addClass('disabled'); + $item.removeClass('enabled').addClass('disabled'); + } + + if (self.changeTrackingEnabled && !self.initializing) { + self.formChanged = true; } }); @@ -290,9 +420,10 @@ var WPTagAdmin = { handleModal: function() { var $ = jQuery; - $('.wptag-modal-close, #wptag-preview-modal').on('click', function(e) { + + $('.wptag-modal-close, .wptag-modal-backdrop').on('click', function(e) { if (e.target === this) { - $('#wptag-preview-modal').hide(); + WPTagAdmin.closeModal(); } }); @@ -302,39 +433,43 @@ var WPTagAdmin = { $(document).on('keydown', function(e) { if (e.keyCode === 27 && $('#wptag-preview-modal').is(':visible')) { - $('#wptag-preview-modal').hide(); + WPTagAdmin.closeModal(); } }); }, + closeModal: function() { + var $ = jQuery; + $('#wptag-preview-modal').fadeOut(300, function() { + $('body').removeClass('modal-open'); + }); + }, + handleFormSubmission: function() { var $ = jQuery; + var self = this; + $('.wptag-settings-form').on('submit', function(e) { var hasErrors = false; + var $form = $(this); - $('.wptag-service-card').each(function() { + $('.wptag-service-card.enabled').each(function() { var $card = $(this); - var isEnabled = $card.find('input[name*="[enabled]"]').is(':checked'); - - if (!isEnabled) { - return; - } - var useTemplate = $card.find('input[name*="[use_template]"]:checked').val() === '1'; if (useTemplate) { - var idInput = $card.find('input[type="text"]').first(); - if (idInput.val().trim() === '') { - alert('Please enter an ID for enabled services or disable them.'); - idInput.focus(); + var $idInput = $card.find('input[type="text"]').first(); + if ($idInput.val().trim() === '') { + WPTagAdmin.showNotice('error', 'Please enter an ID for all enabled services or disable them.'); + $idInput.focus(); hasErrors = true; return false; } } else { - var customCodeTextarea = $card.find('textarea'); - if (customCodeTextarea.val().trim() === '') { - alert('Please enter custom code for enabled services or disable them.'); - customCodeTextarea.focus(); + var $customCodeTextarea = $card.find('textarea'); + if ($customCodeTextarea.val().trim() === '') { + WPTagAdmin.showNotice('error', 'Please enter custom code for all enabled services or disable them.'); + $customCodeTextarea.focus(); hasErrors = true; return false; } @@ -346,51 +481,69 @@ var WPTagAdmin = { return false; } - $(this).find('input[type="submit"]').prop('disabled', true).val('Saving...'); + var $saveBtn = $form.find('#wptag-save-btn'); + $saveBtn.addClass('wptag-button-loading').prop('disabled', true).val('Saving...'); + self.formChanged = false; + }); + + $('#wptag-services-form').on('submit', function() { + var $saveBtn = $(this).find('input[type="submit"]'); + $saveBtn.addClass('wptag-button-loading').prop('disabled', true).val('Saving...'); + WPTagAdmin.formChanged = false; }); }, handleTabSwitching: function() { var $ = jQuery; + var self = this; + $('.nav-tab').on('click', function(e) { - var href = $(this).attr('href'); - if (href && href.indexOf('tab=') !== -1) { - var tab = href.split('tab=')[1]; - var currentUrl = window.location.href.split('?')[0]; - var newUrl = currentUrl + '?page=wptag-settings&tab=' + tab; - window.location.href = newUrl; + if (self.formChanged && !self.initializing) { + var confirmed = confirm('You have unsaved changes. Do you want to continue without saving?'); + if (!confirmed) { + e.preventDefault(); + return false; + } else { + self.formChanged = false; + } } }); }, handleAdvancedToggle: function() { var $ = jQuery; + $('.wptag-toggle-advanced').on('click', function(e) { e.preventDefault(); + var $btn = $(this); var $card = $btn.closest('.wptag-service-card'); var $advanced = $card.find('.wptag-advanced-settings'); var $icon = $btn.find('.dashicons'); if ($advanced.is(':visible')) { - $advanced.slideUp(); + $advanced.slideUp(300); $icon.removeClass('dashicons-arrow-up-alt2').addClass('dashicons-arrow-down-alt2'); - $btn.html('Advanced Settings'); + $btn.html('' + wptagAdmin.strings.advanced_settings); } else { - $advanced.slideDown(); + $advanced.slideDown(300); $icon.removeClass('dashicons-arrow-down-alt2').addClass('dashicons-arrow-up-alt2'); - $btn.html('Hide Advanced Settings'); + $btn.html('' + wptagAdmin.strings.hide_advanced); } }); }, handleCodeEditor: function() { var $ = jQuery; + $('.wptag-code-editor').each(function() { var $editor = $(this); $editor.on('input', function() { WPTagAdmin.updateEditorHeight(this); + if (WPTagAdmin.changeTrackingEnabled && !WPTagAdmin.initializing) { + WPTagAdmin.formChanged = true; + } }); $editor.on('keydown', function(e) { @@ -407,7 +560,9 @@ var WPTagAdmin = { WPTagAdmin.updateEditorHeight(this); }); - $('.wptag-format-code').on('click', function() { + $('.wptag-format-code').on('click', function(e) { + e.preventDefault(); + var $btn = $(this); var $editor = $btn.closest('.wptag-code-editor-wrapper').find('.wptag-code-editor'); var code = $editor.val(); @@ -417,25 +572,88 @@ var WPTagAdmin = { var formatted = WPTagAdmin.formatCode(code); $editor.val(formatted); WPTagAdmin.updateEditorHeight($editor[0]); + if (WPTagAdmin.changeTrackingEnabled && !WPTagAdmin.initializing) { + WPTagAdmin.formChanged = true; + } + WPTagAdmin.showNotice('success', 'Code formatted successfully.'); } catch (e) { console.log('Code formatting failed:', e); + WPTagAdmin.showNotice('error', 'Code formatting failed. Please check your code syntax.'); } } }); - $('.wptag-clear-code').on('click', function() { + $('.wptag-clear-code').on('click', function(e) { + e.preventDefault(); + if (confirm('Are you sure you want to clear the code?')) { var $btn = $(this); var $editor = $btn.closest('.wptag-code-editor-wrapper').find('.wptag-code-editor'); - $editor.val(''); + $editor.val('').focus(); WPTagAdmin.updateEditorHeight($editor[0]); + if (WPTagAdmin.changeTrackingEnabled && !WPTagAdmin.initializing) { + WPTagAdmin.formChanged = true; + } } }); }, + handleFormChangeTracking: function() { + var $ = jQuery; + var self = this; + + self.changeTrackingEnabled = true; + + $(document).on('input keyup', 'input[type="text"], textarea', function() { + if ($(this).closest('.wptag-admin').length > 0 && self.changeTrackingEnabled && !self.initializing) { + self.formChanged = true; + } + }); + + $(document).on('change', 'select, input[type="radio"], input[type="checkbox"]:not([name*="enabled_services"]):not([name*="[enabled]"]):not([name*="[use_template]"])', function() { + if ($(this).closest('.wptag-admin').length > 0 && self.changeTrackingEnabled && !self.initializing) { + self.formChanged = true; + } + }); + + window.addEventListener('beforeunload', function(e) { + if (self.formChanged && !self.initializing) { + e.preventDefault(); + e.returnValue = 'You have unsaved changes. Are you sure you want to leave?'; + return e.returnValue; + } + }); + }, + + initTooltips: function() { + var $ = jQuery; + + $('[title]').each(function() { + var $el = $(this); + var title = $el.attr('title'); + + $el.removeAttr('title').on('mouseenter', function() { + $('
' + title + '
') + .appendTo('body') + .fadeIn(200); + }).on('mouseleave', function() { + $('.wptag-tooltip').remove(); + }).on('mousemove', function(e) { + $('.wptag-tooltip').css({ + top: e.pageY + 10, + left: e.pageX + 10 + }); + }); + }); + }, + updateEditorHeight: function(editor) { + var $ = jQuery; + var $editor = $(editor); + editor.style.height = 'auto'; - editor.style.height = Math.max(editor.scrollHeight, 120) + 'px'; + var newHeight = Math.max(editor.scrollHeight, 120); + editor.style.height = newHeight + 'px'; }, formatCode: function(code) { @@ -444,11 +662,30 @@ var WPTagAdmin = { .replace(/>\n<') .replace(/^\s+|\s+$/g, '') .split('\n') - .map(function(line) { - return line.trim(); + .map(function(line, index, array) { + line = line.trim(); + if (line.length === 0) return ''; + + var indent = 0; + for (var i = 0; i < index; i++) { + var prevLine = array[i].trim(); + if (prevLine.match(/<[^\/][^>]*[^\/]>$/)) { + indent += 2; + } + if (prevLine.match(/<\/[^>]+>$/)) { + indent -= 2; + } + } + + if (line.match(/^<\/[^>]+>$/)) { + indent -= 2; + } + + indent = Math.max(0, indent); + return ' '.repeat(indent) + line; }) .filter(function(line) { - return line.length > 0; + return line.trim().length > 0; }) .join('\n'); } catch (e) { @@ -458,33 +695,54 @@ var WPTagAdmin = { }, downloadFile: function(data, filename) { - var blob = new Blob([data], { type: 'application/json' }); - var url = window.URL.createObjectURL(blob); - var a = document.createElement('a'); - a.href = url; - a.download = filename; - a.style.display = 'none'; - document.body.appendChild(a); - a.click(); - window.URL.revokeObjectURL(url); - document.body.removeChild(a); + try { + var blob = new Blob([data], { type: 'application/json' }); + + if (window.navigator && window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveOrOpenBlob(blob, filename); + return; + } + + var url = window.URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = filename; + a.style.display = 'none'; + document.body.appendChild(a); + a.click(); + + setTimeout(function() { + window.URL.revokeObjectURL(url); + document.body.removeChild(a); + }, 100); + } catch (e) { + console.error('Download failed:', e); + WPTagAdmin.showNotice('error', 'Download failed. Please try again.'); + } }, showNotice: function(type, message) { var $ = jQuery; - var $notice = $('

' + message + '

'); + + $('.wptag-admin .notice').remove(); + + var $notice = $('

' + message + '

'); $('.wptag-admin h1').after($notice); + $notice.find('.notice-dismiss').on('click', function() { + $notice.fadeOut(300, function() { + $(this).remove(); + }); + }); + setTimeout(function() { - $notice.fadeOut(function() { + $notice.fadeOut(300, function() { $(this).remove(); }); }, 5000); - $notice.find('.notice-dismiss').on('click', function() { - $notice.fadeOut(function() { - $(this).remove(); - }); - }); + $('html, body').animate({ + scrollTop: $('.wptag-admin').offset().top - 50 + }, 500); } }; \ No newline at end of file diff --git a/includes/class-admin.php b/includes/class-admin.php index 93d5d4f..55cd58b 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -28,6 +28,8 @@ class Admin { } private function init_hooks() { + add_filter('wptag_should_output_codes', '__return_false', 999); + add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_init', array($this, 'admin_init')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); @@ -120,7 +122,10 @@ class Admin { 'reset_success' => 'Settings reset successfully', 'confirm_reset' => 'Are you sure you want to reset all settings? This cannot be undone.', 'confirm_import' => 'This will overwrite your current settings. Continue?', - 'loading' => 'Loading...' + 'loading' => 'Loading...', + 'fill_required' => 'Please fill in required fields for enabled services.', + 'advanced_settings' => 'Advanced Settings', + 'hide_advanced' => 'Hide Advanced Settings' ) )); } @@ -130,7 +135,10 @@ class Admin { wp_die(__('You do not have sufficient permissions to access this page.')); } - $this->handle_form_submission(); + add_filter('wptag_should_output_codes', '__return_false', 999); + remove_action('wp_head', array($this->frontend ?? null, 'output_head_codes'), 1); + remove_action('wp_body_open', array($this->frontend ?? null, 'output_body_codes'), 1); + remove_action('wp_footer', array($this->frontend ?? null, 'output_footer_codes'), 1); $active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'analytics'; $categories = $this->get_categories_with_services(); @@ -143,13 +151,25 @@ class Admin {
-

Manage your tracking codes and analytics services with ease.

+

Manage your tracking codes and analytics services with ease. Enable services in the Services Management tab, then configure them in their respective category tabs.

+ +

Debug Mode: Check your website's source code for WPTag debug comments if codes are not appearing.

+
current_user_can_manage_codes()): ?> - - - + + +
@@ -157,14 +177,18 @@ class Admin {