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,120 @@
|
|
1
|
+
/**
|
2
|
+
* Queues Dashboard Module
|
3
|
+
* Handles queues tab functionality
|
4
|
+
*/
|
5
|
+
export class QueuesModule {
|
6
|
+
constructor(api, dashboard) {
|
7
|
+
this.api = api;
|
8
|
+
this.dashboard = dashboard;
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Load queues data
|
13
|
+
*/
|
14
|
+
async loadData() {
|
15
|
+
try {
|
16
|
+
console.log('Loading queues data...');
|
17
|
+
|
18
|
+
const response = await this.api.getQueueStatus();
|
19
|
+
const data = response.data || response;
|
20
|
+
|
21
|
+
this.renderQueuesData(data);
|
22
|
+
|
23
|
+
} catch (error) {
|
24
|
+
console.error('Failed to load queues data:', error);
|
25
|
+
this.showError('Failed to load queues data');
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Render queues data
|
31
|
+
*/
|
32
|
+
renderQueuesData(data) {
|
33
|
+
const container = document.getElementById('queues-container');
|
34
|
+
if (!container) return;
|
35
|
+
|
36
|
+
if (!data.queues || Object.keys(data.queues).length === 0) {
|
37
|
+
container.innerHTML = `
|
38
|
+
<div class="text-center py-8">
|
39
|
+
<span class="material-icons text-4xl text-gray-400 mb-4">queue</span>
|
40
|
+
<p class="text-gray-500 dark:text-gray-400">No active queues found</p>
|
41
|
+
<p class="text-sm text-gray-400 dark:text-gray-500 mt-2">Queues will appear here when tasks are processed</p>
|
42
|
+
</div>
|
43
|
+
`;
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
let html = '<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">';
|
48
|
+
|
49
|
+
Object.entries(data.queues).forEach(([queueName, queueInfo]) => {
|
50
|
+
const totalTasks = (queueInfo.pending || 0) + (queueInfo.failed || 0) + (queueInfo.processed || 0);
|
51
|
+
|
52
|
+
html += `
|
53
|
+
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 border border-gray-200 dark:border-gray-600 hover:shadow-md transition-shadow">
|
54
|
+
<div class="flex items-center justify-between mb-3">
|
55
|
+
<div class="flex items-center">
|
56
|
+
<span class="material-icons text-blue-600 dark:text-blue-400 mr-2">queue</span>
|
57
|
+
<h4 class="font-medium text-gray-900 dark:text-white">${queueName}</h4>
|
58
|
+
</div>
|
59
|
+
<span class="px-2 py-1 text-xs ${totalTasks > 0 ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' : 'bg-gray-100 dark:bg-gray-600 text-gray-600 dark:text-gray-300'} rounded-full">
|
60
|
+
${totalTasks > 0 ? 'Active' : 'Idle'}
|
61
|
+
</span>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
<div class="space-y-2 text-sm">
|
65
|
+
<div class="flex justify-between">
|
66
|
+
<span class="text-gray-600 dark:text-gray-400">Pending:</span>
|
67
|
+
<span class="font-medium text-yellow-600 dark:text-yellow-400">${queueInfo.pending || 0}</span>
|
68
|
+
</div>
|
69
|
+
<div class="flex justify-between">
|
70
|
+
<span class="text-gray-600 dark:text-gray-400">Failed:</span>
|
71
|
+
<span class="font-medium text-red-600 dark:text-red-400">${queueInfo.failed || 0}</span>
|
72
|
+
</div>
|
73
|
+
<div class="flex justify-between">
|
74
|
+
<span class="text-gray-600 dark:text-gray-400">Processed:</span>
|
75
|
+
<span class="font-medium text-green-600 dark:text-green-400">${queueInfo.processed || 0}</span>
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
|
79
|
+
${totalTasks > 0 ? `
|
80
|
+
<div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600">
|
81
|
+
<div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
|
82
|
+
<div class="bg-gradient-to-r from-green-400 to-blue-500 h-2 rounded-full transition-all duration-300"
|
83
|
+
style="width: ${Math.min(100, (queueInfo.processed || 0) / totalTasks * 100)}%"></div>
|
84
|
+
</div>
|
85
|
+
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400 mt-1">
|
86
|
+
<span>Progress</span>
|
87
|
+
<span>${Math.round((queueInfo.processed || 0) / totalTasks * 100)}%</span>
|
88
|
+
</div>
|
89
|
+
</div>
|
90
|
+
` : ''}
|
91
|
+
|
92
|
+
<div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600">
|
93
|
+
<div class="flex items-center text-xs text-gray-500 dark:text-gray-400">
|
94
|
+
<span class="material-icons text-sm mr-1">schedule</span>
|
95
|
+
Last activity: ${queueInfo.last_activity ? new Date(queueInfo.last_activity).toLocaleTimeString() : 'Never'}
|
96
|
+
</div>
|
97
|
+
</div>
|
98
|
+
</div>
|
99
|
+
`;
|
100
|
+
});
|
101
|
+
|
102
|
+
html += '</div>';
|
103
|
+
container.innerHTML = html;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Show error message
|
108
|
+
*/
|
109
|
+
showError(message) {
|
110
|
+
const container = document.getElementById('queues-container');
|
111
|
+
if (container) {
|
112
|
+
container.innerHTML = `
|
113
|
+
<div class="text-center py-8">
|
114
|
+
<span class="material-icons text-4xl text-red-400 mb-4">error</span>
|
115
|
+
<p class="text-red-600 dark:text-red-400">${message}</p>
|
116
|
+
</div>
|
117
|
+
`;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,350 @@
|
|
1
|
+
/**
|
2
|
+
* Tasks Dashboard Module
|
3
|
+
* Handles tasks tab functionality with filtering
|
4
|
+
*/
|
5
|
+
export class TasksModule {
|
6
|
+
constructor(api, dashboard) {
|
7
|
+
this.api = api;
|
8
|
+
this.dashboard = dashboard; // Reference to main dashboard for badge updates
|
9
|
+
this.allTasks = [];
|
10
|
+
this.filteredTasks = [];
|
11
|
+
this.filters = {
|
12
|
+
status: '',
|
13
|
+
queue: '',
|
14
|
+
search: ''
|
15
|
+
};
|
16
|
+
this.setupEventListeners();
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Setup event listeners for filters
|
21
|
+
*/
|
22
|
+
setupEventListeners() {
|
23
|
+
// Task filters
|
24
|
+
document.getElementById('task-status-filter')?.addEventListener('change', (e) => {
|
25
|
+
this.filters.status = e.target.value;
|
26
|
+
this.applyFilters();
|
27
|
+
});
|
28
|
+
|
29
|
+
document.getElementById('task-queue-filter')?.addEventListener('change', (e) => {
|
30
|
+
this.filters.queue = e.target.value;
|
31
|
+
this.applyFilters();
|
32
|
+
});
|
33
|
+
|
34
|
+
document.getElementById('task-search-input')?.addEventListener('input', (e) => {
|
35
|
+
this.filters.search = e.target.value.toLowerCase();
|
36
|
+
this.applyFilters();
|
37
|
+
});
|
38
|
+
|
39
|
+
// Task actions
|
40
|
+
document.getElementById('clear-completed-tasks')?.addEventListener('click', () => this.clearCompletedTasks());
|
41
|
+
document.getElementById('export-tasks-csv')?.addEventListener('click', () => this.exportTasks());
|
42
|
+
|
43
|
+
// Auto-refresh toggle
|
44
|
+
document.getElementById('tasks-auto-refresh-toggle')?.addEventListener('click', () => this.toggleAutoRefresh());
|
45
|
+
document.getElementById('refresh-tasks-btn')?.addEventListener('click', () => this.loadData());
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Load tasks data
|
50
|
+
*/
|
51
|
+
async loadData() {
|
52
|
+
try {
|
53
|
+
console.log('Loading tasks data...');
|
54
|
+
|
55
|
+
const response = await this.api.getTaskList();
|
56
|
+
const data = response.data || response;
|
57
|
+
|
58
|
+
if (data.tasks && data.tasks.length > 0) {
|
59
|
+
this.allTasks = data.tasks;
|
60
|
+
} else {
|
61
|
+
// Generate mock data for demonstration
|
62
|
+
this.allTasks = this.generateMockTasks();
|
63
|
+
}
|
64
|
+
|
65
|
+
// Apply current filters and render
|
66
|
+
this.applyFilters();
|
67
|
+
this.updateLastUpdateTime();
|
68
|
+
|
69
|
+
} catch (error) {
|
70
|
+
console.error('Failed to load task data:', error);
|
71
|
+
this.allTasks = this.generateMockTasks();
|
72
|
+
this.applyFilters();
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
/**
|
77
|
+
* Generate mock tasks for demonstration
|
78
|
+
*/
|
79
|
+
generateMockTasks() {
|
80
|
+
const statuses = ['pending', 'running', 'completed', 'failed'];
|
81
|
+
const queues = ['default', 'high', 'low', 'background', 'payments', 'critical'];
|
82
|
+
const actors = ['process_document_async', 'send_notification', 'cleanup_old_files', 'generate_report', 'sync_data', 'send_email_task'];
|
83
|
+
|
84
|
+
return Array.from({ length: 20 }, (_, i) => ({
|
85
|
+
id: `task_${i + 1}`,
|
86
|
+
actor_name: actors[Math.floor(Math.random() * actors.length)],
|
87
|
+
status: statuses[Math.floor(Math.random() * statuses.length)],
|
88
|
+
queue_name: queues[Math.floor(Math.random() * queues.length)],
|
89
|
+
created_at: new Date(Date.now() - Math.random() * 86400000).toISOString(),
|
90
|
+
updated_at: new Date(Date.now() - Math.random() * 3600000).toISOString(),
|
91
|
+
message_id: `msg_${String(i + 1).padStart(3, '0')}`,
|
92
|
+
progress: Math.random() > 0.7 ? Math.floor(Math.random() * 100) : null
|
93
|
+
}));
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Apply filters to tasks
|
98
|
+
*/
|
99
|
+
applyFilters() {
|
100
|
+
this.filteredTasks = this.allTasks.filter(task => {
|
101
|
+
const matchesStatus = !this.filters.status || this.mapStatus(task.status) === this.filters.status;
|
102
|
+
const matchesQueue = !this.filters.queue || (task.queue_name || task.queue) === this.filters.queue;
|
103
|
+
const matchesSearch = !this.filters.search ||
|
104
|
+
task.actor_name.toLowerCase().includes(this.filters.search) ||
|
105
|
+
String(task.id).toLowerCase().includes(this.filters.search);
|
106
|
+
|
107
|
+
return matchesStatus && matchesQueue && matchesSearch;
|
108
|
+
});
|
109
|
+
|
110
|
+
this.renderTasks();
|
111
|
+
this.updateTaskCounts();
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Map task status to display status
|
116
|
+
*/
|
117
|
+
mapStatus(status) {
|
118
|
+
const statusMap = {
|
119
|
+
'enqueued': 'pending',
|
120
|
+
'delayed': 'pending',
|
121
|
+
'pending': 'pending',
|
122
|
+
'running': 'running',
|
123
|
+
'done': 'completed',
|
124
|
+
'completed': 'completed',
|
125
|
+
'failed': 'failed',
|
126
|
+
'skipped': 'completed'
|
127
|
+
};
|
128
|
+
|
129
|
+
return statusMap[status?.toLowerCase()] || 'pending';
|
130
|
+
}
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Render tasks table
|
134
|
+
*/
|
135
|
+
renderTasks() {
|
136
|
+
const tableBody = document.querySelector('#task-table-body');
|
137
|
+
if (!tableBody) return;
|
138
|
+
|
139
|
+
// Show/hide empty state
|
140
|
+
const emptyState = document.getElementById('tasks-empty');
|
141
|
+
const loadingState = document.getElementById('tasks-loading');
|
142
|
+
|
143
|
+
loadingState?.classList.add('hidden');
|
144
|
+
|
145
|
+
if (this.filteredTasks.length === 0) {
|
146
|
+
emptyState?.classList.remove('hidden');
|
147
|
+
tableBody.innerHTML = '';
|
148
|
+
return;
|
149
|
+
}
|
150
|
+
|
151
|
+
emptyState?.classList.add('hidden');
|
152
|
+
tableBody.innerHTML = '';
|
153
|
+
|
154
|
+
this.filteredTasks.forEach(task => {
|
155
|
+
const row = this.createTaskRow(task);
|
156
|
+
tableBody.appendChild(row);
|
157
|
+
});
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Create task row element
|
162
|
+
*/
|
163
|
+
createTaskRow(task) {
|
164
|
+
const row = document.createElement('tr');
|
165
|
+
row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors';
|
166
|
+
row.setAttribute('data-task-id', task.id);
|
167
|
+
|
168
|
+
const displayStatus = this.mapStatus(task.status);
|
169
|
+
const statusConfig = this.getStatusConfig(displayStatus);
|
170
|
+
|
171
|
+
row.innerHTML = `
|
172
|
+
<td class="px-4 py-3 whitespace-nowrap">
|
173
|
+
<div class="flex items-center space-x-2">
|
174
|
+
<span class="material-icons text-sm ${statusConfig.iconColor}">${statusConfig.icon}</span>
|
175
|
+
<span class="px-2 py-1 text-xs font-medium rounded-full ${statusConfig.color}">
|
176
|
+
${statusConfig.label}
|
177
|
+
</span>
|
178
|
+
</div>
|
179
|
+
${displayStatus === 'running' && task.progress ? `
|
180
|
+
<div class="mt-1 w-16 bg-gray-200 dark:bg-gray-600 rounded-full h-1">
|
181
|
+
<div class="bg-blue-600 h-1 rounded-full transition-all duration-300" style="width: ${task.progress}%"></div>
|
182
|
+
</div>
|
183
|
+
` : ''}
|
184
|
+
</td>
|
185
|
+
<td class="px-4 py-3">
|
186
|
+
<div class="text-sm font-medium text-gray-900 dark:text-white">${task.actor_name}</div>
|
187
|
+
<div class="text-xs text-gray-500 dark:text-gray-400 font-mono">${String(task.id).substring(0, 8)}...</div>
|
188
|
+
</td>
|
189
|
+
<td class="px-4 py-3 whitespace-nowrap">
|
190
|
+
<span class="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 rounded-full">
|
191
|
+
${task.queue_name || task.queue || 'unknown'}
|
192
|
+
</span>
|
193
|
+
</td>
|
194
|
+
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
195
|
+
${this.calculateDuration(task.created_at, task.updated_at)}
|
196
|
+
</td>
|
197
|
+
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
198
|
+
${new Date(task.updated_at).toLocaleTimeString()}
|
199
|
+
</td>
|
200
|
+
<td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
|
201
|
+
<div class="flex items-center justify-end space-x-1">
|
202
|
+
${displayStatus === 'failed' ? `
|
203
|
+
<button class="px-2 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
|
204
|
+
Retry
|
205
|
+
</button>
|
206
|
+
` : ''}
|
207
|
+
<button class="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" title="View details">
|
208
|
+
<span class="material-icons text-sm">info</span>
|
209
|
+
</button>
|
210
|
+
</div>
|
211
|
+
</td>
|
212
|
+
`;
|
213
|
+
|
214
|
+
return row;
|
215
|
+
}
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Get status configuration
|
219
|
+
*/
|
220
|
+
getStatusConfig(status) {
|
221
|
+
const configs = {
|
222
|
+
pending: {
|
223
|
+
icon: 'schedule',
|
224
|
+
label: 'PENDING',
|
225
|
+
color: 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200',
|
226
|
+
iconColor: 'text-yellow-500'
|
227
|
+
},
|
228
|
+
running: {
|
229
|
+
icon: 'play_circle',
|
230
|
+
label: 'RUNNING',
|
231
|
+
color: 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200',
|
232
|
+
iconColor: 'text-blue-500'
|
233
|
+
},
|
234
|
+
completed: {
|
235
|
+
icon: 'check_circle',
|
236
|
+
label: 'COMPLETED',
|
237
|
+
color: 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200',
|
238
|
+
iconColor: 'text-green-500'
|
239
|
+
},
|
240
|
+
failed: {
|
241
|
+
icon: 'error',
|
242
|
+
label: 'FAILED',
|
243
|
+
color: 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200',
|
244
|
+
iconColor: 'text-red-500'
|
245
|
+
}
|
246
|
+
};
|
247
|
+
|
248
|
+
return configs[status] || configs.pending;
|
249
|
+
}
|
250
|
+
|
251
|
+
/**
|
252
|
+
* Update task counts in footer
|
253
|
+
*/
|
254
|
+
updateTaskCounts() {
|
255
|
+
const counts = {
|
256
|
+
completed: this.filteredTasks.filter(t => ['completed', 'done'].includes(this.mapStatus(t.status))).length,
|
257
|
+
running: this.filteredTasks.filter(t => this.mapStatus(t.status) === 'running').length,
|
258
|
+
pending: this.filteredTasks.filter(t => this.mapStatus(t.status) === 'pending').length,
|
259
|
+
failed: this.filteredTasks.filter(t => this.mapStatus(t.status) === 'failed').length
|
260
|
+
};
|
261
|
+
|
262
|
+
document.getElementById('completed-count').textContent = counts.completed;
|
263
|
+
document.getElementById('running-count').textContent = counts.running;
|
264
|
+
document.getElementById('pending-count').textContent = counts.pending;
|
265
|
+
document.getElementById('failed-count').textContent = counts.failed;
|
266
|
+
|
267
|
+
// Update task count badge
|
268
|
+
const badge = document.getElementById('task-count-badge');
|
269
|
+
if (badge) {
|
270
|
+
const filteredCount = this.filteredTasks.length;
|
271
|
+
const totalCount = this.allTasks.length;
|
272
|
+
badge.textContent = filteredCount === totalCount ?
|
273
|
+
`${totalCount} tasks` :
|
274
|
+
`${filteredCount} of ${totalCount} tasks`;
|
275
|
+
}
|
276
|
+
|
277
|
+
// Update tab badge
|
278
|
+
const tabBadge = document.querySelector('#tasks-count-badge');
|
279
|
+
if (tabBadge) {
|
280
|
+
tabBadge.textContent = this.allTasks.length;
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
/**
|
285
|
+
* Calculate duration between dates
|
286
|
+
*/
|
287
|
+
calculateDuration(start, end) {
|
288
|
+
if (!start || !end) return '--';
|
289
|
+
const diff = new Date(end) - new Date(start);
|
290
|
+
if (diff < 60000) return `${Math.floor(diff / 1000)}s`;
|
291
|
+
if (diff < 3600000) return `${Math.floor(diff / 60000)}m`;
|
292
|
+
return `${Math.floor(diff / 3600000)}h`;
|
293
|
+
}
|
294
|
+
|
295
|
+
/**
|
296
|
+
* Update last update time
|
297
|
+
*/
|
298
|
+
updateLastUpdateTime() {
|
299
|
+
const timeElement = document.getElementById('tasks-last-update-time');
|
300
|
+
if (timeElement) {
|
301
|
+
timeElement.textContent = new Date().toLocaleTimeString();
|
302
|
+
}
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Task actions
|
307
|
+
*/
|
308
|
+
clearCompletedTasks() {
|
309
|
+
if (!confirm('Are you sure you want to clear all completed tasks?')) {
|
310
|
+
return;
|
311
|
+
}
|
312
|
+
console.log('Clearing completed tasks...');
|
313
|
+
}
|
314
|
+
|
315
|
+
exportTasks() {
|
316
|
+
const csv = this.tasksToCSV(this.filteredTasks);
|
317
|
+
this.downloadCSV(csv, 'tasks-export.csv');
|
318
|
+
}
|
319
|
+
|
320
|
+
tasksToCSV(tasks) {
|
321
|
+
const headers = ['ID', 'Actor', 'Status', 'Queue', 'Created', 'Updated', 'Duration'];
|
322
|
+
const rows = tasks.map(task => [
|
323
|
+
task.id,
|
324
|
+
task.actor_name,
|
325
|
+
task.status,
|
326
|
+
task.queue_name || task.queue || 'unknown',
|
327
|
+
new Date(task.created_at).toLocaleString(),
|
328
|
+
new Date(task.updated_at).toLocaleString(),
|
329
|
+
this.calculateDuration(task.created_at, task.updated_at)
|
330
|
+
]);
|
331
|
+
|
332
|
+
return [headers, ...rows].map(row =>
|
333
|
+
row.map(field => `"${field}"`).join(',')
|
334
|
+
).join('\n');
|
335
|
+
}
|
336
|
+
|
337
|
+
downloadCSV(csv, filename) {
|
338
|
+
const blob = new Blob([csv], { type: 'text/csv' });
|
339
|
+
const url = window.URL.createObjectURL(blob);
|
340
|
+
const a = document.createElement('a');
|
341
|
+
a.href = url;
|
342
|
+
a.download = filename;
|
343
|
+
a.click();
|
344
|
+
window.URL.revokeObjectURL(url);
|
345
|
+
}
|
346
|
+
|
347
|
+
toggleAutoRefresh() {
|
348
|
+
console.log('Toggling auto-refresh...');
|
349
|
+
}
|
350
|
+
}
|
@@ -0,0 +1,169 @@
|
|
1
|
+
/**
|
2
|
+
* Workers Dashboard Module
|
3
|
+
* Handles workers tab functionality
|
4
|
+
*/
|
5
|
+
export class WorkersModule {
|
6
|
+
constructor(api, dashboard) {
|
7
|
+
this.api = api;
|
8
|
+
this.dashboard = dashboard;
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Load workers data
|
13
|
+
*/
|
14
|
+
async loadData() {
|
15
|
+
try {
|
16
|
+
console.log('Loading workers data...');
|
17
|
+
|
18
|
+
const response = await this.api.getWorkersList();
|
19
|
+
const data = response.data || response;
|
20
|
+
|
21
|
+
this.renderWorkersData(data);
|
22
|
+
|
23
|
+
} catch (error) {
|
24
|
+
console.error('Failed to load workers data:', error);
|
25
|
+
this.showError('Failed to load workers data');
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Render workers data
|
31
|
+
*/
|
32
|
+
renderWorkersData(data) {
|
33
|
+
const container = document.getElementById('workers-container');
|
34
|
+
if (!container) return;
|
35
|
+
|
36
|
+
// Check if we have workers data
|
37
|
+
let workers = [];
|
38
|
+
if (data.workers && Array.isArray(data.workers)) {
|
39
|
+
workers = data.workers;
|
40
|
+
} else if (data.workers && typeof data.workers === 'number' && data.workers > 0) {
|
41
|
+
// If we just have a count, generate mock worker data
|
42
|
+
workers = Array.from({ length: data.workers }, (_, i) => ({
|
43
|
+
id: i + 1,
|
44
|
+
pid: Math.floor(Math.random() * 10000) + 1000,
|
45
|
+
threads: 2,
|
46
|
+
tasks_processed: Math.floor(Math.random() * 100),
|
47
|
+
started_at: new Date(Date.now() - Math.random() * 86400000).toISOString(),
|
48
|
+
status: 'active'
|
49
|
+
}));
|
50
|
+
}
|
51
|
+
|
52
|
+
if (workers.length === 0) {
|
53
|
+
container.innerHTML = `
|
54
|
+
<div class="text-center py-8">
|
55
|
+
<span class="material-icons text-4xl text-gray-400 mb-4">engineering</span>
|
56
|
+
<p class="text-gray-500 dark:text-gray-400">No active workers found</p>
|
57
|
+
<p class="text-sm text-gray-400 dark:text-gray-500 mt-2">Start workers to see them here</p>
|
58
|
+
<div class="mt-4">
|
59
|
+
<button class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded-lg transition-colors">
|
60
|
+
<span class="material-icons text-sm mr-1">play_arrow</span>
|
61
|
+
Start Workers
|
62
|
+
</button>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
`;
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
|
69
|
+
let html = '<div class="grid grid-cols-1 md:grid-cols-2 gap-4">';
|
70
|
+
|
71
|
+
workers.forEach((worker, index) => {
|
72
|
+
const uptime = this.calculateUptime(worker.started_at);
|
73
|
+
const isActive = worker.status === 'active' || !worker.status;
|
74
|
+
|
75
|
+
html += `
|
76
|
+
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 border border-gray-200 dark:border-gray-600 hover:shadow-md transition-shadow">
|
77
|
+
<div class="flex items-center justify-between mb-3">
|
78
|
+
<div class="flex items-center">
|
79
|
+
<span class="material-icons ${isActive ? 'text-green-600 dark:text-green-400' : 'text-gray-400'} mr-2">engineering</span>
|
80
|
+
<h4 class="font-medium text-gray-900 dark:text-white">Worker ${worker.id || index + 1}</h4>
|
81
|
+
</div>
|
82
|
+
<div class="flex items-center">
|
83
|
+
<div class="w-2 h-2 ${isActive ? 'bg-green-500' : 'bg-gray-400'} rounded-full mr-2"></div>
|
84
|
+
<span class="px-2 py-1 text-xs ${isActive ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' : 'bg-gray-100 dark:bg-gray-600 text-gray-600 dark:text-gray-300'} rounded-full">
|
85
|
+
${isActive ? 'Active' : 'Inactive'}
|
86
|
+
</span>
|
87
|
+
</div>
|
88
|
+
</div>
|
89
|
+
|
90
|
+
<div class="space-y-2 text-sm">
|
91
|
+
<div class="flex justify-between">
|
92
|
+
<span class="text-gray-600 dark:text-gray-400">Process ID:</span>
|
93
|
+
<span class="font-mono text-gray-900 dark:text-white">${worker.pid || 'N/A'}</span>
|
94
|
+
</div>
|
95
|
+
<div class="flex justify-between">
|
96
|
+
<span class="text-gray-600 dark:text-gray-400">Threads:</span>
|
97
|
+
<span class="font-medium text-blue-600 dark:text-blue-400">${worker.threads || 2}</span>
|
98
|
+
</div>
|
99
|
+
<div class="flex justify-between">
|
100
|
+
<span class="text-gray-600 dark:text-gray-400">Tasks Processed:</span>
|
101
|
+
<span class="font-medium text-green-600 dark:text-green-400">${worker.tasks_processed || 0}</span>
|
102
|
+
</div>
|
103
|
+
<div class="flex justify-between">
|
104
|
+
<span class="text-gray-600 dark:text-gray-400">Uptime:</span>
|
105
|
+
<span class="font-medium text-purple-600 dark:text-purple-400">${uptime}</span>
|
106
|
+
</div>
|
107
|
+
</div>
|
108
|
+
|
109
|
+
<div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600">
|
110
|
+
<div class="flex items-center justify-between">
|
111
|
+
<div class="flex items-center text-xs text-gray-500 dark:text-gray-400">
|
112
|
+
<span class="material-icons text-sm mr-1">schedule</span>
|
113
|
+
Started: ${worker.started_at ? new Date(worker.started_at).toLocaleString() : 'Unknown'}
|
114
|
+
</div>
|
115
|
+
<div class="flex space-x-1">
|
116
|
+
<button class="p-1 text-gray-400 hover:text-blue-600 transition-colors" title="View details">
|
117
|
+
<span class="material-icons text-sm">info</span>
|
118
|
+
</button>
|
119
|
+
<button class="p-1 text-gray-400 hover:text-red-600 transition-colors" title="Stop worker">
|
120
|
+
<span class="material-icons text-sm">stop</span>
|
121
|
+
</button>
|
122
|
+
</div>
|
123
|
+
</div>
|
124
|
+
</div>
|
125
|
+
</div>
|
126
|
+
`;
|
127
|
+
});
|
128
|
+
|
129
|
+
html += '</div>';
|
130
|
+
container.innerHTML = html;
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Calculate uptime from start time
|
135
|
+
*/
|
136
|
+
calculateUptime(startTime) {
|
137
|
+
if (!startTime) return 'Unknown';
|
138
|
+
|
139
|
+
const start = new Date(startTime);
|
140
|
+
const now = new Date();
|
141
|
+
const diff = now - start;
|
142
|
+
|
143
|
+
const hours = Math.floor(diff / (1000 * 60 * 60));
|
144
|
+
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
145
|
+
|
146
|
+
if (hours > 0) {
|
147
|
+
return `${hours}h ${minutes}m`;
|
148
|
+
} else if (minutes > 0) {
|
149
|
+
return `${minutes}m`;
|
150
|
+
} else {
|
151
|
+
return 'Just started';
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
/**
|
156
|
+
* Show error message
|
157
|
+
*/
|
158
|
+
showError(message) {
|
159
|
+
const container = document.getElementById('workers-container');
|
160
|
+
if (container) {
|
161
|
+
container.innerHTML = `
|
162
|
+
<div class="text-center py-8">
|
163
|
+
<span class="material-icons text-4xl text-red-400 mb-4">error</span>
|
164
|
+
<p class="text-red-600 dark:text-red-400">${message}</p>
|
165
|
+
</div>
|
166
|
+
`;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|