django-cfg 1.4.75__py3-none-any.whl → 1.4.76__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/agents/__init__.py +1 -1
- django_cfg/apps/agents/integration/registry.py +1 -1
- django_cfg/apps/agents/patterns/content_agents.py +1 -1
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +1 -1
- django_cfg/apps/centrifugo/views/dashboard.py +13 -0
- django_cfg/apps/centrifugo/views/testing_api.py +74 -15
- django_cfg/apps/tasks/views/dashboard.py +4 -4
- django_cfg/core/generation/integration_generators/api.py +5 -2
- django_cfg/management/commands/check_endpoints.py +1 -1
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +2 -2
- django_cfg/modules/django_unfold/callbacks/main.py +27 -25
- django_cfg/pyproject.toml +1 -1
- django_cfg/static/admin/css/constance.css +44 -0
- django_cfg/static/admin/css/dashboard.css +6 -170
- django_cfg/static/admin/css/layout.css +21 -0
- django_cfg/static/admin/css/tabs.css +95 -0
- django_cfg/static/admin/css/theme.css +74 -0
- django_cfg/static/admin/js/alpine/activity-tracker.js +55 -0
- django_cfg/static/admin/js/alpine/chart.js +101 -0
- django_cfg/static/admin/js/alpine/command-modal.js +159 -0
- django_cfg/static/admin/js/alpine/commands-panel.js +139 -0
- django_cfg/static/admin/js/alpine/commands-section.js +260 -0
- django_cfg/static/admin/js/alpine/dashboard-tabs.js +46 -0
- django_cfg/static/admin/js/alpine/system-metrics.js +20 -0
- django_cfg/static/admin/js/alpine/toggle-section.js +28 -0
- django_cfg/static/admin/js/utils.js +60 -0
- django_cfg/templates/admin/components/modal.html +1 -1
- django_cfg/templates/admin/constance/change_list.html +3 -42
- django_cfg/templates/admin/index.html +0 -8
- django_cfg/templates/admin/layouts/base_dashboard.html +4 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +104 -502
- django_cfg/templates/admin/sections/commands_section.html +374 -451
- django_cfg/templates/admin/sections/documentation_section.html +13 -33
- django_cfg/templates/admin/snippets/components/activity_tracker.html +27 -49
- django_cfg/templates/admin/snippets/components/charts_section.html +8 -74
- django_cfg/templates/admin/snippets/components/django_commands.html +94 -181
- django_cfg/templates/admin/snippets/components/system_metrics.html +18 -10
- django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +2 -2
- django_cfg/templates/admin/snippets/tabs/commands_tab.html +48 -0
- django_cfg/templates/admin/snippets/tabs/documentation_tab.html +1 -190
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +1 -1
- {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/METADATA +1 -1
- {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/RECORD +47 -39
- django_cfg/static/admin/js/commands.js +0 -171
- django_cfg/static/admin/js/dashboard.js +0 -126
- django_cfg/templates/admin/components/management_commands.js +0 -375
- django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +0 -322
- django_cfg/templates/admin/snippets/components/recent_activity.html +0 -35
- {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,425 +6,29 @@
|
|
|
6
6
|
{{ block.super }}
|
|
7
7
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
8
8
|
<!-- Removed Tailwind CDN - using CSS-only solution -->
|
|
9
|
-
|
|
10
|
-
<script>
|
|
11
|
-
// Dashboard tabs functionality
|
|
12
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
13
|
-
const tabs = document.querySelectorAll('#dashboard-tabs button');
|
|
14
|
-
const contents = document.querySelectorAll('.tab-content');
|
|
15
|
-
|
|
16
|
-
if (!tabs.length || !contents.length) return;
|
|
17
|
-
|
|
18
|
-
// Tab names for URL hash
|
|
19
|
-
const tabNames = ['overview', 'zones', 'users', 'system', 'stats', 'app-stats', 'commands', 'documentation', 'widgets'];
|
|
20
|
-
|
|
21
|
-
// Global function for tab switching
|
|
22
|
-
window.switchTab = function(idx, updateHash = true) {
|
|
23
|
-
tabs.forEach((t, i) => {
|
|
24
|
-
if (i === idx) {
|
|
25
|
-
// Active tab
|
|
26
|
-
t.classList.add('active');
|
|
27
|
-
} else {
|
|
28
|
-
// Inactive tab
|
|
29
|
-
t.classList.remove('active');
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
contents.forEach((content, i) => {
|
|
34
|
-
content.style.display = i === idx ? 'block' : 'none';
|
|
35
|
-
content.classList.toggle('active', i === idx);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Update URL hash
|
|
39
|
-
if (updateHash && tabNames[idx]) {
|
|
40
|
-
history.replaceState(null, null, '#' + tabNames[idx]);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Function to get tab index from hash
|
|
45
|
-
function getTabFromHash() {
|
|
46
|
-
const hash = window.location.hash.substring(1); // Remove #
|
|
47
|
-
const tabIndex = tabNames.indexOf(hash);
|
|
48
|
-
return tabIndex >= 0 ? tabIndex : 0; // Default to first tab
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Add click handlers
|
|
52
|
-
tabs.forEach((tab, idx) => {
|
|
53
|
-
tab.onclick = function(e) {
|
|
54
|
-
e.preventDefault();
|
|
55
|
-
switchTab(idx, true);
|
|
56
|
-
};
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Handle browser back/forward buttons
|
|
60
|
-
window.addEventListener('hashchange', function() {
|
|
61
|
-
const tabIndex = getTabFromHash();
|
|
62
|
-
switchTab(tabIndex, false); // Don't update hash to avoid loop
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Activate tab based on URL hash or default to first tab
|
|
66
|
-
const initialTab = getTabFromHash();
|
|
67
|
-
switchTab(initialTab, false);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Global functions for components
|
|
71
|
-
window.toggleCategory = function(category) {
|
|
72
|
-
const content = document.getElementById(`content-${category}`);
|
|
73
|
-
const icon = document.getElementById(`icon-${category}`);
|
|
74
|
-
|
|
75
|
-
if (!content || !icon) return;
|
|
76
|
-
|
|
77
|
-
if (content.style.display === 'none' || content.style.display === '') {
|
|
78
|
-
content.style.display = 'block';
|
|
79
|
-
icon.style.transform = 'rotate(0deg)';
|
|
80
|
-
icon.textContent = 'expand_more';
|
|
81
|
-
} else {
|
|
82
|
-
content.style.display = 'none';
|
|
83
|
-
icon.style.transform = 'rotate(-90deg)';
|
|
84
|
-
icon.textContent = 'expand_less';
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
9
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const originalText = button.innerHTML;
|
|
93
|
-
button.innerHTML = '<span class="material-icons text-xs mr-1">check</span>Copied';
|
|
94
|
-
button.classList.remove('bg-gray-100', 'dark:bg-gray-700', 'hover:bg-gray-200', 'dark:hover:bg-gray-600');
|
|
95
|
-
button.classList.add('bg-green-600', 'hover:bg-green-700', 'dark:bg-green-500', 'dark:hover:bg-green-600', 'text-white');
|
|
96
|
-
|
|
97
|
-
setTimeout(function() {
|
|
98
|
-
button.innerHTML = originalText;
|
|
99
|
-
button.classList.remove('bg-green-600', 'hover:bg-green-700', 'dark:bg-green-500', 'dark:hover:bg-green-600', 'text-white');
|
|
100
|
-
button.classList.add('bg-gray-100', 'dark:bg-gray-700', 'hover:bg-gray-200', 'dark:hover:bg-gray-600');
|
|
101
|
-
}, 2000);
|
|
102
|
-
}).catch(function(err) {
|
|
103
|
-
console.error('Could not copy text: ', err);
|
|
104
|
-
});
|
|
105
|
-
};
|
|
10
|
+
<!-- Alpine.js Components - Load before Alpine.js -->
|
|
11
|
+
{% load static %}
|
|
12
|
+
<script src="{% static 'admin/js/utils.js' %}"></script>
|
|
13
|
+
<script src="{% static 'admin/js/alpine/dashboard-tabs.js' %}"></script>
|
|
106
14
|
|
|
107
|
-
|
|
15
|
+
<!-- Alpine.js Collapse Plugin (must load before Alpine.js) -->
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
|
|
108
17
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const commandNameEl = document.getElementById('commandName');
|
|
112
|
-
const commandOutput = document.getElementById('commandOutput');
|
|
113
|
-
const commandStatus = document.getElementById('commandStatus');
|
|
114
|
-
|
|
115
|
-
if (!modal || !commandNameEl || !commandOutput || !commandStatus) {
|
|
116
|
-
console.error('Command modal elements not found');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
18
|
+
<!-- Alpine.js - Load last with defer -->
|
|
19
|
+
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
119
20
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fetch('/cfg/commands/execute/', {
|
|
126
|
-
method: 'POST',
|
|
127
|
-
headers: {
|
|
128
|
-
'Content-Type': 'application/json',
|
|
129
|
-
'X-CSRFToken': getCookie('csrftoken')
|
|
130
|
-
},
|
|
131
|
-
body: JSON.stringify({
|
|
132
|
-
command: commandName,
|
|
133
|
-
args: [],
|
|
134
|
-
options: {}
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
.then(response => {
|
|
138
|
-
if (!response.ok) {
|
|
139
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const reader = response.body.getReader();
|
|
143
|
-
const decoder = new TextDecoder();
|
|
144
|
-
|
|
145
|
-
function readStream() {
|
|
146
|
-
return reader.read().then(({done, value}) => {
|
|
147
|
-
if (done) return;
|
|
148
|
-
|
|
149
|
-
const chunk = decoder.decode(value);
|
|
150
|
-
const lines = chunk.split('\n');
|
|
151
|
-
|
|
152
|
-
lines.forEach(line => {
|
|
153
|
-
if (line.startsWith('data: ')) {
|
|
154
|
-
try {
|
|
155
|
-
const data = JSON.parse(line.slice(6));
|
|
156
|
-
handleCommandData(data);
|
|
157
|
-
} catch (e) {
|
|
158
|
-
console.error('Error parsing command data:', e);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
return readStream();
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return readStream();
|
|
168
|
-
})
|
|
169
|
-
.catch(error => {
|
|
170
|
-
console.error('Error executing command:', error);
|
|
171
|
-
commandOutput.textContent += `\n❌ Error: ${error.message}`;
|
|
172
|
-
commandStatus.innerHTML = '<div class="w-3 h-3 bg-red-500 rounded-full mr-2"></div><span class="text-sm font-medium text-red-600 dark:text-red-400">Error</span>';
|
|
173
|
-
});
|
|
174
|
-
};
|
|
21
|
+
<!-- Admin Styles -->
|
|
22
|
+
<link rel="stylesheet" href="{% static 'admin/css/layout.css' %}">
|
|
23
|
+
<link rel="stylesheet" href="{% static 'admin/css/theme.css' %}">
|
|
24
|
+
<link rel="stylesheet" href="{% static 'admin/css/tabs.css' %}">
|
|
175
25
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
switch (data.type) {
|
|
183
|
-
case 'start':
|
|
184
|
-
// Clear previous content and add header
|
|
185
|
-
output.innerHTML = '';
|
|
186
|
-
addLogLine(output, `🚀 Starting command: ${data.command}`, 'info');
|
|
187
|
-
addLogLine(output, `📝 Arguments: ${data.args.join(' ')}`, 'info');
|
|
188
|
-
addLogLine(output, '', 'spacer');
|
|
189
|
-
status.innerHTML = '<div class="w-3 h-3 bg-yellow-500 rounded-full mr-2 animate-pulse"></div><span class="text-sm font-medium text-gray-700 dark:text-gray-300">Executing...</span>';
|
|
190
|
-
break;
|
|
191
|
-
case 'output':
|
|
192
|
-
addLogLine(output, data.line, 'output');
|
|
193
|
-
scrollToBottom(output);
|
|
194
|
-
break;
|
|
195
|
-
case 'complete':
|
|
196
|
-
const success = data.return_code === 0;
|
|
197
|
-
status.innerHTML = success
|
|
198
|
-
? '<div class="w-3 h-3 bg-green-500 rounded-full mr-2"></div><span class="text-sm font-medium text-green-600 dark:text-green-400">Completed</span>'
|
|
199
|
-
: '<div class="w-3 h-3 bg-red-500 rounded-full mr-2"></div><span class="text-sm font-medium text-red-600 dark:text-red-400">Failed</span>';
|
|
200
|
-
|
|
201
|
-
addLogLine(output, '', 'spacer');
|
|
202
|
-
let completionMessage = `${success ? '✅' : '❌'} Command completed with exit code: ${data.return_code}`;
|
|
203
|
-
if (data.execution_time) {
|
|
204
|
-
completionMessage += ` (${data.execution_time}s)`;
|
|
205
|
-
}
|
|
206
|
-
addLogLine(output, completionMessage, success ? 'success' : 'error');
|
|
207
|
-
scrollToBottom(output);
|
|
208
|
-
break;
|
|
209
|
-
case 'error':
|
|
210
|
-
addLogLine(output, `❌ Error: ${data.error}`, 'error');
|
|
211
|
-
status.innerHTML = '<div class="w-3 h-3 bg-red-500 rounded-full mr-2"></div><span class="text-sm font-medium text-red-600 dark:text-red-400">Error</span>';
|
|
212
|
-
scrollToBottom(output);
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function addLogLine(container, text, type = 'output') {
|
|
218
|
-
const line = document.createElement('div');
|
|
219
|
-
line.className = 'log-line';
|
|
220
|
-
|
|
221
|
-
// Add type-specific styling
|
|
222
|
-
switch (type) {
|
|
223
|
-
case 'info':
|
|
224
|
-
line.className += ' text-blue-600 dark:text-blue-400 font-medium';
|
|
225
|
-
break;
|
|
226
|
-
case 'success':
|
|
227
|
-
line.className += ' text-green-600 dark:text-green-400 font-medium';
|
|
228
|
-
break;
|
|
229
|
-
case 'error':
|
|
230
|
-
line.className += ' text-red-600 dark:text-red-400 font-medium';
|
|
231
|
-
break;
|
|
232
|
-
case 'spacer':
|
|
233
|
-
line.className += ' h-2';
|
|
234
|
-
break;
|
|
235
|
-
default:
|
|
236
|
-
line.className += ' text-gray-700 dark:text-gray-300';
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Set content
|
|
240
|
-
if (type === 'spacer') {
|
|
241
|
-
line.innerHTML = ' ';
|
|
242
|
-
} else {
|
|
243
|
-
line.textContent = text;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
container.appendChild(line);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function scrollToBottom(element) {
|
|
250
|
-
if (!element) return;
|
|
251
|
-
|
|
252
|
-
// Принудительно скроллим элемент
|
|
253
|
-
setTimeout(() => {
|
|
254
|
-
element.scrollTop = element.scrollHeight;
|
|
255
|
-
console.log('Scrolled to bottom:', element.scrollTop, element.scrollHeight);
|
|
256
|
-
}, 50);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
window.closeCommandModal = function() {
|
|
260
|
-
const modal = document.getElementById('commandModal');
|
|
261
|
-
if (modal) {
|
|
262
|
-
modal.classList.add('hidden');
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
function getCookie(name) {
|
|
267
|
-
let cookieValue = null;
|
|
268
|
-
if (document.cookie && document.cookie !== '') {
|
|
269
|
-
const cookies = document.cookie.split(';');
|
|
270
|
-
for (let i = 0; i < cookies.length; i++) {
|
|
271
|
-
const cookie = cookies[i].trim();
|
|
272
|
-
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
273
|
-
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return cookieValue;
|
|
279
|
-
}
|
|
280
|
-
</script>
|
|
281
|
-
|
|
282
|
-
<style>
|
|
283
|
-
/* Dashboard tab styles */
|
|
284
|
-
.tab-content {
|
|
285
|
-
display: none;
|
|
286
|
-
animation: fadeIn 0.3s ease-in-out;
|
|
287
|
-
}
|
|
288
|
-
.tab-content.active {
|
|
289
|
-
display: block;
|
|
290
|
-
}
|
|
291
|
-
#dashboard-tabs button {
|
|
292
|
-
cursor: pointer;
|
|
293
|
-
transition: all 0.2s ease-in-out;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
@keyframes fadeIn {
|
|
297
|
-
from { opacity: 0; transform: translateY(10px); }
|
|
298
|
-
to { opacity: 1; transform: translateY(0); }
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/* 🎨 Theme classes - cross-theme compatible cards */
|
|
302
|
-
.theme-card {
|
|
303
|
-
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
304
|
-
backdrop-filter: blur(10px) !important;
|
|
305
|
-
color: #111827 !important;
|
|
306
|
-
border-color: rgba(229, 231, 235, 0.6) !important;
|
|
307
|
-
border-radius: 10px !important;
|
|
308
|
-
transition: all 0.2s ease;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
html.dark .theme-card {
|
|
312
|
-
background-color: rgba(31, 41, 55, 0.2) !important;
|
|
313
|
-
backdrop-filter: blur(10px) !important;
|
|
314
|
-
color: white !important;
|
|
315
|
-
border-color: rgba(55, 65, 81, 0.6) !important;
|
|
316
|
-
border-radius: 10px !important;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.theme-text {
|
|
320
|
-
color: #111827 !important;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
html.dark .theme-text {
|
|
324
|
-
color: white !important;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
.theme-border {
|
|
328
|
-
border-color: rgba(229, 231, 235, 0.6) !important;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
html.dark .theme-border {
|
|
332
|
-
border-color: rgba(55, 65, 81, 0.6) !important;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/* Fix grid layout */
|
|
336
|
-
.overview-grid {
|
|
337
|
-
display: grid !important;
|
|
338
|
-
grid-template-columns: 1fr !important;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
@media (min-width: 1280px) {
|
|
342
|
-
.overview-grid {
|
|
343
|
-
grid-template-columns: 2fr 1fr !important;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/* 🎯 Tab styling */
|
|
348
|
-
#dashboard-tabs {
|
|
349
|
-
border-bottom: 2px solid #e5e7eb !important;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
html.dark #dashboard-tabs {
|
|
353
|
-
border-bottom-color: #374151 !important;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
#dashboard-tabs button {
|
|
357
|
-
background-color: #f3f4f6 !important;
|
|
358
|
-
color: #6b7280 !important;
|
|
359
|
-
border-bottom: 2px solid transparent !important;
|
|
360
|
-
transition: all 0.2s ease !important;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
html.dark #dashboard-tabs button {
|
|
364
|
-
background-color: #374151 !important;
|
|
365
|
-
color: #9ca3af !important;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
#dashboard-tabs button:hover {
|
|
369
|
-
background-color: #e5e7eb !important;
|
|
370
|
-
color: #374151 !important;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
html.dark #dashboard-tabs button:hover {
|
|
374
|
-
background-color: #4b5563 !important;
|
|
375
|
-
color: #d1d5db !important;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/* Active tab */
|
|
379
|
-
#dashboard-tabs button.active,
|
|
380
|
-
#dashboard-tabs button[class*="bg-blue"] {
|
|
381
|
-
background-color: #2563eb !important;
|
|
382
|
-
color: white !important;
|
|
383
|
-
border-bottom-color: #2563eb !important;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
html.dark #dashboard-tabs button.active,
|
|
387
|
-
html.dark #dashboard-tabs button[class*="bg-blue"] {
|
|
388
|
-
background-color: #3b82f6 !important;
|
|
389
|
-
color: white !important;
|
|
390
|
-
border-bottom-color: #3b82f6 !important;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/* 🎯 Universal card borders - transparent cross-theme */
|
|
394
|
-
.card-border,
|
|
395
|
-
.border-base-200,
|
|
396
|
-
[class*="border-base-200"],
|
|
397
|
-
[class*="dark:border-base-700"] {
|
|
398
|
-
border-color: rgba(229, 231, 235, 0.6) !important;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
html.dark .card-border,
|
|
402
|
-
html.dark .border-base-200,
|
|
403
|
-
html.dark [class*="border-base-200"],
|
|
404
|
-
html.dark [class*="dark:border-base-700"] {
|
|
405
|
-
border-color: rgba(55, 65, 81, 0.6) !important;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/* 🎯 Icon spacing defaults - more specific targeting */
|
|
409
|
-
.theme-card .material-icons:not(.no-margin) {
|
|
410
|
-
margin-right: 0.75rem !important; /* 12px default spacing */
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/* Override for specific contexts */
|
|
414
|
-
.theme-card .flex.items-center .material-icons {
|
|
415
|
-
margin-right: 0.75rem !important;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
.theme-card .status-badge .material-icons {
|
|
419
|
-
margin-right: 0.25rem !important; /* 4px for badges */
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/* Fix for buttons and interactive elements */
|
|
423
|
-
.theme-card button .material-icons,
|
|
424
|
-
.theme-card a .material-icons {
|
|
425
|
-
margin-right: 0.5rem !important;
|
|
426
|
-
}
|
|
427
|
-
</style>
|
|
26
|
+
<!-- JavaScript Component Notes:
|
|
27
|
+
- Dashboard tabs functionality: dashboard-tabs.js Alpine component
|
|
28
|
+
- Toggle category functionality: commands-panel.js Alpine component
|
|
29
|
+
- Command execution: command-modal.js Alpine component
|
|
30
|
+
- copyToClipboard and getCookie: dashboard.js (loaded in base_dashboard.html)
|
|
31
|
+
-->
|
|
428
32
|
{% endblock %}
|
|
429
33
|
|
|
430
34
|
{% block breadcrumbs %}{% endblock %}
|
|
@@ -443,120 +47,118 @@
|
|
|
443
47
|
{% block content %}
|
|
444
48
|
<!-- Main Dashboard Container -->
|
|
445
49
|
{% component "unfold/components/container.html" %}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
<
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
<
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
50
|
+
<div x-data="dashboardTabs">
|
|
51
|
+
<!-- Tabs Navigation -->
|
|
52
|
+
<div class="flex gap-1 mb-4" id="dashboard-tabs">
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
@click="switchTab(0)"
|
|
56
|
+
:class="isActive(0) ? 'active' : ''"
|
|
57
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none font-semibold"
|
|
58
|
+
>
|
|
59
|
+
<span class="material-icons mr-2 align-middle text-sm">dashboard</span>
|
|
60
|
+
Overview
|
|
61
|
+
</button>
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
@click="switchTab(1)"
|
|
65
|
+
:class="isActive(1) ? 'active' : ''"
|
|
66
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
67
|
+
>
|
|
68
|
+
<span class="material-icons mr-2 align-middle text-sm">public</span>
|
|
69
|
+
API Zones
|
|
70
|
+
</button>
|
|
71
|
+
<button
|
|
72
|
+
type="button"
|
|
73
|
+
@click="switchTab(2)"
|
|
74
|
+
:class="isActive(2) ? 'active' : ''"
|
|
75
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
76
|
+
>
|
|
77
|
+
<span class="material-icons mr-2 align-middle text-sm">people</span>
|
|
78
|
+
Users
|
|
79
|
+
</button>
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
@click="switchTab(3)"
|
|
83
|
+
:class="isActive(3) ? 'active' : ''"
|
|
84
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
85
|
+
>
|
|
86
|
+
<span class="material-icons mr-2 align-middle text-sm">settings</span>
|
|
87
|
+
System
|
|
88
|
+
</button>
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
@click="switchTab(4)"
|
|
92
|
+
:class="isActive(4) ? 'active' : ''"
|
|
93
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
94
|
+
>
|
|
95
|
+
<span class="material-icons mr-2 align-middle text-sm">analytics</span>
|
|
96
|
+
Statistics
|
|
97
|
+
</button>
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
@click="switchTab(5)"
|
|
101
|
+
:class="isActive(5) ? 'active' : ''"
|
|
102
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
103
|
+
>
|
|
104
|
+
<span class="material-icons mr-2 align-middle text-sm">apps</span>
|
|
105
|
+
App Stats
|
|
106
|
+
</button>
|
|
107
|
+
<button
|
|
108
|
+
type="button"
|
|
109
|
+
@click="switchTab(6)"
|
|
110
|
+
:class="isActive(6) ? 'active' : ''"
|
|
111
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
112
|
+
>
|
|
113
|
+
<span class="material-icons mr-2 align-middle text-sm">terminal</span>
|
|
114
|
+
Commands
|
|
115
|
+
</button>
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
@click="switchTab(7)"
|
|
119
|
+
:class="isActive(7) ? 'active' : ''"
|
|
120
|
+
class="px-4 py-3 rounded-t-lg focus:outline-none"
|
|
121
|
+
>
|
|
122
|
+
<span class="material-icons mr-2 align-middle text-sm">widgets</span>
|
|
123
|
+
Widgets
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
521
126
|
|
|
522
|
-
|
|
127
|
+
<div class="flex flex-col gap-5">
|
|
523
128
|
|
|
524
129
|
<!-- Tab Content with improved spacing -->
|
|
525
|
-
<div class="tab-content
|
|
130
|
+
<div class="tab-content" x-show="isActive(0)">
|
|
526
131
|
{% block overview_tab %}{% endblock %}
|
|
527
132
|
</div>
|
|
528
133
|
|
|
529
|
-
<div class="tab-content"
|
|
134
|
+
<div class="tab-content" x-show="isActive(1)">
|
|
530
135
|
{% block zones_tab %}{% endblock %}
|
|
531
136
|
</div>
|
|
532
137
|
|
|
533
|
-
<div class="tab-content"
|
|
138
|
+
<div class="tab-content" x-show="isActive(2)">
|
|
534
139
|
{% block users_tab %}{% endblock %}
|
|
535
140
|
</div>
|
|
536
141
|
|
|
537
|
-
<div class="tab-content"
|
|
142
|
+
<div class="tab-content" x-show="isActive(3)">
|
|
538
143
|
{% block system_tab %}{% endblock %}
|
|
539
144
|
</div>
|
|
540
145
|
|
|
541
|
-
<div class="tab-content"
|
|
146
|
+
<div class="tab-content" x-show="isActive(4)">
|
|
542
147
|
{% block stats_tab %}{% endblock %}
|
|
543
148
|
</div>
|
|
544
149
|
|
|
545
|
-
<div class="tab-content"
|
|
150
|
+
<div class="tab-content" x-show="isActive(5)">
|
|
546
151
|
{% block app_stats_tab %}{% endblock %}
|
|
547
152
|
</div>
|
|
548
153
|
|
|
549
|
-
<div class="tab-content"
|
|
154
|
+
<div class="tab-content" x-show="isActive(6)">
|
|
550
155
|
{% block commands_tab %}{% endblock %}
|
|
551
156
|
</div>
|
|
552
157
|
|
|
553
|
-
<div class="tab-content"
|
|
554
|
-
{% block documentation_tab %}{% endblock %}
|
|
555
|
-
</div>
|
|
556
|
-
|
|
557
|
-
<div class="tab-content" data-tab="8">
|
|
158
|
+
<div class="tab-content" x-show="isActive(7)">
|
|
558
159
|
{% block widgets_tab %}{% endblock %}
|
|
559
160
|
</div>
|
|
161
|
+
</div>
|
|
560
162
|
</div>
|
|
561
163
|
|
|
562
164
|
<!-- Footer Section -->
|