django-cfg 1.3.7__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.
Files changed (246) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.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
+ }