Add inline editing for licenses and copy license key button

- Add inline editing for status, expiry date, and domain fields
- Add copy-to-clipboard button for license keys
- Add AJAX handlers for inline editing with nonce verification
- Update LicenseManager with updateLicenseExpiry method
- Add new translations for inline editing strings (de_CH)
- Compile updated German translations to .mo file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 23:13:07 +01:00
parent e88423e882
commit 024733bb31
8 changed files with 872 additions and 29 deletions

View File

@@ -579,3 +579,144 @@
#license-search-input {
width: 280px;
}
/* Inline Editing Styles */
.wclp-editable-cell {
position: relative;
}
.wclp-editable-cell .wclp-display-value {
display: inline;
}
.wclp-editable-cell .wclp-edit-btn {
opacity: 0;
transition: opacity 0.15s ease;
color: #2271b1;
padding: 0;
margin-left: 5px;
vertical-align: middle;
}
.wclp-editable-cell:hover .wclp-edit-btn {
opacity: 1;
}
.wclp-editable-cell .wclp-edit-btn .dashicons {
font-size: 14px;
width: 14px;
height: 14px;
vertical-align: middle;
}
.wclp-editable-cell .wclp-edit-btn:hover {
color: #135e96;
}
.wclp-edit-form {
display: flex;
align-items: center;
gap: 5px;
flex-wrap: wrap;
}
.wclp-edit-form .wclp-edit-input {
max-width: 150px;
height: 28px;
padding: 0 8px;
}
.wclp-edit-form .wclp-edit-input[type="date"] {
max-width: 130px;
}
.wclp-edit-form select.wclp-edit-input {
max-width: 120px;
}
.wclp-edit-form .button-small {
height: 26px;
line-height: 24px;
padding: 0 8px;
font-size: 11px;
}
.wclp-edit-form .wclp-lifetime-btn {
font-size: 16px;
padding: 0 6px;
}
/* Inline notice animation */
.wclp-inline-notice {
margin: 10px 0;
}
/* Make editable cells have a minimum height for consistency */
.licenses-table .wclp-editable-cell {
min-width: 120px;
}
/* Domain column wider for edit form */
.licenses-table td[data-field="domain"] {
min-width: 180px;
}
/* Status column */
.licenses-table td[data-field="status"] {
min-width: 150px;
}
/* Expiry column */
.licenses-table td[data-field="expiry"] {
min-width: 200px;
}
/* Copy License Key Button */
.wclp-copy-btn {
color: #2271b1;
padding: 0;
margin-left: 5px;
vertical-align: middle;
position: relative;
cursor: pointer;
}
.wclp-copy-btn:hover {
color: #135e96;
}
.wclp-copy-btn .dashicons {
font-size: 16px;
width: 16px;
height: 16px;
vertical-align: middle;
}
.wclp-copy-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #1d2327;
color: #fff;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
white-space: nowrap;
z-index: 100;
margin-bottom: 5px;
}
.wclp-copy-tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-top-color: #1d2327;
}
.wclp-license-key {
user-select: all;
}

View File

@@ -234,4 +234,266 @@
// Initialize when document is ready
$(document).ready(init);
// ========================================
// Inline Editing Functionality
// ========================================
/**
* Initialize inline editing
*/
function initInlineEditing() {
// Edit button click
$(document).on('click', '.wclp-edit-btn', function(e) {
e.preventDefault();
var $cell = $(this).closest('.wclp-editable-cell');
showEditForm($cell);
});
// Cancel button click
$(document).on('click', '.wclp-cancel-btn', function(e) {
e.preventDefault();
var $cell = $(this).closest('.wclp-editable-cell');
hideEditForm($cell);
});
// Save button click
$(document).on('click', '.wclp-save-btn', function(e) {
e.preventDefault();
var $cell = $(this).closest('.wclp-editable-cell');
saveEdit($cell);
});
// Lifetime button click (for expiry field)
$(document).on('click', '.wclp-lifetime-btn', function(e) {
e.preventDefault();
var $cell = $(this).closest('.wclp-editable-cell');
$cell.find('.wclp-edit-input').val('');
saveEdit($cell);
});
// Enter key saves, Escape cancels
$(document).on('keydown', '.wclp-edit-input', function(e) {
var $cell = $(this).closest('.wclp-editable-cell');
if (e.keyCode === 13) { // Enter
e.preventDefault();
saveEdit($cell);
} else if (e.keyCode === 27) { // Escape
e.preventDefault();
hideEditForm($cell);
}
});
}
/**
* Show edit form for a cell
*/
function showEditForm($cell) {
$cell.find('.wclp-display-value, .wclp-edit-btn').hide();
$cell.find('.wclp-edit-form').show();
$cell.find('.wclp-edit-input').focus().select();
}
/**
* Hide edit form for a cell
*/
function hideEditForm($cell) {
$cell.find('.wclp-edit-form').hide();
$cell.find('.wclp-display-value, .wclp-edit-btn').show();
}
/**
* Save edit via AJAX
*/
function saveEdit($cell) {
var field = $cell.data('field');
var licenseId = $cell.data('license-id');
var $input = $cell.find('.wclp-edit-input');
var value = $input.val();
var $saveBtn = $cell.find('.wclp-save-btn');
var originalText = $saveBtn.text();
// Determine action based on field
var action;
var data = {
action: '',
nonce: wclpAdmin.editNonce,
license_id: licenseId
};
switch (field) {
case 'status':
data.action = 'wclp_update_license_status';
data.status = value;
break;
case 'expiry':
data.action = 'wclp_update_license_expiry';
data.expiry_date = value;
break;
case 'domain':
data.action = 'wclp_update_license_domain';
data.domain = value;
break;
default:
return;
}
// Show saving state
$saveBtn.text(wclpAdmin.strings.saving).prop('disabled', true);
$cell.find('.wclp-cancel-btn, .wclp-lifetime-btn').prop('disabled', true);
$.ajax({
url: wclpAdmin.ajaxUrl,
type: 'POST',
data: data,
success: function(response) {
if (response.success) {
// Update display value
updateDisplayValue($cell, field, response.data);
hideEditForm($cell);
showNotice('success', response.data.message);
} else {
showNotice('error', response.data.message || wclpAdmin.strings.saveFailed);
}
},
error: function() {
showNotice('error', wclpAdmin.strings.saveFailed);
},
complete: function() {
$saveBtn.text(originalText).prop('disabled', false);
$cell.find('.wclp-cancel-btn, .wclp-lifetime-btn').prop('disabled', false);
}
});
}
/**
* Update the display value after successful save
*/
function updateDisplayValue($cell, field, data) {
var $display = $cell.find('.wclp-display-value');
switch (field) {
case 'status':
var statusHtml = '<span class="license-status license-status-' + escapeHtml(data.status) + '">' +
escapeHtml(data.status_label) + '</span>';
$display.html(statusHtml);
break;
case 'expiry':
if (data.expiry_date) {
$display.html(escapeHtml(data.expiry_display));
} else {
$display.html('<span class="license-lifetime">' + wclpAdmin.strings.lifetime + '</span>');
}
// Update the input value
$cell.find('.wclp-edit-input').val(data.expiry_date || '');
break;
case 'domain':
$display.text(data.domain);
// Update the input value
$cell.find('.wclp-edit-input').val(data.domain);
break;
}
}
/**
* Show a temporary notice
*/
function showNotice(type, message) {
var $notice = $('<div class="notice notice-' + type + ' is-dismissible wclp-inline-notice"><p>' +
escapeHtml(message) + '</p></div>');
// Insert at the top of the wrap
$('.wrap h1').first().after($notice);
// Auto-dismiss after 3 seconds
setTimeout(function() {
$notice.fadeOut(300, function() {
$(this).remove();
});
}, 3000);
}
// Initialize inline editing when document is ready
$(document).ready(initInlineEditing);
// ========================================
// Copy License Key Functionality
// ========================================
/**
* Initialize copy license key buttons
*/
function initCopyButtons() {
$(document).on('click', '.wclp-copy-btn', function(e) {
e.preventDefault();
var $btn = $(this);
var licenseKey = $btn.data('license-key');
copyToClipboard(licenseKey).then(function() {
showCopyFeedback($btn, true);
}).catch(function() {
showCopyFeedback($btn, false);
});
});
}
/**
* Copy text to clipboard
*/
function copyToClipboard(text) {
// Modern Clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
// Fallback for older browsers
return new Promise(function(resolve, reject) {
var textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
try {
var successful = document.execCommand('copy');
document.body.removeChild(textarea);
if (successful) {
resolve();
} else {
reject();
}
} catch (err) {
document.body.removeChild(textarea);
reject(err);
}
});
}
/**
* Show copy feedback
*/
function showCopyFeedback($btn, success) {
var $icon = $btn.find('.dashicons');
var originalClass = 'dashicons-clipboard';
var feedbackClass = success ? 'dashicons-yes' : 'dashicons-no';
var feedbackColor = success ? '#00a32a' : '#d63638';
// Change icon temporarily
$icon.removeClass(originalClass).addClass(feedbackClass).css('color', feedbackColor);
// Show tooltip
var message = success ? wclpAdmin.strings.copied : wclpAdmin.strings.copyFailed;
var $tooltip = $('<span class="wclp-copy-tooltip">' + escapeHtml(message) + '</span>');
$btn.append($tooltip);
// Reset after delay
setTimeout(function() {
$icon.removeClass(feedbackClass).addClass(originalClass).css('color', '');
$tooltip.remove();
}, 1500);
}
// Initialize copy buttons when document is ready
$(document).ready(initCopyButtons);
})(jQuery);