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,133 @@
1
+ """
2
+ Demo tasks for Tasks Dashboard demonstration.
3
+
4
+ These tasks are used to generate test data for the dashboard
5
+ and demonstrate different queue behaviors and execution times.
6
+ """
7
+
8
+ import dramatiq
9
+ import time
10
+ import random
11
+ import logging
12
+ from datetime import datetime
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @dramatiq.actor(queue_name='default')
18
+ def quick_task(task_id: str, duration: int = 2):
19
+ """Quick processing task (2-3 seconds)."""
20
+ logger.info(f'🚀 Quick task {task_id} started at {datetime.now().strftime("%H:%M:%S")}')
21
+ time.sleep(duration)
22
+ logger.info(f'✅ Quick task {task_id} completed in {duration}s')
23
+ return f'Quick task {task_id} done in {duration}s'
24
+
25
+
26
+ @dramatiq.actor(queue_name='background')
27
+ def medium_task(task_id: str, duration: int = 5):
28
+ """Medium processing task (5-8 seconds)."""
29
+ logger.info(f'🔄 Medium task {task_id} started at {datetime.now().strftime("%H:%M:%S")}')
30
+ time.sleep(duration)
31
+ logger.info(f'✅ Medium task {task_id} completed in {duration}s')
32
+ return f'Medium task {task_id} done in {duration}s'
33
+
34
+
35
+ @dramatiq.actor(queue_name='low')
36
+ def slow_task(task_id: str, duration: int = 10):
37
+ """Slow processing task (10-15 seconds)."""
38
+ logger.info(f'🐌 Slow task {task_id} started at {datetime.now().strftime("%H:%M:%S")}')
39
+ time.sleep(duration)
40
+ logger.info(f'✅ Slow task {task_id} completed in {duration}s')
41
+ return f'Slow task {task_id} done in {duration}s'
42
+
43
+
44
+ @dramatiq.actor(queue_name='critical')
45
+ def critical_task(task_id: str, duration: int = 3):
46
+ """Critical processing task (2-4 seconds)."""
47
+ logger.info(f'🔥 Critical task {task_id} started at {datetime.now().strftime("%H:%M:%S")}')
48
+ time.sleep(duration)
49
+ logger.info(f'✅ Critical task {task_id} completed in {duration}s')
50
+ return f'Critical task {task_id} done in {duration}s'
51
+
52
+
53
+ @dramatiq.actor(queue_name='payments')
54
+ def payment_task(task_id: str, amount: float):
55
+ """Payment processing task (1-3 seconds)."""
56
+ duration = random.randint(1, 3)
57
+ logger.info(f'💳 Payment task {task_id} processing ${amount} at {datetime.now().strftime("%H:%M:%S")}')
58
+ time.sleep(duration)
59
+ logger.info(f'✅ Payment task {task_id} completed in {duration}s')
60
+ return f'Payment ${amount} processed in {duration}s'
61
+
62
+
63
+ @dramatiq.actor(queue_name='agents')
64
+ def agent_task(task_id: str, query: str):
65
+ """AI agent processing task (3-7 seconds)."""
66
+ duration = random.randint(3, 7)
67
+ logger.info(f'🤖 Agent task {task_id} processing query "{query}" at {datetime.now().strftime("%H:%M:%S")}')
68
+ time.sleep(duration)
69
+ logger.info(f'✅ Agent task {task_id} completed in {duration}s')
70
+ return f'Agent processed query "{query}" in {duration}s'
71
+
72
+
73
+ @dramatiq.actor(queue_name='high')
74
+ def priority_task(task_id: str, priority_level: str):
75
+ """High priority task (1-4 seconds)."""
76
+ duration = random.randint(1, 4)
77
+ logger.info(f'⚡ Priority task {task_id} ({priority_level}) started at {datetime.now().strftime("%H:%M:%S")}')
78
+ time.sleep(duration)
79
+ logger.info(f'✅ Priority task {task_id} completed in {duration}s')
80
+ return f'Priority task {task_id} ({priority_level}) done in {duration}s'
81
+
82
+
83
+ def generate_demo_tasks() -> int:
84
+ """
85
+ Generate a variety of demo tasks for dashboard demonstration.
86
+
87
+ Returns:
88
+ Number of tasks created
89
+ """
90
+ tasks_count = 0
91
+
92
+ # Quick tasks (3-5 tasks)
93
+ for i in range(random.randint(3, 5)):
94
+ quick_task.send(f'quick_{i+1}', random.randint(2, 3))
95
+ tasks_count += 1
96
+
97
+ # Medium tasks (2-4 tasks)
98
+ for i in range(random.randint(2, 4)):
99
+ medium_task.send(f'medium_{i+1}', random.randint(5, 8))
100
+ tasks_count += 1
101
+
102
+ # Slow tasks (1-2 tasks)
103
+ for i in range(random.randint(1, 2)):
104
+ slow_task.send(f'slow_{i+1}', random.randint(10, 15))
105
+ tasks_count += 1
106
+
107
+ # Critical tasks (2-3 tasks)
108
+ for i in range(random.randint(2, 3)):
109
+ critical_task.send(f'critical_{i+1}', random.randint(2, 4))
110
+ tasks_count += 1
111
+
112
+ # Payment tasks (1-3 tasks)
113
+ for i in range(random.randint(1, 3)):
114
+ amount = round(random.uniform(10.0, 500.0), 2)
115
+ payment_task.send(f'payment_{i+1}', amount)
116
+ tasks_count += 1
117
+
118
+ # Agent tasks (1-2 tasks)
119
+ for i in range(random.randint(1, 2)):
120
+ queries = ['analyze data', 'generate report', 'process document', 'classify content']
121
+ query = random.choice(queries)
122
+ agent_task.send(f'agent_{i+1}', query)
123
+ tasks_count += 1
124
+
125
+ # Priority tasks (1-2 tasks)
126
+ for i in range(random.randint(1, 2)):
127
+ priority_levels = ['urgent', 'high', 'critical']
128
+ priority = random.choice(priority_levels)
129
+ priority_task.send(f'priority_{i+1}', priority)
130
+ tasks_count += 1
131
+
132
+ logger.info(f"Generated {tasks_count} demo tasks across all queues")
133
+ return tasks_count
@@ -1,51 +1,48 @@
1
1
  <!-- Management Actions -->
2
- <div class="mb-8">
3
- <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
4
- <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
5
- <div class="flex items-center">
6
- <span class="material-icons text-lg mr-2 text-gray-600 dark:text-gray-400">settings</span>
7
- <h3 class="text-lg font-medium text-gray-900 dark:text-white">Management Actions</h3>
8
- </div>
2
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 mb-6">
3
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
4
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
5
+ <span class="material-icons text-primary-600 dark:text-primary-400 mr-2">settings</span>
6
+ Management Actions
7
+ </h2>
8
+ </div>
9
+
10
+ <div class="p-4">
11
+ <!-- All Actions in One Row -->
12
+ <div class="flex flex-wrap gap-3">
13
+ <!-- Primary Actions -->
14
+ <button id="simulate-data-btn"
15
+ class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded-lg border border-blue-600 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1">
16
+ <span class="material-icons text-sm mr-2">science</span>
17
+ Simulate Data
18
+ </button>
19
+
20
+ <button id="clear-test-data-btn"
21
+ class="inline-flex items-center px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white text-sm rounded-lg border border-orange-600 transition-colors focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-1">
22
+ <span class="material-icons text-sm mr-2">clear_all</span>
23
+ Clear Data
24
+ </button>
25
+
26
+ <!-- Secondary Actions -->
27
+ <button id="clear-all-queues-btn"
28
+ class="inline-flex items-center px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm rounded-lg border border-red-600 transition-colors focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-1">
29
+ <span class="material-icons text-sm mr-2">delete_sweep</span>
30
+ Clear Queues
31
+ </button>
32
+
33
+ <button id="purge-failed-tasks-btn"
34
+ class="inline-flex items-center px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg border border-purple-600 transition-colors focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-1">
35
+ <span class="material-icons text-sm mr-2">cleaning_services</span>
36
+ Purge Failed
37
+ </button>
9
38
  </div>
10
39
 
11
- <div class="p-6">
12
- <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
13
- <!-- Task Monitor -->
14
- <button onclick="showTaskMonitor()"
15
- class="flex items-center justify-center px-4 py-3 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800">
16
- <span class="material-icons text-lg mr-2">analytics</span>
17
- <span class="font-medium">Task Monitor</span>
18
- </button>
19
-
20
- <!-- Clear All Queues -->
21
- <button id="clear-all-queues-btn"
22
- class="flex items-center justify-center px-4 py-3 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800">
23
- <span class="material-icons text-lg mr-2">delete_sweep</span>
24
- <span class="font-medium">Clear All Queues</span>
25
- </button>
26
-
27
- <!-- Purge Failed Tasks -->
28
- <button id="purge-failed-tasks-btn"
29
- class="flex items-center justify-center px-4 py-3 bg-orange-600 hover:bg-orange-700 text-white rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800">
30
- <span class="material-icons text-lg mr-2">cleaning_services</span>
31
- <span class="font-medium">Purge Failed Tasks</span>
32
- </button>
33
-
34
- <!-- Admin Settings -->
35
- <a href="/admin/constance/config/"
36
- class="flex items-center justify-center px-4 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 text-decoration-none">
37
- <span class="material-icons text-lg mr-2">admin_panel_settings</span>
38
- <span class="font-medium">Admin Settings</span>
39
- </a>
40
- </div>
41
-
42
- <!-- Action Status -->
43
- <div id="management-action-status" class="mt-4 hidden">
44
- <div class="p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
45
- <div class="flex items-center">
46
- <span class="material-icons text-blue-600 dark:text-blue-400 mr-2">info</span>
47
- <span id="management-action-message" class="text-sm text-blue-800 dark:text-blue-200"></span>
48
- </div>
40
+ <!-- Action Status -->
41
+ <div id="management-action-status" class="mt-4 hidden">
42
+ <div class="p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
43
+ <div class="flex items-center">
44
+ <span class="material-icons text-blue-600 dark:text-blue-400 mr-2">info</span>
45
+ <span id="management-action-message" class="text-sm text-blue-800 dark:text-blue-200"></span>
49
46
  </div>
50
47
  </div>
51
48
  </div>
@@ -1,13 +1,14 @@
1
- <!-- Status Cards -->
2
- <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
1
+ <!-- Overview Content -->
2
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
3
+ <!-- Status Cards -->
3
4
  <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
4
5
  <div class="flex items-center">
5
6
  <div class="flex-shrink-0">
6
- <span class="material-icons text-blue-600 dark:text-blue-400 text-3xl">queue</span>
7
+ <span class="material-icons text-2xl text-blue-600 dark:text-blue-400">queue</span>
7
8
  </div>
8
9
  <div class="ml-4">
9
10
  <p class="text-sm font-medium text-gray-500 dark:text-gray-400">Active Queues</p>
10
- <p id="active-queues-count" class="text-2xl font-bold text-gray-900 dark:text-white">-</p>
11
+ <p id="active-queues-count" class="text-2xl font-semibold text-gray-900 dark:text-white">0</p>
11
12
  </div>
12
13
  </div>
13
14
  </div>
@@ -15,11 +16,11 @@
15
16
  <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
16
17
  <div class="flex items-center">
17
18
  <div class="flex-shrink-0">
18
- <span class="material-icons text-green-600 dark:text-green-400 text-3xl">engineering</span>
19
+ <span class="material-icons text-2xl text-green-600 dark:text-green-400">engineering</span>
19
20
  </div>
20
21
  <div class="ml-4">
21
- <p class="text-sm font-medium text-gray-500 dark:text-gray-400">Active Workers</p>
22
- <p id="active-workers-count" class="text-2xl font-bold text-gray-900 dark:text-white">-</p>
22
+ <p class="text-sm font-medium text-gray-500 dark:text-gray-400">Workers</p>
23
+ <p id="workers-count" class="text-2xl font-semibold text-gray-900 dark:text-white">0</p>
23
24
  </div>
24
25
  </div>
25
26
  </div>
@@ -27,11 +28,11 @@
27
28
  <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
28
29
  <div class="flex items-center">
29
30
  <div class="flex-shrink-0">
30
- <span class="material-icons text-yellow-600 dark:text-yellow-400 text-3xl">pending</span>
31
+ <span class="material-icons text-2xl text-yellow-600 dark:text-yellow-400">schedule</span>
31
32
  </div>
32
33
  <div class="ml-4">
33
34
  <p class="text-sm font-medium text-gray-500 dark:text-gray-400">Pending Tasks</p>
34
- <p id="pending-tasks-count" class="text-2xl font-bold text-gray-900 dark:text-white">-</p>
35
+ <p id="pending-tasks-count" class="text-2xl font-semibold text-gray-900 dark:text-white">0</p>
35
36
  </div>
36
37
  </div>
37
38
  </div>
@@ -39,11 +40,29 @@
39
40
  <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
40
41
  <div class="flex items-center">
41
42
  <div class="flex-shrink-0">
42
- <span class="material-icons text-red-600 dark:text-red-400 text-3xl">error</span>
43
+ <span class="material-icons text-2xl text-red-600 dark:text-red-400">error</span>
43
44
  </div>
44
45
  <div class="ml-4">
45
46
  <p class="text-sm font-medium text-gray-500 dark:text-gray-400">Failed Tasks</p>
46
- <p id="failed-tasks-count" class="text-2xl font-bold text-gray-900 dark:text-white">-</p>
47
+ <p id="failed-tasks-count" class="text-2xl font-semibold text-gray-900 dark:text-white">0</p>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <!-- System Status -->
54
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
55
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
56
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
57
+ <span class="material-icons text-primary-600 dark:text-primary-400 mr-2">monitor_heart</span>
58
+ System Status
59
+ </h2>
60
+ </div>
61
+ <div class="p-6">
62
+ <div id="system-status">
63
+ <div class="flex items-center justify-center py-12">
64
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
65
+ <span class="ml-3 text-gray-600 dark:text-gray-400">Loading status...</span>
47
66
  </div>
48
67
  </div>
49
68
  </div>
@@ -0,0 +1,19 @@
1
+ <!-- Queues Content -->
2
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
3
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
4
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
5
+ <span class="material-icons text-blue-600 dark:text-blue-400 mr-2">queue</span>
6
+ Queue Details
7
+ </h2>
8
+ <button id="refresh-queues-btn"
9
+ class="inline-flex items-center px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded-lg transition-colors">
10
+ <span class="material-icons text-sm">refresh</span>
11
+ </button>
12
+ </div>
13
+ <div class="p-6">
14
+ <div id="queues-container">
15
+ <!-- Queues will be loaded here by JavaScript -->
16
+ <p class="text-gray-600 dark:text-gray-400">Loading queues...</p>
17
+ </div>
18
+ </div>
19
+ </div>
@@ -1,26 +1,32 @@
1
1
  <!-- Tab Navigation -->
2
- <div class="mb-8">
2
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
3
3
  <div class="border-b border-gray-200 dark:border-gray-700">
4
- <nav class="-mb-px flex space-x-8" aria-label="Tabs">
5
- <button class="tab-button active border-primary-500 text-primary-600 dark:text-primary-400 whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm"
6
- data-tab="overview">
4
+ <nav class="-mb-px flex space-x-8 px-6" aria-label="Tabs">
5
+ <button data-tab="overview"
6
+ class="tab-button active whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm border-primary-500 text-primary-600 dark:text-primary-400">
7
7
  <span class="material-icons text-sm mr-2">dashboard</span>
8
8
  Overview
9
9
  </button>
10
- <button class="tab-button border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm"
11
- data-tab="queues">
10
+
11
+ <button data-tab="queues"
12
+ class="tab-button whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300">
12
13
  <span class="material-icons text-sm mr-2">queue</span>
13
14
  Queues
15
+ <span id="queues-count-badge" class="ml-2 px-2 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded-full">0</span>
14
16
  </button>
15
- <button class="tab-button border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm"
16
- data-tab="workers">
17
+
18
+ <button data-tab="workers"
19
+ class="tab-button whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300">
17
20
  <span class="material-icons text-sm mr-2">engineering</span>
18
21
  Workers
22
+ <span id="workers-count-badge" class="ml-2 px-2 py-1 text-xs bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-full">0</span>
19
23
  </button>
20
- <button class="tab-button border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm"
21
- data-tab="tasks">
24
+
25
+ <button data-tab="tasks"
26
+ class="tab-button whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300">
22
27
  <span class="material-icons text-sm mr-2">list</span>
23
28
  Tasks
29
+ <span id="tasks-count-badge" class="ml-2 px-2 py-1 text-xs bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full">0</span>
24
30
  </button>
25
31
  </nav>
26
32
  </div>
@@ -0,0 +1,51 @@
1
+ <!-- Tasks Content -->
2
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
3
+ <!-- Header -->
4
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
5
+ <div class="flex items-center justify-between mb-4">
6
+ <div class="flex items-center">
7
+ <span class="material-icons text-purple-600 dark:text-purple-400 mr-2">list</span>
8
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-white">Task Monitor</h2>
9
+ <span id="task-count-badge" class="ml-3 px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 rounded-full">0 tasks</span>
10
+ </div>
11
+
12
+ <div class="flex items-center space-x-2">
13
+ <button id="tasks-auto-refresh-toggle"
14
+ class="px-3 py-1 text-sm bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-lg hover:bg-green-200 dark:hover:bg-green-800 transition-colors">
15
+ <span class="material-icons text-sm mr-1">refresh</span>Auto: ON
16
+ </button>
17
+ <button id="refresh-tasks-btn"
18
+ class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors">
19
+ <span class="material-icons text-sm">refresh</span>
20
+ </button>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Filters -->
25
+ {% include 'tasks/widgets/task_filters.html' %}
26
+ </div>
27
+
28
+ <!-- Content -->
29
+ <div class="p-6">
30
+ <!-- Loading State -->
31
+ <div id="tasks-loading" class="hidden flex items-center justify-center py-12">
32
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600"></div>
33
+ <span class="ml-3 text-gray-600 dark:text-gray-400">Loading tasks...</span>
34
+ </div>
35
+
36
+ <!-- Empty State -->
37
+ <div id="tasks-empty" class="hidden text-center py-12">
38
+ <span class="material-icons text-6xl text-gray-400 dark:text-gray-600 mb-4">inbox</span>
39
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">No tasks found</h3>
40
+ <p class="text-gray-500 dark:text-gray-400">Try adjusting your filters or run some tasks.</p>
41
+ </div>
42
+
43
+ <!-- Task List -->
44
+ <div id="tasks-container">
45
+ {% include 'tasks/widgets/task_table.html' %}
46
+ </div>
47
+ </div>
48
+
49
+ <!-- Footer -->
50
+ {% include 'tasks/widgets/task_footer.html' %}
51
+ </div>
@@ -0,0 +1,30 @@
1
+ <!-- Workers Content -->
2
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
3
+ <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
4
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
5
+ <span class="material-icons text-green-600 dark:text-green-400 mr-2">engineering</span>
6
+ Worker Management
7
+ </h2>
8
+ <div class="flex space-x-2">
9
+ <button id="start-workers-btn"
10
+ class="inline-flex items-center px-3 py-2 bg-green-600 hover:bg-green-700 text-white text-sm rounded-lg transition-colors">
11
+ <span class="material-icons text-sm mr-1">play_arrow</span>
12
+ Start
13
+ </button>
14
+ <button id="stop-workers-btn"
15
+ class="inline-flex items-center px-3 py-2 bg-red-600 hover:bg-red-700 text-white text-sm rounded-lg transition-colors">
16
+ <span class="material-icons text-sm mr-1">stop</span>
17
+ Stop
18
+ </button>
19
+ </div>
20
+ </div>
21
+ <div class="p-6">
22
+ <div id="workers-container">
23
+ <!-- Workers will be loaded here by JavaScript -->
24
+ <div class="flex items-center justify-center py-12">
25
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
26
+ <span class="ml-3 text-gray-600 dark:text-gray-400">Loading workers...</span>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
@@ -0,0 +1,117 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="h-full">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}Tasks Dashboard{% endblock %} - Django CFG</title>
7
+
8
+ <!-- Tailwind CSS -->
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <script>
11
+ tailwind.config = {
12
+ darkMode: 'class',
13
+ theme: {
14
+ extend: {
15
+ colors: {
16
+ primary: {
17
+ 50: '#eff6ff',
18
+ 100: '#dbeafe',
19
+ 200: '#bfdbfe',
20
+ 300: '#93c5fd',
21
+ 400: '#60a5fa',
22
+ 500: '#3b82f6',
23
+ 600: '#2563eb',
24
+ 700: '#1d4ed8',
25
+ 800: '#1e40af',
26
+ 900: '#1e3a8a',
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ </script>
33
+
34
+ <!-- Material Icons -->
35
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
36
+
37
+ <!-- Custom CSS -->
38
+ {% load static %}
39
+ <link rel="stylesheet" href="{% static 'tasks/css/dashboard.css' %}">
40
+
41
+ {% block extra_css %}{% endblock %}
42
+ </head>
43
+ <body class="h-full bg-gray-50 dark:bg-gray-900">
44
+ <!-- Navigation -->
45
+ <nav class="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
46
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
47
+ <div class="flex justify-between h-16">
48
+ <!-- Logo & Title -->
49
+ <div class="flex items-center">
50
+ <div class="flex-shrink-0 flex items-center">
51
+ <span class="material-icons text-2xl text-primary-600 dark:text-primary-400 mr-3">dashboard</span>
52
+ <h1 class="text-xl font-semibold text-gray-900 dark:text-white">
53
+ {% block page_title %}Tasks Dashboard{% endblock %}
54
+ </h1>
55
+ </div>
56
+ </div>
57
+
58
+ <!-- Actions -->
59
+ <div class="flex items-center space-x-3">
60
+ {% block header_actions %}
61
+ <a href="/admin/django_dramatiq/task/"
62
+ class="inline-flex items-center px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors duration-200">
63
+ <span class="material-icons text-sm mr-2">history</span>
64
+ Task History
65
+ </a>
66
+ <a href="/admin/constance/config/"
67
+ class="inline-flex items-center px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors duration-200">
68
+ <span class="material-icons text-sm mr-2">admin_panel_settings</span>
69
+ Settings
70
+ </a>
71
+ <button id="theme-toggle"
72
+ class="group inline-flex items-center justify-center px-3 py-2 rounded-md bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
73
+ <span class="material-icons text-gray-600 dark:text-yellow-400 group-hover:text-primary-600 dark:group-hover:text-yellow-300 transition-colors duration-200">light_mode</span>
74
+ </button>
75
+ {% endblock %}
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </nav>
80
+
81
+ <!-- Main Content -->
82
+ <main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
83
+ {% block content %}{% endblock %}
84
+ </main>
85
+
86
+ <!-- Scripts -->
87
+ <script>
88
+ // Theme toggle functionality
89
+ const themeToggle = document.getElementById('theme-toggle');
90
+ const themeIcon = themeToggle?.querySelector('.material-icons');
91
+ const html = document.documentElement;
92
+
93
+ // Update theme icon
94
+ function updateThemeIcon(isDark) {
95
+ if (themeIcon) {
96
+ themeIcon.textContent = isDark ? 'dark_mode' : 'light_mode';
97
+ }
98
+ }
99
+
100
+ // Check for saved theme or default to light
101
+ const currentTheme = localStorage.getItem('theme') || 'light';
102
+ const isDark = currentTheme === 'dark';
103
+ html.classList.toggle('dark', isDark);
104
+ updateThemeIcon(isDark);
105
+
106
+ themeToggle?.addEventListener('click', () => {
107
+ const wasDark = html.classList.contains('dark');
108
+ html.classList.toggle('dark');
109
+ const nowDark = html.classList.contains('dark');
110
+ localStorage.setItem('theme', nowDark ? 'dark' : 'light');
111
+ updateThemeIcon(nowDark);
112
+ });
113
+ </script>
114
+
115
+ {% block extra_js %}{% endblock %}
116
+ </body>
117
+ </html>