feat: Add license management and tabbed settings (v0.3.0)

- Implement license management using magdev/wc-licensed-product-client
- Reorganize settings page into License, Default Settings, Integrations tabs
- Add license validation and activation via AJAX
- Frontend features require valid license (admin works always)
- Update translations with German (de_CH) for license strings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-29 12:03:05 +01:00
parent d62f01cf41
commit f3cd19efe0
15 changed files with 6539 additions and 99 deletions

View File

@@ -4,4 +4,122 @@
* @package WP_FediStream
*/
/* Admin styles will be added here */
/* Settings page tabs */
.nav-tab-wrapper + .fedistream-settings-content {
margin-top: -1px;
}
.fedistream-settings-content {
background: #fff;
border: 1px solid #c3c4c7;
border-top: none;
padding: 20px;
}
/* Active tab styling */
.wrap .nav-tab-wrapper .nav-tab-active {
background: #fff;
border-bottom-color: #fff;
}
/* License status banner */
.fedistream-license-status {
margin-bottom: 20px;
}
.fedistream-license-status .dashicons {
color: inherit;
}
.fedistream-license-status.notice-success .dashicons {
color: #00a32a;
}
.fedistream-license-status.notice-error .dashicons {
color: #d63638;
}
.fedistream-license-status.notice-warning .dashicons {
color: #dba617;
}
.fedistream-license-status.notice-info .dashicons {
color: #72aee6;
}
/* License form buttons */
#fedistream-license-form .button .dashicons {
font-size: 16px;
width: 16px;
height: 16px;
line-height: 1.3;
}
/* License message display */
#fedistream-license-message {
padding: 10px 15px;
}
#fedistream-license-message p {
margin: 0;
}
/* Dashboard stats grid */
.fedistream-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin: 20px 0;
}
.fedistream-stat-box {
background: #fff;
padding: 20px;
border: 1px solid #ccd0d4;
border-radius: 4px;
}
.fedistream-stat-box h3 {
margin: 0 0 10px;
}
.fedistream-stat-box p {
font-size: 2em;
margin: 0;
color: #2271b1;
}
/* Quick actions */
.fedistream-quick-actions {
background: #fff;
padding: 20px;
border: 1px solid #ccd0d4;
border-radius: 4px;
margin: 20px 0;
}
/* Info box */
.fedistream-info {
background: #fff;
padding: 20px;
border: 1px solid #ccd0d4;
border-radius: 4px;
}
/* Responsive adjustments */
@media screen and (max-width: 782px) {
.fedistream-stats {
grid-template-columns: repeat(2, 1fr);
}
#fedistream-license-form .button {
display: block;
margin: 10px 0 0 0 !important;
}
}
@media screen and (max-width: 480px) {
.fedistream-stats {
grid-template-columns: 1fr;
}
}

View File

@@ -8,7 +8,115 @@
'use strict';
$(document).ready(function() {
// Admin scripts will be added here
// License validation functionality
initLicenseValidation();
});
/**
* Initialize license validation AJAX handlers.
*/
function initLicenseValidation() {
var $validateBtn = $('#fedistream-validate-license');
var $activateBtn = $('#fedistream-activate-license');
var $spinner = $('#fedistream-license-spinner');
var $message = $('#fedistream-license-message');
if (!$validateBtn.length) {
return;
}
// Validate license button
$validateBtn.on('click', function(e) {
e.preventDefault();
performLicenseAction('fedistream_validate_license', 'Validating...');
});
// Activate license button
$activateBtn.on('click', function(e) {
e.preventDefault();
performLicenseAction('fedistream_activate_license', 'Activating...');
});
/**
* Perform license AJAX action.
*
* @param {string} action AJAX action name.
* @param {string} loadingText Loading button text.
*/
function performLicenseAction(action, loadingText) {
var originalText = $validateBtn.text();
// Show loading state
$spinner.addClass('is-active');
$validateBtn.prop('disabled', true);
$activateBtn.prop('disabled', true);
$message.hide();
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: action,
nonce: fedistreamLicenseNonce
},
success: function(response) {
$spinner.removeClass('is-active');
$validateBtn.prop('disabled', false);
$activateBtn.prop('disabled', false);
if (response.success) {
showMessage('success', response.data.message);
// Reload page to show updated status
setTimeout(function() {
window.location.reload();
}, 1500);
} else {
showMessage('error', response.data.message || 'An error occurred.');
}
},
error: function(xhr, status, error) {
$spinner.removeClass('is-active');
$validateBtn.prop('disabled', false);
$activateBtn.prop('disabled', false);
showMessage('error', 'Request failed. Please try again.');
}
});
}
/**
* Show a message to the user.
*
* @param {string} type Message type: 'success', 'error', 'warning', 'info'.
* @param {string} text Message text.
*/
function showMessage(type, text) {
var classMap = {
'success': 'notice-success',
'error': 'notice-error',
'warning': 'notice-warning',
'info': 'notice-info'
};
var noticeClass = classMap[type] || 'notice-info';
$message
.removeClass('notice-success notice-error notice-warning notice-info')
.addClass('notice ' + noticeClass)
.html('<p>' + escapeHtml(text) + '</p>')
.show();
}
/**
* Escape HTML entities.
*
* @param {string} text Text to escape.
* @return {string} Escaped text.
*/
function escapeHtml(text) {
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
})(jQuery);