mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-01-29 18:59:04 +01:00
Add better plugin description + new example plugin
Add better plugin description + new example plugin
This commit is contained in:
336
testPlugin/static/testPlugin/css/testPlugin.css
Normal file
336
testPlugin/static/testPlugin/css/testPlugin.css
Normal file
@@ -0,0 +1,336 @@
|
||||
/* Test Plugin CSS - Additional styles for better integration */
|
||||
|
||||
/* Popup Message Animations */
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced Button Styles */
|
||||
.btn-test {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-test::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
}
|
||||
|
||||
.btn-test:active::before {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* Toggle Switch Enhanced */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
transition: .4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #5856d6;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Loading States */
|
||||
.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -10px 0 0 -10px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Card Hover Effects */
|
||||
.plugin-card {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.plugin-card:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 12px 32px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
/* Status Indicators */
|
||||
.status-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.status-indicator.enabled {
|
||||
background: #e8f5e8;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
.status-indicator.disabled {
|
||||
background: #ffebee;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
/* Responsive Enhancements */
|
||||
@media (max-width: 768px) {
|
||||
.control-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.logs-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.logs-table th,
|
||||
.logs-table td {
|
||||
padding: 8px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.test-plugin-wrapper {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.plugin-header,
|
||||
.control-panel,
|
||||
.settings-form,
|
||||
.logs-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.plugin-header h1 {
|
||||
font-size: 24px;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-test,
|
||||
.btn-secondary {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Mode Support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg-primary: #1a1a1a;
|
||||
--bg-secondary: #2d2d2d;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #b3b3b3;
|
||||
--text-tertiary: #808080;
|
||||
--border-primary: #404040;
|
||||
--shadow-md: 0 2px 8px rgba(0,0,0,0.3);
|
||||
--shadow-lg: 0 8px 24px rgba(0,0,0,0.4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.test-plugin-wrapper {
|
||||
background: white !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.btn-test,
|
||||
.btn-secondary,
|
||||
.toggle-switch {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.popup-message {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional styles for inline elements */
|
||||
.popup-message {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px 20px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
||||
border-left: 4px solid #10b981;
|
||||
z-index: 9999;
|
||||
max-width: 400px;
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.popup-message.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.popup-message.error {
|
||||
border-left-color: #ef4444;
|
||||
}
|
||||
|
||||
.popup-message.warning {
|
||||
border-left-color: #f59e0b;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary, #2f3640);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary, #64748b);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.popup-time {
|
||||
font-size: 12px;
|
||||
color: var(--text-tertiary, #9ca3af);
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
color: var(--text-tertiary, #9ca3af);
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px 20px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
||||
border-left: 4px solid #10b981;
|
||||
z-index: 9999;
|
||||
max-width: 400px;
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.notification.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.notification.error {
|
||||
border-left-color: #ef4444;
|
||||
}
|
||||
|
||||
.notification-title {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary, #2f3640);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.notification-content {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary, #64748b);
|
||||
}
|
||||
|
||||
.popup-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
323
testPlugin/static/testPlugin/js/testPlugin.js
Normal file
323
testPlugin/static/testPlugin/js/testPlugin.js
Normal file
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* Test Plugin JavaScript
|
||||
* Handles all client-side functionality for the test plugin
|
||||
*/
|
||||
|
||||
class TestPlugin {
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.initializeComponents();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
// Toggle switch functionality
|
||||
const toggleSwitch = document.getElementById('plugin-toggle');
|
||||
if (toggleSwitch) {
|
||||
toggleSwitch.addEventListener('change', (e) => this.handleToggle(e));
|
||||
}
|
||||
|
||||
// Test button functionality
|
||||
const testButton = document.getElementById('test-button');
|
||||
if (testButton) {
|
||||
testButton.addEventListener('click', (e) => this.handleTestClick(e));
|
||||
}
|
||||
|
||||
// Settings form
|
||||
const settingsForm = document.getElementById('settings-form');
|
||||
if (settingsForm) {
|
||||
settingsForm.addEventListener('submit', (e) => this.handleSettingsSubmit(e));
|
||||
}
|
||||
|
||||
// Log filter
|
||||
const actionFilter = document.getElementById('action-filter');
|
||||
if (actionFilter) {
|
||||
actionFilter.addEventListener('change', (e) => this.handleLogFilter(e));
|
||||
}
|
||||
}
|
||||
|
||||
initializeComponents() {
|
||||
// Initialize any components that need setup
|
||||
this.initializeTooltips();
|
||||
this.initializeAnimations();
|
||||
}
|
||||
|
||||
async handleToggle(event) {
|
||||
const toggleSwitch = event.target;
|
||||
const testButton = document.getElementById('test-button');
|
||||
|
||||
try {
|
||||
const response = await fetch('/testPlugin/toggle/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': this.getCSRFToken()
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status === 1) {
|
||||
if (testButton) {
|
||||
testButton.disabled = !data.enabled;
|
||||
}
|
||||
this.showNotification('success', 'Plugin Toggle', data.message);
|
||||
|
||||
// Update status indicator if exists
|
||||
this.updateStatusIndicator(data.enabled);
|
||||
|
||||
// Reload page after a short delay to update UI
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
this.showNotification('error', 'Error', data.error_message);
|
||||
// Revert toggle state
|
||||
toggleSwitch.checked = !toggleSwitch.checked;
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('error', 'Error', 'Failed to toggle plugin');
|
||||
// Revert toggle state
|
||||
toggleSwitch.checked = !toggleSwitch.checked;
|
||||
}
|
||||
}
|
||||
|
||||
async handleTestClick(event) {
|
||||
const testButton = event.target;
|
||||
|
||||
if (testButton.disabled) return;
|
||||
|
||||
// Add loading state
|
||||
testButton.classList.add('loading');
|
||||
testButton.disabled = true;
|
||||
const originalContent = testButton.innerHTML;
|
||||
testButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Testing...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/testPlugin/test/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': this.getCSRFToken()
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status === 1) {
|
||||
// Update test count
|
||||
const testCountElement = document.getElementById('test-count');
|
||||
if (testCountElement) {
|
||||
testCountElement.textContent = data.test_count;
|
||||
}
|
||||
|
||||
// Show popup message
|
||||
this.showPopup(
|
||||
data.popup_message.type,
|
||||
data.popup_message.title,
|
||||
data.popup_message.message
|
||||
);
|
||||
|
||||
// Add success animation
|
||||
testButton.style.background = 'linear-gradient(135deg, #10b981, #059669)';
|
||||
setTimeout(() => {
|
||||
testButton.style.background = '';
|
||||
}, 2000);
|
||||
} else {
|
||||
this.showNotification('error', 'Error', data.error_message);
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('error', 'Error', 'Failed to execute test');
|
||||
} finally {
|
||||
// Remove loading state
|
||||
testButton.classList.remove('loading');
|
||||
testButton.disabled = false;
|
||||
testButton.innerHTML = originalContent;
|
||||
}
|
||||
}
|
||||
|
||||
async handleSettingsSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
const data = {
|
||||
custom_message: formData.get('custom_message')
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/testPlugin/update-settings/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': this.getCSRFToken()
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.status === 1) {
|
||||
this.showNotification('success', 'Settings Updated', result.message);
|
||||
} else {
|
||||
this.showNotification('error', 'Error', result.error_message);
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('error', 'Error', 'Failed to update settings');
|
||||
}
|
||||
}
|
||||
|
||||
handleLogFilter(event) {
|
||||
const selectedAction = event.target.value;
|
||||
const logRows = document.querySelectorAll('.log-row');
|
||||
|
||||
logRows.forEach(row => {
|
||||
if (selectedAction === '' || row.dataset.action === selectedAction) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showPopup(type, title, message) {
|
||||
const popupContainer = document.getElementById('popup-container') || this.createPopupContainer();
|
||||
const popup = document.createElement('div');
|
||||
popup.className = `popup-message ${type}`;
|
||||
|
||||
popup.innerHTML = `
|
||||
<button class="popup-close" onclick="this.parentElement.remove()">×</button>
|
||||
<div class="popup-title">${title}</div>
|
||||
<div class="popup-content">${message}</div>
|
||||
<div class="popup-time">${new Date().toLocaleTimeString()}</div>
|
||||
`;
|
||||
|
||||
popupContainer.appendChild(popup);
|
||||
|
||||
// Show popup with animation
|
||||
setTimeout(() => popup.classList.add('show'), 100);
|
||||
|
||||
// Auto remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
popup.classList.remove('show');
|
||||
setTimeout(() => popup.remove(), 300);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
showNotification(type, title, message) {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `notification ${type}`;
|
||||
|
||||
notification.innerHTML = `
|
||||
<div class="notification-title">${title}</div>
|
||||
<div class="notification-content">${message}</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Show notification
|
||||
setTimeout(() => notification.classList.add('show'), 100);
|
||||
|
||||
// Auto remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('show');
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
createPopupContainer() {
|
||||
const container = document.createElement('div');
|
||||
container.id = 'popup-container';
|
||||
container.className = 'popup-container';
|
||||
document.body.appendChild(container);
|
||||
return container;
|
||||
}
|
||||
|
||||
updateStatusIndicator(enabled) {
|
||||
const statusElements = document.querySelectorAll('.status-indicator');
|
||||
statusElements.forEach(element => {
|
||||
element.className = `status-indicator ${enabled ? 'enabled' : 'disabled'}`;
|
||||
element.innerHTML = enabled ?
|
||||
'<i class="fas fa-check-circle"></i> Enabled' :
|
||||
'<i class="fas fa-times-circle"></i> Disabled';
|
||||
});
|
||||
}
|
||||
|
||||
initializeTooltips() {
|
||||
// Add tooltips to buttons and controls
|
||||
const elements = document.querySelectorAll('[data-tooltip]');
|
||||
elements.forEach(element => {
|
||||
element.addEventListener('mouseenter', (e) => this.showTooltip(e));
|
||||
element.addEventListener('mouseleave', (e) => this.hideTooltip(e));
|
||||
});
|
||||
}
|
||||
|
||||
showTooltip(event) {
|
||||
const element = event.target;
|
||||
const tooltipText = element.dataset.tooltip;
|
||||
|
||||
if (!tooltipText) return;
|
||||
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'tooltip';
|
||||
tooltip.textContent = tooltipText;
|
||||
tooltip.style.cssText = `
|
||||
position: absolute;
|
||||
background: #333;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
z-index: 10000;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
tooltip.style.left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) + 'px';
|
||||
tooltip.style.top = rect.top - tooltip.offsetHeight - 8 + 'px';
|
||||
|
||||
element._tooltip = tooltip;
|
||||
}
|
||||
|
||||
hideTooltip(event) {
|
||||
const element = event.target;
|
||||
if (element._tooltip) {
|
||||
element._tooltip.remove();
|
||||
delete element._tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
initializeAnimations() {
|
||||
// Add entrance animations to cards
|
||||
const cards = document.querySelectorAll('.plugin-card, .stat-card, .log-item');
|
||||
cards.forEach((card, index) => {
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'translateY(20px)';
|
||||
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
||||
|
||||
setTimeout(() => {
|
||||
card.style.opacity = '1';
|
||||
card.style.transform = 'translateY(0)';
|
||||
}, index * 100);
|
||||
});
|
||||
}
|
||||
|
||||
getCSRFToken() {
|
||||
const token = document.querySelector('[name=csrfmiddlewaretoken]');
|
||||
return token ? token.value : '';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the plugin when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
new TestPlugin();
|
||||
});
|
||||
|
||||
// Export for potential external use
|
||||
window.TestPlugin = TestPlugin;
|
||||
Reference in New Issue
Block a user