django-cfg 1.3.9__py3-none-any.whl → 1.3.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin/networks_admin.py +12 -1
- django_cfg/apps/payments/admin/payments_admin.py +13 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/payment_service.py +265 -38
- django_cfg/apps/payments/services/providers/base.py +209 -3
- django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
- django_cfg/apps/payments/services/providers/models/base.py +25 -2
- django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
- django_cfg/apps/payments/services/providers/registry.py +5 -5
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/views/api/currencies.py +3 -0
- django_cfg/apps/payments/views/serializers/currencies.py +18 -5
- django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/registry/core.py +4 -9
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
- django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
- django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/app_agent_diagnose.py +0 -470
- django_cfg/management/commands/app_agent_generate.py +0 -342
- django_cfg/management/commands/app_agent_info.py +0 -308
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/modules/django_app_agent/__init__.py +0 -87
- django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
- django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
- django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
- django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
- django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
- django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
- django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
- django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
- django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
- django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
- django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
- django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
- django_cfg/modules/django_app_agent/core/__init__.py +0 -33
- django_cfg/modules/django_app_agent/core/config.py +0 -300
- django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
- django_cfg/modules/django_app_agent/models/__init__.py +0 -71
- django_cfg/modules/django_app_agent/models/base.py +0 -283
- django_cfg/modules/django_app_agent/models/context.py +0 -496
- django_cfg/modules/django_app_agent/models/enums.py +0 -481
- django_cfg/modules/django_app_agent/models/requests.py +0 -500
- django_cfg/modules/django_app_agent/models/responses.py +0 -585
- django_cfg/modules/django_app_agent/pytest.ini +0 -6
- django_cfg/modules/django_app_agent/services/__init__.py +0 -42
- django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
- django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
- django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
- django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
- django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
- django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
- django_cfg/modules/django_app_agent/services/base.py +0 -437
- django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
- django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
- django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
- django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
- django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
- django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
- django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
- django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
- django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
- django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
- django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
- django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
- django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
- django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
- django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
- django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
- django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
- django_cfg/modules/django_app_agent/services/report_service.py +0 -332
- django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
- django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
- django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
- django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
- django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
- django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
- django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
- django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
- django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
- django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
- django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
- django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
- django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
- django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
- django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
- django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
- django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
- django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
- django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
- django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
- django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
- django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
- django_cfg/modules/django_app_agent/ui/cli.py +0 -419
- django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
- django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
- django_cfg/modules/django_app_agent/utils/logging.py +0 -360
- django_cfg/modules/django_app_agent/utils/validation.py +0 -417
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,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-
|
3
|
-
<div class="
|
4
|
-
<
|
5
|
-
<
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
<
|
15
|
-
|
16
|
-
<span class="
|
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>
|
django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html}
RENAMED
@@ -1,13 +1,14 @@
|
|
1
|
-
<!--
|
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
|
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-
|
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
|
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">
|
22
|
-
<p id="
|
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
|
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-
|
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
|
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-
|
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="
|
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
|
6
|
-
|
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
|
-
|
11
|
-
|
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
|
-
|
16
|
-
|
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
|
-
|
21
|
-
|
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>
|