django-cfg 1.4.59__py3-none-any.whl → 1.4.60__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.

Files changed (53) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/ipc/RPC_LOGGING.md +321 -0
  3. django_cfg/apps/ipc/TESTING.md +539 -0
  4. django_cfg/apps/ipc/__init__.py +12 -3
  5. django_cfg/apps/ipc/admin.py +212 -0
  6. django_cfg/apps/ipc/migrations/0001_initial.py +137 -0
  7. django_cfg/apps/ipc/migrations/__init__.py +0 -0
  8. django_cfg/apps/ipc/models.py +221 -0
  9. django_cfg/apps/ipc/serializers/__init__.py +10 -0
  10. django_cfg/apps/ipc/serializers/serializers.py +114 -0
  11. django_cfg/apps/ipc/services/client/client.py +83 -4
  12. django_cfg/apps/ipc/services/logging.py +239 -0
  13. django_cfg/apps/ipc/services/monitor.py +5 -3
  14. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +269 -0
  15. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +259 -0
  16. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +375 -0
  17. django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +22 -0
  18. django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +9 -0
  19. django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +9 -0
  20. django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +23 -0
  21. django_cfg/apps/ipc/templates/django_cfg_ipc/components/stat_cards.html +50 -0
  22. django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +47 -0
  23. django_cfg/apps/ipc/templates/django_cfg_ipc/components/tab_navigation.html +29 -0
  24. django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +184 -0
  25. django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +56 -0
  26. django_cfg/apps/ipc/urls.py +4 -2
  27. django_cfg/apps/ipc/views/__init__.py +7 -2
  28. django_cfg/apps/ipc/views/dashboard.py +1 -1
  29. django_cfg/apps/ipc/views/{viewsets.py → monitoring.py} +17 -11
  30. django_cfg/apps/ipc/views/testing.py +285 -0
  31. django_cfg/modules/django_client/system/generate_mjs_clients.py +1 -1
  32. django_cfg/modules/django_dashboard/sections/widgets.py +209 -0
  33. django_cfg/modules/django_unfold/callbacks/main.py +43 -18
  34. django_cfg/modules/django_unfold/dashboard.py +41 -4
  35. django_cfg/pyproject.toml +1 -1
  36. django_cfg/static/js/api/index.mjs +8 -3
  37. django_cfg/static/js/api/ipc/client.mjs +40 -0
  38. django_cfg/static/js/api/knowbase/client.mjs +309 -0
  39. django_cfg/static/js/api/knowbase/index.mjs +13 -0
  40. django_cfg/static/js/api/payments/client.mjs +46 -1215
  41. django_cfg/static/js/api/types.mjs +164 -337
  42. django_cfg/templates/admin/index.html +8 -0
  43. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +13 -1
  44. django_cfg/templates/admin/sections/widgets_section.html +129 -0
  45. django_cfg/templates/admin/snippets/tabs/widgets_tab.html +38 -0
  46. {django_cfg-1.4.59.dist-info → django_cfg-1.4.60.dist-info}/METADATA +1 -1
  47. {django_cfg-1.4.59.dist-info → django_cfg-1.4.60.dist-info}/RECORD +52 -28
  48. django_cfg/apps/ipc/templates/django_cfg_ipc/dashboard.html +0 -202
  49. /django_cfg/apps/ipc/static/django_cfg_ipc/js/{dashboard.mjs → dashboard.mjs.old} +0 -0
  50. /django_cfg/apps/ipc/templates/django_cfg_ipc/{base.html → layout/base.html} +0 -0
  51. {django_cfg-1.4.59.dist-info → django_cfg-1.4.60.dist-info}/WHEEL +0 -0
  52. {django_cfg-1.4.59.dist-info → django_cfg-1.4.60.dist-info}/entry_points.txt +0 -0
  53. {django_cfg-1.4.59.dist-info → django_cfg-1.4.60.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,375 @@
1
+ /**
2
+ * RPC Testing Tools Module
3
+ * Handles test client and load testing functionality
4
+ */
5
+ export class TestingModule {
6
+ constructor(api, dashboard) {
7
+ this.api = api;
8
+ this.dashboard = dashboard;
9
+ this.loadTestInterval = null;
10
+ this.isLoadTestRunning = false;
11
+ }
12
+
13
+ /**
14
+ * Initialize testing tools
15
+ */
16
+ init() {
17
+ this.setupEventListeners();
18
+ }
19
+
20
+ /**
21
+ * Setup event listeners for testing tools
22
+ */
23
+ setupEventListeners() {
24
+ // Test RPC Client
25
+ const sendTestBtn = document.getElementById('send-test-rpc-btn');
26
+ const clearTestBtn = document.getElementById('clear-test-rpc-btn');
27
+
28
+ if (sendTestBtn) {
29
+ sendTestBtn.addEventListener('click', () => this.sendTestRequest());
30
+ }
31
+
32
+ if (clearTestBtn) {
33
+ clearTestBtn.addEventListener('click', () => this.clearTestForm());
34
+ }
35
+
36
+ // Load Testing
37
+ const startLoadTestBtn = document.getElementById('start-load-test-btn');
38
+ const stopLoadTestBtn = document.getElementById('stop-load-test-btn');
39
+
40
+ if (startLoadTestBtn) {
41
+ startLoadTestBtn.addEventListener('click', () => this.startLoadTest());
42
+ }
43
+
44
+ if (stopLoadTestBtn) {
45
+ stopLoadTestBtn.addEventListener('click', () => this.stopLoadTest());
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Send a single test RPC request
51
+ */
52
+ async sendTestRequest() {
53
+ try {
54
+ const method = document.getElementById('test-rpc-method')?.value;
55
+ const timeout = parseInt(document.getElementById('test-rpc-timeout')?.value || '10');
56
+ const paramsText = document.getElementById('test-rpc-params')?.value || '{}';
57
+
58
+ // Validate input
59
+ if (!method) {
60
+ this.showTestResponse('Please select a method', 'error');
61
+ return;
62
+ }
63
+
64
+ let params = {};
65
+ try {
66
+ params = JSON.parse(paramsText);
67
+ } catch (e) {
68
+ this.showTestResponse('Invalid JSON in parameters: ' + e.message, 'error');
69
+ return;
70
+ }
71
+
72
+ // Auto-add timestamp if not present (required by WebSocket server)
73
+ if (!params.timestamp) {
74
+ params.timestamp = new Date().toISOString();
75
+ }
76
+
77
+ // Show loading state
78
+ this.showTestResponse('Sending request...', 'info');
79
+
80
+ // Send test request via API
81
+ const response = await this.api.ipcAdminApiTestSendCreate({
82
+ method,
83
+ params,
84
+ timeout
85
+ });
86
+
87
+ // Display response
88
+ this.displayTestResponse(response);
89
+
90
+ } catch (error) {
91
+ this.showTestResponse('Request failed: ' + error.message, 'error');
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Display test response
97
+ */
98
+ displayTestResponse(response) {
99
+ const resultDiv = document.getElementById('test-rpc-result');
100
+ if (!resultDiv) return;
101
+
102
+ const isSuccess = response.success;
103
+ const statusClass = isSuccess ? 'text-green-600' : 'text-red-600';
104
+ const statusIcon = isSuccess ? 'check_circle' : 'error';
105
+
106
+ resultDiv.innerHTML = `
107
+ <div class="p-4 bg-gray-50 dark:bg-gray-700 rounded">
108
+ <!-- Status -->
109
+ <div class="flex items-center mb-3">
110
+ <span class="material-icons ${statusClass} mr-2">${statusIcon}</span>
111
+ <h4 class="font-semibold ${statusClass}">
112
+ ${isSuccess ? 'Success' : 'Failed'}
113
+ </h4>
114
+ </div>
115
+
116
+ <!-- Timing -->
117
+ <div class="mb-3 text-sm">
118
+ <strong>Duration:</strong> ${response.duration_ms?.toFixed(2) || 'N/A'} ms
119
+ </div>
120
+
121
+ <!-- Correlation ID -->
122
+ <div class="mb-3 text-sm">
123
+ <strong>Correlation ID:</strong>
124
+ <code class="bg-gray-200 dark:bg-gray-600 px-2 py-1 rounded text-xs">
125
+ ${this.escapeHtml(response.correlation_id || 'N/A')}
126
+ </code>
127
+ </div>
128
+
129
+ <!-- Response/Error -->
130
+ <div class="mt-3">
131
+ <strong class="text-sm">${isSuccess ? 'Response:' : 'Error:'}</strong>
132
+ <pre class="mt-2 text-xs bg-gray-100 dark:bg-gray-900 p-3 rounded overflow-auto max-h-60">${
133
+ this.escapeHtml(JSON.stringify(isSuccess ? response.response : response.error, null, 2))
134
+ }</pre>
135
+ </div>
136
+ </div>
137
+ `;
138
+
139
+ resultDiv.classList.remove('hidden');
140
+ }
141
+
142
+ /**
143
+ * Show test response message
144
+ */
145
+ showTestResponse(message, type = 'info') {
146
+ const resultDiv = document.getElementById('test-rpc-result');
147
+ if (!resultDiv) return;
148
+
149
+ const colors = {
150
+ info: 'text-blue-600 dark:text-blue-400',
151
+ error: 'text-red-600 dark:text-red-400',
152
+ success: 'text-green-600 dark:text-green-400'
153
+ };
154
+
155
+ const icons = {
156
+ info: 'info',
157
+ error: 'error',
158
+ success: 'check_circle'
159
+ };
160
+
161
+ resultDiv.innerHTML = `
162
+ <div class="p-4 bg-gray-50 dark:bg-gray-700 rounded">
163
+ <div class="flex items-center">
164
+ <span class="material-icons ${colors[type]} mr-2">${icons[type]}</span>
165
+ <span class="${colors[type]}">${this.escapeHtml(message)}</span>
166
+ </div>
167
+ </div>
168
+ `;
169
+
170
+ resultDiv.classList.remove('hidden');
171
+ }
172
+
173
+ /**
174
+ * Clear test form
175
+ */
176
+ clearTestForm() {
177
+ const methodSelect = document.getElementById('test-rpc-method');
178
+ const paramsTextarea = document.getElementById('test-rpc-params');
179
+ const timeoutInput = document.getElementById('test-rpc-timeout');
180
+ const resultDiv = document.getElementById('test-rpc-result');
181
+
182
+ if (methodSelect) methodSelect.value = '';
183
+ if (paramsTextarea) paramsTextarea.value = '{}';
184
+ if (timeoutInput) timeoutInput.value = '10';
185
+ if (resultDiv) resultDiv.classList.add('hidden');
186
+ }
187
+
188
+ /**
189
+ * Start load test
190
+ */
191
+ async startLoadTest() {
192
+ try {
193
+ const method = document.getElementById('load-test-method')?.value;
194
+ const totalRequests = parseInt(document.getElementById('load-test-total')?.value || '100');
195
+ const concurrency = parseInt(document.getElementById('load-test-concurrency')?.value || '10');
196
+ const paramsText = document.getElementById('load-test-params')?.value || '{}';
197
+
198
+ // Validate input
199
+ if (!method) {
200
+ window.showNotification?.('Please select a method', 'error');
201
+ return;
202
+ }
203
+
204
+ if (totalRequests < 1 || totalRequests > 10000) {
205
+ window.showNotification?.('Total requests must be between 1 and 10,000', 'error');
206
+ return;
207
+ }
208
+
209
+ if (concurrency < 1 || concurrency > 100) {
210
+ window.showNotification?.('Concurrency must be between 1 and 100', 'error');
211
+ return;
212
+ }
213
+
214
+ // Parse params
215
+ let params = {};
216
+ try {
217
+ params = JSON.parse(paramsText);
218
+ } catch (e) {
219
+ window.showNotification?.('Invalid JSON in parameters: ' + e.message, 'error');
220
+ return;
221
+ }
222
+
223
+ // Auto-add timestamp if not present
224
+ if (!params.timestamp) {
225
+ params.timestamp = new Date().toISOString();
226
+ }
227
+
228
+ // Start load test
229
+ const response = await this.api.ipcAdminApiTestLoadStartCreate({
230
+ method,
231
+ total_requests: totalRequests,
232
+ concurrency,
233
+ params: params
234
+ });
235
+
236
+ if (response && response.test_id) {
237
+ this.isLoadTestRunning = true;
238
+ this.toggleLoadTestButtons(true);
239
+ this.startLoadTestPolling();
240
+ window.showNotification?.('Load test started', 'success');
241
+ }
242
+
243
+ } catch (error) {
244
+ window.showNotification?.('Failed to start load test: ' + error.message, 'error');
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Stop load test
250
+ */
251
+ async stopLoadTest() {
252
+ try {
253
+ await this.api.ipcAdminApiTestLoadStopCreate({
254
+ method: '',
255
+ params: {},
256
+ timeout: 10
257
+ });
258
+
259
+ this.isLoadTestRunning = false;
260
+ this.stopLoadTestPolling();
261
+ this.toggleLoadTestButtons(false);
262
+ window.showNotification?.('Load test stopped', 'info');
263
+
264
+ } catch (error) {
265
+ window.showNotification?.('Failed to stop load test: ' + error.message, 'error');
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Toggle load test buttons
271
+ */
272
+ toggleLoadTestButtons(isRunning) {
273
+ const startBtn = document.getElementById('start-load-test-btn');
274
+ const stopBtn = document.getElementById('stop-load-test-btn');
275
+
276
+ if (startBtn) {
277
+ startBtn.disabled = isRunning;
278
+ startBtn.classList.toggle('opacity-50', isRunning);
279
+ startBtn.classList.toggle('cursor-not-allowed', isRunning);
280
+ }
281
+
282
+ if (stopBtn) {
283
+ stopBtn.disabled = !isRunning;
284
+ stopBtn.classList.toggle('opacity-50', !isRunning);
285
+ stopBtn.classList.toggle('cursor-not-allowed', !isRunning);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Start polling for load test status
291
+ */
292
+ startLoadTestPolling() {
293
+ // Initial update
294
+ this.updateLoadTestStatus();
295
+
296
+ // Poll every 500ms
297
+ if (this.loadTestInterval) {
298
+ clearInterval(this.loadTestInterval);
299
+ }
300
+
301
+ this.loadTestInterval = setInterval(() => {
302
+ this.updateLoadTestStatus();
303
+ }, 500);
304
+ }
305
+
306
+ /**
307
+ * Stop polling for load test status
308
+ */
309
+ stopLoadTestPolling() {
310
+ if (this.loadTestInterval) {
311
+ clearInterval(this.loadTestInterval);
312
+ this.loadTestInterval = null;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Update load test status
318
+ */
319
+ async updateLoadTestStatus() {
320
+ try {
321
+ const status = await this.api.ipcAdminApiTestLoadStatusRetrieve();
322
+
323
+ if (status) {
324
+ // Update progress bar
325
+ const progressBar = document.getElementById('load-test-progress');
326
+ if (progressBar) {
327
+ const percentage = status.total > 0 ? (status.progress / status.total * 100) : 0;
328
+ progressBar.style.width = percentage + '%';
329
+ }
330
+
331
+ // Update progress text
332
+ const progressText = document.getElementById('load-test-progress-text');
333
+ if (progressText) {
334
+ progressText.textContent = `${status.progress} / ${status.total}`;
335
+ }
336
+
337
+ // Update stats
338
+ this.updateElement('load-test-success', status.success_count);
339
+ this.updateElement('load-test-failed', status.failed_count);
340
+ this.updateElement('load-test-avg-time', status.avg_duration_ms?.toFixed(2) || '0');
341
+ this.updateElement('load-test-rps', status.rps?.toFixed(2) || '0');
342
+
343
+ // Check if test is complete
344
+ if (!status.running && this.isLoadTestRunning) {
345
+ this.isLoadTestRunning = false;
346
+ this.stopLoadTestPolling();
347
+ this.toggleLoadTestButtons(false);
348
+ window.showNotification?.('Load test completed', 'success');
349
+ }
350
+ }
351
+
352
+ } catch (error) {
353
+ console.error('Error updating load test status:', error);
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Helper: Update element text content
359
+ */
360
+ updateElement(id, value) {
361
+ const element = document.getElementById(id);
362
+ if (element) {
363
+ element.textContent = value;
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Helper: Escape HTML to prevent XSS
369
+ */
370
+ escapeHtml(unsafe) {
371
+ const div = document.createElement('div');
372
+ div.textContent = unsafe;
373
+ return div.innerHTML;
374
+ }
375
+ }
@@ -0,0 +1,22 @@
1
+ <!-- Methods Tab -->
2
+ <div id="methods-tab" class="tab-panel hidden">
3
+ <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
4
+ <table class="w-full">
5
+ <thead class="bg-gray-50 dark:bg-gray-900">
6
+ <tr>
7
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Method</th>
8
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Count</th>
9
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Percentage</th>
10
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Avg Time</th>
11
+ </tr>
12
+ </thead>
13
+ <tbody id="methods-table-body" class="divide-y divide-gray-200 dark:divide-gray-700">
14
+ <tr>
15
+ <td colspan="4" class="px-4 py-8 text-center text-gray-600 dark:text-gray-400">
16
+ Loading...
17
+ </td>
18
+ </tr>
19
+ </tbody>
20
+ </table>
21
+ </div>
22
+ </div>
@@ -0,0 +1,9 @@
1
+ <!-- Notifications Tab -->
2
+ <div id="notifications-tab" class="tab-panel hidden">
3
+ <div class="bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700">
4
+ <h3 class="text-lg font-bold text-gray-900 dark:text-white mb-4">Notification Statistics</h3>
5
+ <div id="notification-stats-content">
6
+ <p class="text-gray-600 dark:text-gray-400">Loading...</p>
7
+ </div>
8
+ </div>
9
+ </div>
@@ -0,0 +1,9 @@
1
+ <!-- Overview Tab (Active by default) -->
2
+ <div id="overview-tab" class="tab-panel">
3
+ <div class="bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700">
4
+ <h3 class="text-lg font-bold text-gray-900 dark:text-white mb-4">Top Method</h3>
5
+ <p class="text-gray-700 dark:text-gray-300" id="top-method">
6
+ <span class="text-gray-500">Loading...</span>
7
+ </p>
8
+ </div>
9
+ </div>
@@ -0,0 +1,23 @@
1
+ <!-- Recent Requests Tab -->
2
+ <div id="requests-tab" class="tab-panel hidden">
3
+ <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
4
+ <table class="w-full">
5
+ <thead class="bg-gray-50 dark:bg-gray-900">
6
+ <tr>
7
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Time</th>
8
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Method</th>
9
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Status</th>
10
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Duration</th>
11
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-400 uppercase">Correlation ID</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody id="requests-table-body" class="divide-y divide-gray-200 dark:divide-gray-700">
15
+ <tr>
16
+ <td colspan="5" class="px-4 py-8 text-center text-gray-600 dark:text-gray-400">
17
+ Loading...
18
+ </td>
19
+ </tr>
20
+ </tbody>
21
+ </table>
22
+ </div>
23
+ </div>
@@ -0,0 +1,50 @@
1
+ <!-- Overview Stats Cards -->
2
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
3
+ <!-- Total Requests -->
4
+ <div class="stat-card bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700">
5
+ <div class="flex items-center justify-between mb-4">
6
+ <span class="material-icons text-4xl text-blue-500 dark:text-blue-400">bar_chart</span>
7
+ <span class="text-xs text-gray-600 dark:text-gray-400">Today</span>
8
+ </div>
9
+ <h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-1" id="total-requests">
10
+ 0
11
+ </h3>
12
+ <p class="text-sm text-gray-600 dark:text-gray-400">Total Requests</p>
13
+ </div>
14
+
15
+ <!-- Active Methods -->
16
+ <div class="stat-card bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700">
17
+ <div class="flex items-center justify-between mb-4">
18
+ <span class="material-icons text-4xl text-green-500 dark:text-green-400">functions</span>
19
+ <span class="text-xs text-gray-600 dark:text-gray-400">Active</span>
20
+ </div>
21
+ <h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-1" id="active-methods-count">
22
+ 0
23
+ </h3>
24
+ <p class="text-sm text-gray-600 dark:text-gray-400">Active Methods</p>
25
+ </div>
26
+
27
+ <!-- Avg Response Time -->
28
+ <div class="stat-card bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700">
29
+ <div class="flex items-center justify-between mb-4">
30
+ <span class="material-icons text-4xl text-yellow-500 dark:text-yellow-400">timer</span>
31
+ <span class="text-xs text-gray-600 dark:text-gray-400">Average</span>
32
+ </div>
33
+ <h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-1" id="avg-response-time">
34
+ 0<span class="text-lg text-gray-600 dark:text-gray-400">ms</span>
35
+ </h3>
36
+ <p class="text-sm text-gray-600 dark:text-gray-400">Response Time</p>
37
+ </div>
38
+
39
+ <!-- Success Rate -->
40
+ <div class="stat-card bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700">
41
+ <div class="flex items-center justify-between mb-4">
42
+ <span class="material-icons text-4xl text-purple-500 dark:text-purple-400">check_circle</span>
43
+ <span class="text-xs text-gray-600 dark:text-gray-400">Rate</span>
44
+ </div>
45
+ <h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-1" id="success-rate">
46
+ 0<span class="text-lg text-gray-600 dark:text-gray-400">%</span>
47
+ </h3>
48
+ <p class="text-sm text-gray-600 dark:text-gray-400">Success Rate</p>
49
+ </div>
50
+ </div>
@@ -0,0 +1,47 @@
1
+ <!-- System Status -->
2
+ <div class="bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700 mb-8">
3
+ <h2 class="text-xl font-bold text-gray-900 dark:text-white mb-4 flex items-center">
4
+ <span class="material-icons mr-2">health_and_safety</span>
5
+ System Status
6
+ </h2>
7
+ <div id="system-status" class="grid grid-cols-1 md:grid-cols-3 gap-4">
8
+ <!-- Redis Status -->
9
+ <div class="flex items-start gap-3">
10
+ <span class="material-icons flex-shrink-0 text-2xl text-gray-500" id="redis-status-icon">
11
+ help_outline
12
+ </span>
13
+ <div class="min-w-0">
14
+ <p class="text-sm font-medium text-gray-900 dark:text-white">Redis</p>
15
+ <p class="text-xs text-gray-600 dark:text-gray-400" id="redis-status-text">
16
+ Checking...
17
+ </p>
18
+ </div>
19
+ </div>
20
+
21
+ <!-- Stream Status -->
22
+ <div class="flex items-start gap-3">
23
+ <span class="material-icons flex-shrink-0 text-2xl text-gray-500" id="stream-status-icon">
24
+ help_outline
25
+ </span>
26
+ <div class="min-w-0">
27
+ <p class="text-sm font-medium text-gray-900 dark:text-white">Request Stream</p>
28
+ <p class="text-xs text-gray-600 dark:text-gray-400" id="stream-status-text">
29
+ Checking...
30
+ </p>
31
+ </div>
32
+ </div>
33
+
34
+ <!-- Activity Status -->
35
+ <div class="flex items-start gap-3">
36
+ <span class="material-icons flex-shrink-0 text-2xl text-gray-500" id="activity-status-icon">
37
+ help_outline
38
+ </span>
39
+ <div class="min-w-0">
40
+ <p class="text-sm font-medium text-gray-900 dark:text-white">Recent Activity</p>
41
+ <p class="text-xs text-gray-600 dark:text-gray-400" id="activity-status-text">
42
+ Checking...
43
+ </p>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </div>
@@ -0,0 +1,29 @@
1
+ <!-- Tabs Navigation -->
2
+ <div class="border-b border-gray-200 dark:border-gray-700 mb-6">
3
+ <nav class="flex space-x-4">
4
+ <button class="tab-button active px-4 py-2 text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400 font-medium flex items-center gap-2" data-tab="overview">
5
+ <span class="material-icons text-sm">dashboard</span>
6
+ <span>Overview</span>
7
+ <span class="badge rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 text-xs" id="overview-count-badge">0</span>
8
+ </button>
9
+ <button class="tab-button px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300 border-b-2 border-transparent font-medium flex items-center gap-2" data-tab="requests">
10
+ <span class="material-icons text-sm">list</span>
11
+ <span>Recent Requests</span>
12
+ <span class="badge rounded-full bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 text-xs" id="requests-count-badge">0</span>
13
+ </button>
14
+ <button class="tab-button px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300 border-b-2 border-transparent font-medium flex items-center gap-2" data-tab="notifications">
15
+ <span class="material-icons text-sm">notifications</span>
16
+ <span>Notifications</span>
17
+ <span class="badge rounded-full bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 text-xs" id="notifications-count-badge">0</span>
18
+ </button>
19
+ <button class="tab-button px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300 border-b-2 border-transparent font-medium flex items-center gap-2" data-tab="methods">
20
+ <span class="material-icons text-sm">functions</span>
21
+ <span>Methods</span>
22
+ <span class="badge rounded-full bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 text-xs" id="methods-count-badge">0</span>
23
+ </button>
24
+ <button class="tab-button px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300 border-b-2 border-transparent font-medium flex items-center gap-2" data-tab="testing">
25
+ <span class="material-icons text-sm">science</span>
26
+ <span>Testing</span>
27
+ </button>
28
+ </nav>
29
+ </div>