django-cfg 1.3.9__py3-none-any.whl → 1.3.11__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin/networks_admin.py +12 -1
- django_cfg/apps/payments/admin/payments_admin.py +13 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/payment_service.py +265 -38
- django_cfg/apps/payments/services/providers/base.py +209 -3
- django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
- django_cfg/apps/payments/services/providers/models/base.py +25 -2
- django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
- django_cfg/apps/payments/services/providers/registry.py +5 -5
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/views/api/currencies.py +3 -0
- django_cfg/apps/payments/views/serializers/currencies.py +18 -5
- django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/registry/core.py +4 -9
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
- django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
- django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/app_agent_diagnose.py +0 -470
- django_cfg/management/commands/app_agent_generate.py +0 -342
- django_cfg/management/commands/app_agent_info.py +0 -308
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/modules/django_app_agent/__init__.py +0 -87
- django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
- django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
- django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
- django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
- django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
- django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
- django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
- django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
- django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
- django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
- django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
- django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
- django_cfg/modules/django_app_agent/core/__init__.py +0 -33
- django_cfg/modules/django_app_agent/core/config.py +0 -300
- django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
- django_cfg/modules/django_app_agent/models/__init__.py +0 -71
- django_cfg/modules/django_app_agent/models/base.py +0 -283
- django_cfg/modules/django_app_agent/models/context.py +0 -496
- django_cfg/modules/django_app_agent/models/enums.py +0 -481
- django_cfg/modules/django_app_agent/models/requests.py +0 -500
- django_cfg/modules/django_app_agent/models/responses.py +0 -585
- django_cfg/modules/django_app_agent/pytest.ini +0 -6
- django_cfg/modules/django_app_agent/services/__init__.py +0 -42
- django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
- django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
- django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
- django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
- django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
- django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
- django_cfg/modules/django_app_agent/services/base.py +0 -437
- django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
- django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
- django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
- django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
- django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
- django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
- django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
- django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
- django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
- django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
- django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
- django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
- django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
- django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
- django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
- django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
- django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
- django_cfg/modules/django_app_agent/services/report_service.py +0 -332
- django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
- django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
- django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
- django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
- django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
- django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
- django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
- django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
- django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
- django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
- django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
- django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
- django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
- django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
- django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
- django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
- django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
- django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
- django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
- django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
- django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
- django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
- django_cfg/modules/django_app_agent/ui/cli.py +0 -419
- django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
- django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
- django_cfg/modules/django_app_agent/utils/logging.py +0 -360
- django_cfg/modules/django_app_agent/utils/validation.py +0 -417
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,216 @@
|
|
1
|
+
/**
|
2
|
+
* TaskRenderer - Renders tasks using HTML templates (no HTML in JS!)
|
3
|
+
*/
|
4
|
+
export class TaskRenderer {
|
5
|
+
constructor() {
|
6
|
+
this.taskRowTemplate = document.getElementById('task-row-template');
|
7
|
+
this.statusConfig = {
|
8
|
+
pending: {
|
9
|
+
icon: 'schedule',
|
10
|
+
label: 'PENDING',
|
11
|
+
color: 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200',
|
12
|
+
iconColor: 'text-yellow-500'
|
13
|
+
},
|
14
|
+
running: {
|
15
|
+
icon: 'play_circle',
|
16
|
+
label: 'RUNNING',
|
17
|
+
color: 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200',
|
18
|
+
iconColor: 'text-blue-500'
|
19
|
+
},
|
20
|
+
completed: {
|
21
|
+
icon: 'check_circle',
|
22
|
+
label: 'COMPLETED',
|
23
|
+
color: 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200',
|
24
|
+
iconColor: 'text-green-500'
|
25
|
+
},
|
26
|
+
failed: {
|
27
|
+
icon: 'error',
|
28
|
+
label: 'FAILED',
|
29
|
+
color: 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200',
|
30
|
+
iconColor: 'text-red-500'
|
31
|
+
}
|
32
|
+
};
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Render tasks into the table
|
37
|
+
* @param {Array} tasks - Array of task objects
|
38
|
+
* @param {HTMLElement} container - Container element
|
39
|
+
*/
|
40
|
+
renderTasks(tasks, container) {
|
41
|
+
const tableBody = container.querySelector('#task-table-body');
|
42
|
+
if (!tableBody) return;
|
43
|
+
|
44
|
+
// Clear existing rows
|
45
|
+
tableBody.innerHTML = '';
|
46
|
+
|
47
|
+
// Render each task
|
48
|
+
tasks.forEach(task => {
|
49
|
+
const row = this.createTaskRow(task);
|
50
|
+
if (row) {
|
51
|
+
tableBody.appendChild(row);
|
52
|
+
}
|
53
|
+
});
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Create a task row element using template
|
58
|
+
* @param {Object} task - Task object
|
59
|
+
* @returns {HTMLElement} - Task row element
|
60
|
+
*/
|
61
|
+
createTaskRow(task) {
|
62
|
+
if (!this.taskRowTemplate) {
|
63
|
+
console.error('Task row template not found');
|
64
|
+
return null;
|
65
|
+
}
|
66
|
+
|
67
|
+
// Clone template
|
68
|
+
const row = this.taskRowTemplate.content.cloneNode(true).querySelector('.task-row');
|
69
|
+
|
70
|
+
// Set task ID
|
71
|
+
row.setAttribute('data-task-id', task.id);
|
72
|
+
|
73
|
+
// Map status
|
74
|
+
const displayStatus = this.mapStatus(task.status);
|
75
|
+
const statusConfig = this.statusConfig[displayStatus];
|
76
|
+
|
77
|
+
// Populate status
|
78
|
+
const statusIcon = row.querySelector('.task-status-icon');
|
79
|
+
const statusBadge = row.querySelector('.task-status-badge');
|
80
|
+
|
81
|
+
if (statusIcon && statusBadge && statusConfig) {
|
82
|
+
statusIcon.textContent = statusConfig.icon;
|
83
|
+
statusIcon.className = `material-icons text-sm ${statusConfig.iconColor}`;
|
84
|
+
|
85
|
+
statusBadge.textContent = statusConfig.label;
|
86
|
+
statusBadge.className = `px-2 py-1 text-xs font-medium rounded-full ${statusConfig.color}`;
|
87
|
+
}
|
88
|
+
|
89
|
+
// Show progress bar for running tasks
|
90
|
+
if (displayStatus === 'running' && task.progress) {
|
91
|
+
const progressContainer = row.querySelector('.task-progress');
|
92
|
+
const progressBar = progressContainer?.querySelector('div');
|
93
|
+
if (progressContainer && progressBar) {
|
94
|
+
progressContainer.classList.remove('hidden');
|
95
|
+
progressBar.style.width = `${task.progress}%`;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
// Populate task info
|
100
|
+
const taskName = row.querySelector('.task-name');
|
101
|
+
const taskId = row.querySelector('.task-id');
|
102
|
+
if (taskName) taskName.textContent = task.actor_name || 'Unknown Task';
|
103
|
+
if (taskId) taskId.textContent = `${String(task.id).substring(0, 8)}...`;
|
104
|
+
|
105
|
+
// Populate queue
|
106
|
+
const taskQueue = row.querySelector('.task-queue');
|
107
|
+
if (taskQueue) taskQueue.textContent = task.queue_name || task.queue || 'unknown';
|
108
|
+
|
109
|
+
// Populate duration and time
|
110
|
+
const taskDuration = row.querySelector('.task-duration');
|
111
|
+
const taskUpdated = row.querySelector('.task-updated');
|
112
|
+
if (taskDuration) taskDuration.textContent = this.calculateDuration(task.created_at, task.updated_at);
|
113
|
+
if (taskUpdated) taskUpdated.textContent = new Date(task.updated_at).toLocaleTimeString();
|
114
|
+
|
115
|
+
// Populate actions
|
116
|
+
this.populateActions(row, task, displayStatus);
|
117
|
+
|
118
|
+
return row;
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* Map task status to display status
|
123
|
+
* @param {string} status - Original status
|
124
|
+
* @returns {string} - Mapped status
|
125
|
+
*/
|
126
|
+
mapStatus(status) {
|
127
|
+
const statusMap = {
|
128
|
+
'enqueued': 'pending',
|
129
|
+
'delayed': 'pending',
|
130
|
+
'pending': 'pending',
|
131
|
+
'running': 'running',
|
132
|
+
'done': 'completed',
|
133
|
+
'completed': 'completed',
|
134
|
+
'failed': 'failed',
|
135
|
+
'skipped': 'completed'
|
136
|
+
};
|
137
|
+
|
138
|
+
return statusMap[status?.toLowerCase()] || 'pending';
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Populate task actions (no HTML strings!)
|
143
|
+
* @param {HTMLElement} row - Task row element
|
144
|
+
* @param {Object} task - Task object
|
145
|
+
* @param {string} displayStatus - Display status
|
146
|
+
*/
|
147
|
+
populateActions(row, task, displayStatus) {
|
148
|
+
const actionsContainer = row.querySelector('.task-actions');
|
149
|
+
if (!actionsContainer) return;
|
150
|
+
|
151
|
+
actionsContainer.innerHTML = '';
|
152
|
+
|
153
|
+
// Add retry button for failed tasks
|
154
|
+
if (displayStatus === 'failed') {
|
155
|
+
const retryBtn = document.createElement('button');
|
156
|
+
retryBtn.className = 'px-2 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors';
|
157
|
+
retryBtn.textContent = 'Retry';
|
158
|
+
retryBtn.addEventListener('click', () => this.retryTask(task.id));
|
159
|
+
actionsContainer.appendChild(retryBtn);
|
160
|
+
}
|
161
|
+
|
162
|
+
// Add details button if task has additional info
|
163
|
+
if (task.args || task.kwargs || task.result || task.traceback) {
|
164
|
+
const detailsBtn = document.createElement('button');
|
165
|
+
detailsBtn.className = 'p-1 text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded transition-colors';
|
166
|
+
detailsBtn.title = 'Show details';
|
167
|
+
|
168
|
+
const icon = document.createElement('span');
|
169
|
+
icon.className = 'material-icons text-sm';
|
170
|
+
icon.textContent = 'info';
|
171
|
+
detailsBtn.appendChild(icon);
|
172
|
+
|
173
|
+
detailsBtn.addEventListener('click', () => this.showTaskDetails(task));
|
174
|
+
actionsContainer.appendChild(detailsBtn);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
/**
|
179
|
+
* Calculate duration between two dates
|
180
|
+
* @param {string} startTime - Start time ISO string
|
181
|
+
* @param {string} endTime - End time ISO string
|
182
|
+
* @returns {string} - Formatted duration
|
183
|
+
*/
|
184
|
+
calculateDuration(startTime, endTime) {
|
185
|
+
if (!startTime || !endTime) return '--';
|
186
|
+
|
187
|
+
const start = new Date(startTime);
|
188
|
+
const end = new Date(endTime);
|
189
|
+
const diff = end - start;
|
190
|
+
|
191
|
+
if (diff < 1000) return `${diff}ms`;
|
192
|
+
if (diff < 60000) return `${Math.floor(diff / 1000)}s`;
|
193
|
+
if (diff < 3600000) return `${Math.floor(diff / 60000)}m ${Math.floor((diff % 60000) / 1000)}s`;
|
194
|
+
return `${Math.floor(diff / 3600000)}h ${Math.floor((diff % 3600000) / 60000)}m`;
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Retry a failed task
|
199
|
+
* @param {string} taskId - Task ID
|
200
|
+
*/
|
201
|
+
retryTask(taskId) {
|
202
|
+
console.log('Retrying task:', taskId);
|
203
|
+
// TODO: Implement retry functionality
|
204
|
+
alert(`Retry functionality for task ${taskId} will be implemented`);
|
205
|
+
}
|
206
|
+
|
207
|
+
/**
|
208
|
+
* Show task details
|
209
|
+
* @param {Object} task - Task object
|
210
|
+
*/
|
211
|
+
showTaskDetails(task) {
|
212
|
+
console.log('Showing task details:', task);
|
213
|
+
// TODO: Implement task details modal
|
214
|
+
alert(`Task Details:\nID: ${task.id}\nActor: ${task.actor_name}\nStatus: ${task.status}`);
|
215
|
+
}
|
216
|
+
}
|
@@ -0,0 +1,245 @@
|
|
1
|
+
/**
|
2
|
+
* Main Tasks Dashboard Controller
|
3
|
+
* Orchestrates all dashboard modules
|
4
|
+
*/
|
5
|
+
import { OverviewModule } from './overview.mjs';
|
6
|
+
import { QueuesModule } from './queues.mjs';
|
7
|
+
import { WorkersModule } from './workers.mjs';
|
8
|
+
import { TasksModule } from './tasks.mjs';
|
9
|
+
|
10
|
+
class TasksDashboard {
|
11
|
+
constructor() {
|
12
|
+
this.api = window.tasksAPI;
|
13
|
+
this.currentTab = 'overview';
|
14
|
+
this.refreshInterval = null;
|
15
|
+
this.refreshRate = 30000; // 30 seconds
|
16
|
+
|
17
|
+
// Initialize modules (pass reference to this dashboard for badge updates)
|
18
|
+
this.overviewModule = new OverviewModule(this.api, this);
|
19
|
+
this.queuesModule = new QueuesModule(this.api, this);
|
20
|
+
this.workersModule = new WorkersModule(this.api, this);
|
21
|
+
this.tasksModule = new TasksModule(this.api, this);
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Initialize dashboard
|
26
|
+
*/
|
27
|
+
init() {
|
28
|
+
console.log('Initializing Tasks Dashboard...');
|
29
|
+
this.setupEventListeners();
|
30
|
+
this.loadInitialData();
|
31
|
+
this.startAutoRefresh();
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Setup event listeners
|
36
|
+
*/
|
37
|
+
setupEventListeners() {
|
38
|
+
// Tab buttons
|
39
|
+
document.querySelectorAll('.tab-button').forEach(button => {
|
40
|
+
button.addEventListener('click', (e) => {
|
41
|
+
const tabId = e.currentTarget.dataset.tab;
|
42
|
+
this.switchTab(tabId);
|
43
|
+
});
|
44
|
+
});
|
45
|
+
|
46
|
+
// Management actions
|
47
|
+
document.getElementById('simulate-data-btn')?.addEventListener('click', () => this.simulateData());
|
48
|
+
document.getElementById('clear-test-data-btn')?.addEventListener('click', () => this.clearData());
|
49
|
+
document.getElementById('clear-all-queues-btn')?.addEventListener('click', () => this.clearQueues());
|
50
|
+
document.getElementById('purge-failed-tasks-btn')?.addEventListener('click', () => this.purgeFailed());
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Switch tabs
|
55
|
+
*/
|
56
|
+
switchTab(tabId) {
|
57
|
+
console.log('Switching to tab:', tabId);
|
58
|
+
|
59
|
+
// Update active tab styling
|
60
|
+
document.querySelectorAll('.tab-button').forEach(tab => {
|
61
|
+
tab.classList.remove('active');
|
62
|
+
tab.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300', 'dark:text-gray-400', 'dark:hover:text-gray-300');
|
63
|
+
});
|
64
|
+
|
65
|
+
// Add active to selected tab
|
66
|
+
const activeTab = document.querySelector(`[data-tab="${tabId}"]`);
|
67
|
+
if (activeTab) {
|
68
|
+
activeTab.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300', 'dark:text-gray-400', 'dark:hover:text-gray-300');
|
69
|
+
activeTab.classList.add('active');
|
70
|
+
}
|
71
|
+
|
72
|
+
// Hide all tab panels
|
73
|
+
document.querySelectorAll('.tab-panel').forEach(panel => {
|
74
|
+
panel.classList.add('hidden');
|
75
|
+
panel.classList.remove('active');
|
76
|
+
});
|
77
|
+
|
78
|
+
// Show selected tab panel
|
79
|
+
const activePanel = document.querySelector(`#${tabId}-tab`);
|
80
|
+
if (activePanel) {
|
81
|
+
activePanel.classList.remove('hidden');
|
82
|
+
activePanel.classList.add('active');
|
83
|
+
}
|
84
|
+
|
85
|
+
this.currentTab = tabId;
|
86
|
+
this.loadTabData(tabId);
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Load initial data
|
91
|
+
*/
|
92
|
+
async loadInitialData() {
|
93
|
+
console.log('Loading initial data...');
|
94
|
+
await this.loadTabData(this.currentTab);
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Load data for specific tab using modules
|
99
|
+
*/
|
100
|
+
async loadTabData(tabId) {
|
101
|
+
console.log('Loading data for tab:', tabId);
|
102
|
+
|
103
|
+
switch (tabId) {
|
104
|
+
case 'overview':
|
105
|
+
await this.overviewModule.loadData();
|
106
|
+
break;
|
107
|
+
case 'queues':
|
108
|
+
await this.queuesModule.loadData();
|
109
|
+
break;
|
110
|
+
case 'workers':
|
111
|
+
await this.workersModule.loadData();
|
112
|
+
break;
|
113
|
+
case 'tasks':
|
114
|
+
await this.tasksModule.loadData();
|
115
|
+
break;
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* Management actions
|
121
|
+
*/
|
122
|
+
async simulateData() {
|
123
|
+
try {
|
124
|
+
this.showManagementActionStatus('Simulating test data...', 'info');
|
125
|
+
const response = await this.api.simulateData();
|
126
|
+
|
127
|
+
if (response.success) {
|
128
|
+
this.showManagementActionStatus('Test data simulation completed successfully', 'success');
|
129
|
+
await this.loadTabData(this.currentTab);
|
130
|
+
} else {
|
131
|
+
this.showManagementActionStatus(`Simulation failed: ${response.error}`, 'error');
|
132
|
+
}
|
133
|
+
} catch (error) {
|
134
|
+
this.showManagementActionStatus(`Simulation error: ${error.message}`, 'error');
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
async clearData() {
|
139
|
+
try {
|
140
|
+
this.showManagementActionStatus('Clearing test data...', 'info');
|
141
|
+
const response = await this.api.clearData();
|
142
|
+
|
143
|
+
if (response.success) {
|
144
|
+
this.showManagementActionStatus('Test data cleared successfully', 'success');
|
145
|
+
await this.loadTabData(this.currentTab);
|
146
|
+
} else {
|
147
|
+
this.showManagementActionStatus(`Clear failed: ${response.error}`, 'error');
|
148
|
+
}
|
149
|
+
} catch (error) {
|
150
|
+
this.showManagementActionStatus(`Clear error: ${error.message}`, 'error');
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
async clearQueues() {
|
155
|
+
console.log('Clearing queues...');
|
156
|
+
this.showManagementActionStatus('Clearing all queues...', 'info');
|
157
|
+
try {
|
158
|
+
const response = await this.api.clearQueues();
|
159
|
+
if (response.success) {
|
160
|
+
this.showManagementActionStatus(response.message, 'success');
|
161
|
+
this.loadTabData(this.currentTab);
|
162
|
+
} else {
|
163
|
+
this.showManagementActionStatus(`Failed to clear queues: ${response.error}`, 'error');
|
164
|
+
}
|
165
|
+
} catch (error) {
|
166
|
+
this.showManagementActionStatus(`Failed to clear queues: ${error.message}`, 'error');
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
async purgeFailed() {
|
171
|
+
console.log('Purging failed tasks...');
|
172
|
+
this.showManagementActionStatus('Purging failed tasks...', 'info');
|
173
|
+
try {
|
174
|
+
const response = await this.api.purgeFailed();
|
175
|
+
if (response.success) {
|
176
|
+
this.showManagementActionStatus(response.message, 'success');
|
177
|
+
this.loadTabData(this.currentTab);
|
178
|
+
} else {
|
179
|
+
this.showManagementActionStatus(`Failed to purge failed tasks: ${response.error}`, 'error');
|
180
|
+
}
|
181
|
+
} catch (error) {
|
182
|
+
this.showManagementActionStatus(`Failed to purge failed tasks: ${error.message}`, 'error');
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
/**
|
187
|
+
* Show management action status
|
188
|
+
*/
|
189
|
+
showManagementActionStatus(message, type = 'info') {
|
190
|
+
const statusDiv = document.getElementById('management-action-status');
|
191
|
+
const messageSpan = document.getElementById('management-action-message');
|
192
|
+
|
193
|
+
if (statusDiv && messageSpan) {
|
194
|
+
messageSpan.textContent = message;
|
195
|
+
statusDiv.classList.remove('hidden');
|
196
|
+
|
197
|
+
setTimeout(() => {
|
198
|
+
statusDiv.classList.add('hidden');
|
199
|
+
}, 5000);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Update tab badges with counts
|
205
|
+
*/
|
206
|
+
updateTabBadges(data) {
|
207
|
+
// Update queues badge
|
208
|
+
const queuesBadge = document.querySelector('#queues-count-badge');
|
209
|
+
if (queuesBadge && data.active_queues !== undefined) {
|
210
|
+
queuesBadge.textContent = data.active_queues;
|
211
|
+
}
|
212
|
+
|
213
|
+
// Update workers badge
|
214
|
+
const workersBadge = document.querySelector('#workers-count-badge');
|
215
|
+
if (workersBadge && data.workers !== undefined) {
|
216
|
+
workersBadge.textContent = data.workers;
|
217
|
+
}
|
218
|
+
|
219
|
+
// Update tasks badge - will be updated by tasks module
|
220
|
+
const tasksBadge = document.querySelector('#tasks-count-badge');
|
221
|
+
if (tasksBadge && data.total_tasks !== undefined) {
|
222
|
+
tasksBadge.textContent = data.total_tasks;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
/**
|
227
|
+
* Start auto-refresh
|
228
|
+
*/
|
229
|
+
startAutoRefresh() {
|
230
|
+
if (this.refreshInterval) {
|
231
|
+
clearInterval(this.refreshInterval);
|
232
|
+
}
|
233
|
+
|
234
|
+
this.refreshInterval = setInterval(() => {
|
235
|
+
this.loadTabData(this.currentTab);
|
236
|
+
}, this.refreshRate);
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
// Initialize dashboard when DOM is ready
|
241
|
+
document.addEventListener('DOMContentLoaded', () => {
|
242
|
+
console.log('DOM loaded, initializing dashboard...');
|
243
|
+
window.dashboard = new TasksDashboard();
|
244
|
+
window.dashboard.init();
|
245
|
+
});
|
@@ -0,0 +1,123 @@
|
|
1
|
+
/**
|
2
|
+
* Overview Dashboard Module
|
3
|
+
* Handles overview tab functionality
|
4
|
+
*/
|
5
|
+
export class OverviewModule {
|
6
|
+
constructor(api, dashboard) {
|
7
|
+
this.api = api;
|
8
|
+
this.dashboard = dashboard; // Reference to main dashboard for badge updates
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Load overview data
|
13
|
+
*/
|
14
|
+
async loadData() {
|
15
|
+
try {
|
16
|
+
console.log('Loading overview data...');
|
17
|
+
|
18
|
+
const [queueData, taskStats] = await Promise.all([
|
19
|
+
this.api.getQueueStatus(),
|
20
|
+
this.api.getTaskStatistics()
|
21
|
+
]);
|
22
|
+
|
23
|
+
const queueInfo = queueData.data || queueData;
|
24
|
+
const taskInfo = taskStats.data || taskStats;
|
25
|
+
|
26
|
+
this.updateStatusCards(queueInfo, taskInfo);
|
27
|
+
this.updateSystemStatus(queueInfo);
|
28
|
+
|
29
|
+
// Update tab badges
|
30
|
+
if (this.dashboard && this.dashboard.updateTabBadges) {
|
31
|
+
this.dashboard.updateTabBadges(queueInfo);
|
32
|
+
}
|
33
|
+
|
34
|
+
} catch (error) {
|
35
|
+
console.error('Failed to load overview data:', error);
|
36
|
+
this.showError('Failed to load overview data');
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Update status cards
|
42
|
+
*/
|
43
|
+
updateStatusCards(queueData, taskData) {
|
44
|
+
this.updateElement('active-queues-count', queueData.active_queues || 0);
|
45
|
+
this.updateElement('workers-count', queueData.workers || 0);
|
46
|
+
this.updateElement('pending-tasks-count', queueData.total_pending || 0);
|
47
|
+
this.updateElement('failed-tasks-count', queueData.total_failed || 0);
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Update system status
|
52
|
+
*/
|
53
|
+
updateSystemStatus(data) {
|
54
|
+
const statusContainer = document.getElementById('system-status');
|
55
|
+
if (!statusContainer) return;
|
56
|
+
|
57
|
+
const isHealthy = data.redis_connected && !data.error;
|
58
|
+
const timestamp = new Date().toLocaleTimeString();
|
59
|
+
|
60
|
+
statusContainer.innerHTML = `
|
61
|
+
<div class="flex items-center justify-between mb-4">
|
62
|
+
<div class="flex items-center">
|
63
|
+
<span class="material-icons text-2xl mr-3 ${isHealthy ? 'text-green-600' : 'text-red-600'}">
|
64
|
+
${isHealthy ? 'check_circle' : 'error'}
|
65
|
+
</span>
|
66
|
+
<div>
|
67
|
+
<h3 class="text-lg font-semibold ${isHealthy ? 'text-green-800' : 'text-red-800'} dark:${isHealthy ? 'text-green-200' : 'text-red-200'}">
|
68
|
+
${isHealthy ? 'System Healthy' : 'System Issues Detected'}
|
69
|
+
</h3>
|
70
|
+
<p class="text-sm text-gray-600 dark:text-gray-400">
|
71
|
+
${isHealthy ? 'All systems operational' : 'Some components need attention'}
|
72
|
+
</p>
|
73
|
+
</div>
|
74
|
+
</div>
|
75
|
+
<span class="text-xs text-gray-500 dark:text-gray-400">Last updated: ${timestamp}</span>
|
76
|
+
</div>
|
77
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
78
|
+
<div class="flex items-center">
|
79
|
+
<span class="material-icons text-base mr-2 ${data.redis_connected ? 'text-green-500' : 'text-red-500'}">
|
80
|
+
${data.redis_connected ? 'check' : 'close'}
|
81
|
+
</span>
|
82
|
+
<span class="text-gray-700 dark:text-gray-300">Redis: ${data.redis_connected ? 'Connected' : 'Disconnected'}</span>
|
83
|
+
</div>
|
84
|
+
<div class="flex items-center">
|
85
|
+
<span class="material-icons text-base mr-2 ${data.workers > 0 ? 'text-green-500' : 'text-red-500'}">
|
86
|
+
${data.workers > 0 ? 'check' : 'close'}
|
87
|
+
</span>
|
88
|
+
<span class="text-gray-700 dark:text-gray-300">Workers: ${data.workers} active</span>
|
89
|
+
</div>
|
90
|
+
<div class="flex items-center">
|
91
|
+
<span class="material-icons text-base mr-2 ${data.active_queues > 0 ? 'text-blue-500' : 'text-gray-500'}">queue</span>
|
92
|
+
<span class="text-gray-700 dark:text-gray-300">Queues: ${data.active_queues || 0} configured</span>
|
93
|
+
</div>
|
94
|
+
</div>
|
95
|
+
`;
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Helper method to update element text
|
100
|
+
*/
|
101
|
+
updateElement(id, value) {
|
102
|
+
const element = document.getElementById(id);
|
103
|
+
if (element) {
|
104
|
+
element.textContent = value;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Show error message
|
110
|
+
*/
|
111
|
+
showError(message) {
|
112
|
+
console.error(message);
|
113
|
+
const statusContainer = document.getElementById('system-status');
|
114
|
+
if (statusContainer) {
|
115
|
+
statusContainer.innerHTML = `
|
116
|
+
<div class="text-center py-8">
|
117
|
+
<span class="material-icons text-4xl text-red-400 mb-4">error</span>
|
118
|
+
<p class="text-red-600 dark:text-red-400">${message}</p>
|
119
|
+
</div>
|
120
|
+
`;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|