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,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
+ }
@@ -0,0 +1,10 @@
1
+ """
2
+ Tasks package for Django CFG Tasks app.
3
+
4
+ This package contains Dramatiq actors for background task processing.
5
+ """
6
+
7
+ # Import all task modules to ensure actors are registered
8
+ from . import demo_tasks
9
+
10
+ __all__ = ['demo_tasks']