django-cfg 1.2.7__py3-none-any.whl → 1.2.9__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/urls.py +2 -2
- django_cfg/modules/django_unfold/callbacks/__init__.py +9 -0
- django_cfg/modules/django_unfold/callbacks/actions.py +50 -0
- django_cfg/modules/django_unfold/callbacks/base.py +98 -0
- django_cfg/modules/django_unfold/callbacks/charts.py +224 -0
- django_cfg/modules/django_unfold/callbacks/commands.py +40 -0
- django_cfg/modules/django_unfold/callbacks/main.py +191 -0
- django_cfg/modules/django_unfold/callbacks/revolution.py +76 -0
- django_cfg/modules/django_unfold/callbacks/statistics.py +240 -0
- django_cfg/modules/django_unfold/callbacks/system.py +180 -0
- django_cfg/modules/django_unfold/callbacks/users.py +65 -0
- django_cfg/modules/django_unfold/models/config.py +10 -3
- django_cfg/modules/django_unfold/tailwind.py +68 -0
- django_cfg/templates/admin/components/action_grid.html +49 -0
- django_cfg/templates/admin/components/card.html +50 -0
- django_cfg/templates/admin/components/data_table.html +67 -0
- django_cfg/templates/admin/components/metric_card.html +39 -0
- django_cfg/templates/admin/components/modal.html +58 -0
- django_cfg/templates/admin/components/progress_bar.html +25 -0
- django_cfg/templates/admin/components/section_header.html +26 -0
- django_cfg/templates/admin/components/stat_item.html +32 -0
- django_cfg/templates/admin/components/stats_grid.html +72 -0
- django_cfg/templates/admin/components/status_badge.html +28 -0
- django_cfg/templates/admin/components/user_avatar.html +27 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +7 -7
- django_cfg/templates/admin/snippets/components/activity_tracker.html +48 -11
- django_cfg/templates/admin/snippets/components/charts_section.html +63 -13
- django_cfg/templates/admin/snippets/components/django_commands.html +196 -72
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -47
- django_cfg/templates/admin/snippets/components/recent_activity.html +28 -38
- django_cfg/templates/admin/snippets/components/recent_users_table.html +22 -53
- django_cfg/templates/admin/snippets/components/stats_cards.html +2 -66
- django_cfg/templates/admin/snippets/components/system_health.html +13 -63
- django_cfg/templates/admin/snippets/components/system_metrics.html +8 -25
- django_cfg/templates/admin/snippets/tabs/commands_tab.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +4 -4
- django_cfg/templates/admin/snippets/zones/zones_table.html +12 -33
- django_cfg/templatetags/django_cfg.py +2 -1
- {django_cfg-1.2.7.dist-info → django_cfg-1.2.9.dist-info}/METADATA +2 -1
- {django_cfg-1.2.7.dist-info → django_cfg-1.2.9.dist-info}/RECORD +44 -24
- django_cfg/modules/django_unfold/callbacks.py +0 -795
- {django_cfg-1.2.7.dist-info → django_cfg-1.2.9.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.7.dist-info → django_cfg-1.2.9.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.7.dist-info → django_cfg-1.2.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,16 +1,11 @@
|
|
1
1
|
{% load unfold %}
|
2
2
|
|
3
|
-
<!-- Charts Section -->
|
3
|
+
<!-- Charts Section using reusable components -->
|
4
4
|
<div class="mb-8 w-full">
|
5
|
-
|
6
|
-
<div class="flex items-center justify-center w-8 h-8 bg-blue-100 dark:bg-blue-900/20 rounded-lg mr-3">
|
7
|
-
<span class="material-icons text-blue-600 dark:text-blue-400 text-lg">analytics</span>
|
8
|
-
</div>
|
9
|
-
<h2 class="text-xl font-semibold text-font-important-light dark:text-font-important-dark">
|
10
|
-
Analytics Overview
|
11
|
-
</h2>
|
12
|
-
</div>
|
5
|
+
{% include 'admin/components/section_header.html' with title='Analytics Overview' icon='analytics' icon_color='blue' %}
|
13
6
|
|
7
|
+
|
8
|
+
{% if charts %}
|
14
9
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
15
10
|
<!-- User Registration Chart -->
|
16
11
|
<div class="group">
|
@@ -19,8 +14,32 @@
|
|
19
14
|
<span class="material-icons text-primary-600 dark:text-primary-500 mr-2">trending_up</span>
|
20
15
|
<span class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Growth Trends</span>
|
21
16
|
</div>
|
22
|
-
{%
|
23
|
-
|
17
|
+
{% if charts.user_registrations %}
|
18
|
+
{% component "unfold/components/chart/line.html" with data=charts.user_registrations_json height=300 %}
|
19
|
+
{% endcomponent %}
|
20
|
+
|
21
|
+
<!-- Fallback data table if chart doesn't render -->
|
22
|
+
<div class="mt-4 text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
23
|
+
<div class="flex justify-between items-center mb-2">
|
24
|
+
<span class="font-medium">Data Preview:</span>
|
25
|
+
<span>{{ charts.user_registrations.datasets.0.label }}</span>
|
26
|
+
</div>
|
27
|
+
<div class="grid grid-cols-7 gap-1 text-center">
|
28
|
+
{% for label in charts.user_registrations.labels %}
|
29
|
+
<div class="bg-base-100 dark:bg-base-700 p-1 rounded text-xs">{{ label }}</div>
|
30
|
+
{% endfor %}
|
31
|
+
</div>
|
32
|
+
<div class="grid grid-cols-7 gap-1 text-center mt-1">
|
33
|
+
{% for value in charts.user_registrations.datasets.0.data %}
|
34
|
+
<div class="bg-primary-50 dark:bg-primary-900/20 p-1 rounded text-xs font-medium">{{ value }}</div>
|
35
|
+
{% endfor %}
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
{% else %}
|
39
|
+
<div class="h-[300px] flex items-center justify-center bg-base-50 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700">
|
40
|
+
<p class="text-font-subtle-light dark:text-font-subtle-dark">No registration data available</p>
|
41
|
+
</div>
|
42
|
+
{% endif %}
|
24
43
|
{% endcomponent %}
|
25
44
|
</div>
|
26
45
|
|
@@ -31,9 +50,40 @@
|
|
31
50
|
<span class="material-icons text-green-600 dark:text-green-400 mr-2">bar_chart</span>
|
32
51
|
<span class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Activity Levels</span>
|
33
52
|
</div>
|
34
|
-
{%
|
35
|
-
|
53
|
+
{% if charts.user_activity %}
|
54
|
+
{% component "unfold/components/chart/bar.html" with data=charts.user_activity_json height=300 %}
|
55
|
+
{% endcomponent %}
|
56
|
+
|
57
|
+
<!-- Fallback data table if chart doesn't render -->
|
58
|
+
<div class="mt-4 text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
59
|
+
<div class="flex justify-between items-center mb-2">
|
60
|
+
<span class="font-medium">Data Preview:</span>
|
61
|
+
<span>{{ charts.user_activity.datasets.0.label }}</span>
|
62
|
+
</div>
|
63
|
+
<div class="grid grid-cols-7 gap-1 text-center">
|
64
|
+
{% for label in charts.user_activity.labels %}
|
65
|
+
<div class="bg-base-100 dark:bg-base-700 p-1 rounded text-xs">{{ label }}</div>
|
66
|
+
{% endfor %}
|
67
|
+
</div>
|
68
|
+
<div class="grid grid-cols-7 gap-1 text-center mt-1">
|
69
|
+
{% for value in charts.user_activity.datasets.0.data %}
|
70
|
+
<div class="bg-green-50 dark:bg-green-900/20 p-1 rounded text-xs font-medium">{{ value }}</div>
|
71
|
+
{% endfor %}
|
72
|
+
</div>
|
73
|
+
</div>
|
74
|
+
{% else %}
|
75
|
+
<div class="h-[300px] flex items-center justify-center bg-base-50 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700">
|
76
|
+
<p class="text-font-subtle-light dark:text-font-subtle-dark">No activity data available</p>
|
77
|
+
</div>
|
78
|
+
{% endif %}
|
36
79
|
{% endcomponent %}
|
37
80
|
</div>
|
38
81
|
</div>
|
82
|
+
{% else %}
|
83
|
+
<div class="bg-base-50 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700 p-8 text-center">
|
84
|
+
<span class="material-icons text-4xl text-base-400 dark:text-base-500 mb-4">analytics</span>
|
85
|
+
<h3 class="text-lg font-medium text-font-default-light dark:text-font-default-dark mb-2">No Chart Data</h3>
|
86
|
+
<p class="text-font-subtle-light dark:text-font-subtle-dark">Charts data is not available in the context</p>
|
87
|
+
</div>
|
88
|
+
{% endif %}
|
39
89
|
</div>
|
@@ -3,23 +3,46 @@
|
|
3
3
|
<!-- Django Commands Section -->
|
4
4
|
<div class="mt-8 w-full">
|
5
5
|
<!-- Header -->
|
6
|
-
<div class="flex items-center mb-6">
|
7
|
-
<div class="flex items-center
|
8
|
-
<
|
6
|
+
<div class="flex items-center justify-between mb-6">
|
7
|
+
<div class="flex items-center">
|
8
|
+
<div class="flex items-center justify-center w-8 h-8 bg-purple-100 dark:bg-purple-900/20 rounded-lg mr-3">
|
9
|
+
<span class="material-icons text-purple-600 dark:text-purple-400 text-lg">terminal</span>
|
10
|
+
</div>
|
11
|
+
<h2 class="text-xl font-semibold text-font-important-light dark:text-font-important-dark">
|
12
|
+
Django Commands
|
13
|
+
</h2>
|
14
|
+
<span class="ml-4 text-xs text-font-subtle-light dark:text-font-subtle-dark bg-base-100 dark:bg-base-800 px-2 py-1 rounded-full">
|
15
|
+
<span id="commandsCount">{{ django_commands.total_commands }}</span> commands
|
16
|
+
</span>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<!-- Search Box -->
|
20
|
+
<div class="flex items-center space-x-3">
|
21
|
+
<div class="bg-white border border-base-200 flex flex-row items-center px-3 rounded-default relative shadow-xs w-64 focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-primary-600 dark:bg-base-900 dark:border-base-700">
|
22
|
+
<span class="material-symbols-outlined md-18 text-base-400 dark:text-base-500">search</span>
|
23
|
+
<input
|
24
|
+
type="text"
|
25
|
+
id="commandSearch"
|
26
|
+
placeholder="Search commands..."
|
27
|
+
class="grow font-medium min-w-0 overflow-hidden p-2 placeholder-font-subtle-light truncate focus:outline-hidden dark:bg-base-900 dark:placeholder-font-subtle-dark dark:text-font-default-dark"
|
28
|
+
oninput="searchCommands(this.value)"
|
29
|
+
>
|
30
|
+
</div>
|
31
|
+
<button
|
32
|
+
id="clearSearch"
|
33
|
+
onclick="clearSearch()"
|
34
|
+
class="px-3 py-2 text-xs text-font-subtle-light dark:text-font-subtle-dark hover:text-font-default-light dark:hover:text-font-default-dark transition-colors hidden"
|
35
|
+
>
|
36
|
+
Clear
|
37
|
+
</button>
|
9
38
|
</div>
|
10
|
-
<h2 class="text-xl font-semibold text-font-important-light dark:text-font-important-dark">
|
11
|
-
Django Commands
|
12
|
-
</h2>
|
13
|
-
<span class="ml-auto text-xs text-font-subtle-light dark:text-font-subtle-dark bg-base-100 dark:bg-base-800 px-2 py-1 rounded-full">
|
14
|
-
{{ django_commands.total_commands }} commands
|
15
|
-
</span>
|
16
39
|
</div>
|
17
40
|
|
18
41
|
{% if django_commands.categorized %}
|
19
42
|
<!-- Commands by Category -->
|
20
43
|
<div class="space-y-4">
|
21
44
|
{% for category, commands in django_commands.categorized.items %}
|
22
|
-
<div class="bg-white dark:bg-base-900 rounded-
|
45
|
+
<div class="bg-white dark:bg-base-900 rounded-lg border border-base-200 dark:border-base-700 overflow-hidden shadow-sm mb-4">
|
23
46
|
<!-- Category Header -->
|
24
47
|
<button
|
25
48
|
onclick="toggleCategory('{{ category }}')"
|
@@ -30,7 +53,7 @@
|
|
30
53
|
<h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark capitalize">
|
31
54
|
{{ category|title }}
|
32
55
|
</h3>
|
33
|
-
<span class="ml-3 text-sm text-font-subtle-light dark:text-font-subtle-dark bg-base-100 dark:bg-base-700 px-2 py-1 rounded-
|
56
|
+
<span class="ml-3 text-sm text-font-subtle-light dark:text-font-subtle-dark bg-base-100 dark:bg-base-700 px-2 py-1 rounded-lg">
|
34
57
|
{{ commands|length }} commands
|
35
58
|
</span>
|
36
59
|
</div>
|
@@ -44,21 +67,21 @@
|
|
44
67
|
<div class="p-4">
|
45
68
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
46
69
|
{% for command in commands %}
|
47
|
-
<div class="bg-white dark:bg-base-900 rounded-
|
70
|
+
<div class="command-item bg-white dark:bg-base-900 rounded-lg border border-base-200 dark:border-base-700 hover:shadow-lg dark:hover:shadow-base-900/50 transition-all duration-200 overflow-hidden">
|
48
71
|
<!-- Card Header -->
|
49
72
|
<div class="p-4 border-b border-base-100 dark:border-base-700">
|
50
73
|
<div class="flex items-center justify-between mb-2">
|
51
|
-
<code class="text-sm font-mono bg-base-100 dark:bg-base-700 px-2 py-1 rounded-
|
74
|
+
<code class="command-name text-sm font-mono bg-base-100 dark:bg-base-700 px-2 py-1 rounded-lg text-font-default-light dark:text-font-default-dark">
|
52
75
|
{{ command.name }}
|
53
76
|
</code>
|
54
77
|
<div class="flex items-center space-x-1">
|
55
78
|
{% if command.is_core %}
|
56
|
-
<span class="text-xs bg-primary-100 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400 px-2 py-1 rounded-
|
79
|
+
<span class="text-xs bg-primary-100 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400 px-2 py-1 rounded-lg">
|
57
80
|
Core
|
58
81
|
</span>
|
59
82
|
{% endif %}
|
60
83
|
{% if command.is_custom %}
|
61
|
-
<span class="text-xs bg-indigo-100 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400 px-2 py-1 rounded-
|
84
|
+
<span class="text-xs bg-indigo-100 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400 px-2 py-1 rounded-lg">
|
62
85
|
Custom
|
63
86
|
</span>
|
64
87
|
{% endif %}
|
@@ -74,7 +97,7 @@
|
|
74
97
|
<div class="p-4">
|
75
98
|
<!-- Description -->
|
76
99
|
{% if command.description %}
|
77
|
-
<p class="text-sm text-font-default-light dark:text-font-default-dark mb-3 line-clamp-2">
|
100
|
+
<p class="command-description text-sm text-font-default-light dark:text-font-default-dark mb-3 line-clamp-2">
|
78
101
|
{{ command.description }}
|
79
102
|
</p>
|
80
103
|
{% endif %}
|
@@ -83,7 +106,7 @@
|
|
83
106
|
{% if command.usage %}
|
84
107
|
<div class="mb-3">
|
85
108
|
<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark font-medium">Usage:</span>
|
86
|
-
<code class="block text-xs font-mono bg-base-50 dark:bg-base-700 px-2 py-1 rounded-
|
109
|
+
<code class="block text-xs font-mono bg-base-50 dark:bg-base-700 px-2 py-1 rounded-lg mt-1 text-font-default-light dark:text-font-default-dark">
|
87
110
|
{{ command.usage }}
|
88
111
|
</code>
|
89
112
|
</div>
|
@@ -103,14 +126,14 @@
|
|
103
126
|
<div class="flex gap-2">
|
104
127
|
<button
|
105
128
|
onclick="copyToClipboard('python manage.py {{ command.name }}')"
|
106
|
-
class="flex-1 inline-flex items-center justify-center px-3 py-2 bg-base-100 dark:bg-base-700 hover:bg-base-200 dark:hover:bg-base-600 text-font-default-light dark:text-font-default-dark rounded-
|
129
|
+
class="flex-1 inline-flex items-center justify-center px-3 py-2 bg-base-100 dark:bg-base-700 hover:bg-base-200 dark:hover:bg-base-600 text-font-default-light dark:text-font-default-dark rounded-lg text-xs font-medium transition-colors"
|
107
130
|
title="Copy command to clipboard">
|
108
131
|
<span class="material-icons text-xs mr-1">content_copy</span>
|
109
132
|
Copy
|
110
133
|
</button>
|
111
134
|
<button
|
112
135
|
onclick="executeCommand('{{ command.name }}')"
|
113
|
-
class="flex-1 inline-flex items-center justify-center px-3 py-2 bg-green-600 hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600 text-white rounded-
|
136
|
+
class="flex-1 inline-flex items-center justify-center px-3 py-2 bg-green-600 hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600 text-white rounded-lg text-xs font-medium transition-colors"
|
114
137
|
title="Execute command">
|
115
138
|
<span class="material-icons text-xs mr-1">play_arrow</span>
|
116
139
|
Run
|
@@ -126,49 +149,6 @@
|
|
126
149
|
{% endfor %}
|
127
150
|
</div>
|
128
151
|
|
129
|
-
<!-- Summary Stats -->
|
130
|
-
<div class="mt-6 grid grid-cols-1 md:grid-cols-4 gap-4">
|
131
|
-
<div class="bg-white dark:bg-base-900 rounded-lg p-4 border border-base-200 dark:border-base-700">
|
132
|
-
<div class="flex items-center">
|
133
|
-
<span class="material-icons text-blue-600 dark:text-blue-400 mr-3">terminal</span>
|
134
|
-
<div>
|
135
|
-
<h3 class="text-lg font-semibold text-font-default-light dark:text-font-default-dark">{{ django_commands.total_commands }}</h3>
|
136
|
-
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Total Commands</p>
|
137
|
-
</div>
|
138
|
-
</div>
|
139
|
-
</div>
|
140
|
-
|
141
|
-
<div class="bg-white dark:bg-base-900 rounded-lg p-4 border border-base-200 dark:border-base-700">
|
142
|
-
<div class="flex items-center">
|
143
|
-
<span class="material-icons text-green-600 dark:text-green-400 mr-3">settings</span>
|
144
|
-
<div>
|
145
|
-
<h3 class="text-lg font-semibold text-font-default-light dark:text-font-default-dark">{{ django_commands.core_commands }}</h3>
|
146
|
-
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Core Commands</p>
|
147
|
-
</div>
|
148
|
-
</div>
|
149
|
-
</div>
|
150
|
-
|
151
|
-
<div class="bg-white dark:bg-base-900 rounded-lg p-4 border border-base-200 dark:border-base-700">
|
152
|
-
<div class="flex items-center">
|
153
|
-
<span class="material-icons text-indigo-600 dark:text-indigo-400 mr-3">apps</span>
|
154
|
-
<div>
|
155
|
-
<h3 class="text-lg font-semibold text-font-default-light dark:text-font-default-dark">{{ django_commands.custom_commands }}</h3>
|
156
|
-
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Custom Commands</p>
|
157
|
-
</div>
|
158
|
-
</div>
|
159
|
-
</div>
|
160
|
-
|
161
|
-
<div class="bg-white dark:bg-base-900 rounded-lg p-4 border border-base-200 dark:border-base-700">
|
162
|
-
<div class="flex items-center">
|
163
|
-
<span class="material-icons text-purple-600 dark:text-purple-400 mr-3">category</span>
|
164
|
-
<div>
|
165
|
-
<h3 class="text-lg font-semibold text-font-default-light dark:text-font-default-dark">{{ django_commands.categories|length }}</h3>
|
166
|
-
<p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">Categories</p>
|
167
|
-
</div>
|
168
|
-
</div>
|
169
|
-
</div>
|
170
|
-
</div>
|
171
|
-
|
172
152
|
{% else %}
|
173
153
|
<!-- No Commands -->
|
174
154
|
<div class="text-center py-12">
|
@@ -186,13 +166,13 @@
|
|
186
166
|
</div>
|
187
167
|
|
188
168
|
<!-- Command Execution Modal -->
|
189
|
-
<div id="commandModal" class="fixed inset-0 bg-
|
169
|
+
<div id="commandModal" class="fixed inset-0 bg-black/80 backdrop-blur-sm hidden z-50">
|
190
170
|
<div class="flex items-center justify-center min-h-screen p-4">
|
191
|
-
<div class="bg-white dark:bg-base-900 rounded-
|
171
|
+
<div class="bg-white dark:bg-base-900 rounded-lg shadow-2xl max-w-4xl w-full max-h-[80vh] flex flex-col border border-base-200 dark:border-base-700">
|
192
172
|
<!-- Modal Header -->
|
193
|
-
<div class="flex items-center justify-between p-4 border-b border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800
|
173
|
+
<div class="flex items-center justify-between p-4 border-b border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
|
194
174
|
<div class="flex items-center space-x-3">
|
195
|
-
<div class="flex items-center justify-center w-10 h-10 bg-primary-100 dark:bg-primary-900/20 rounded-
|
175
|
+
<div class="flex items-center justify-center w-10 h-10 bg-primary-100 dark:bg-primary-900/20 rounded-lg">
|
196
176
|
<span class="material-icons text-primary-600 dark:text-primary-400">terminal</span>
|
197
177
|
</div>
|
198
178
|
<div>
|
@@ -204,25 +184,25 @@
|
|
204
184
|
</p>
|
205
185
|
</div>
|
206
186
|
</div>
|
207
|
-
<button onclick="closeCommandModal()" class="p-2 text-font-subtle-light dark:text-font-subtle-dark hover:text-font-default-light dark:hover:text-font-default-dark hover:bg-base-100 dark:hover:bg-base-700 rounded-
|
187
|
+
<button onclick="closeCommandModal()" class="p-2 text-font-subtle-light dark:text-font-subtle-dark hover:text-font-default-light dark:hover:text-font-default-dark hover:bg-base-100 dark:hover:bg-base-700 rounded-lg transition-colors">
|
208
188
|
<span class="material-icons">close</span>
|
209
189
|
</button>
|
210
190
|
</div>
|
211
191
|
|
212
192
|
<!-- Modal Body -->
|
213
|
-
<div class="flex-1
|
214
|
-
<div id="commandOutput" class="bg-base-100 dark:bg-base-800 rounded-
|
193
|
+
<div class="flex-1 p-4" style="min-height: 0;">
|
194
|
+
<div id="commandOutput" class="bg-base-100 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700 overflow-y-auto p-4 text-sm font-mono text-font-default-light dark:text-font-default-dark whitespace-pre-wrap leading-relaxed break-words" style="height: calc(80vh - 200px); max-height: 60vh;"></div>
|
215
195
|
</div>
|
216
196
|
|
217
197
|
<!-- Modal Footer -->
|
218
|
-
<div class="flex items-center justify-between p-4 border-t border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800
|
198
|
+
<div class="flex items-center justify-between p-4 border-t border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
|
219
199
|
<div class="flex items-center space-x-3">
|
220
200
|
<div id="commandStatus" class="flex items-center">
|
221
201
|
<div class="w-3 h-3 bg-yellow-500 rounded-full mr-2 animate-pulse"></div>
|
222
202
|
<span class="text-sm font-medium text-font-default-light dark:text-font-default-dark">Executing...</span>
|
223
203
|
</div>
|
224
204
|
</div>
|
225
|
-
<button onclick="closeCommandModal()" class="px-4 py-2 bg-base-100 dark:bg-base-700 hover:bg-base-200 dark:hover:bg-base-600 text-font-default-light dark:text-font-default-dark rounded-
|
205
|
+
<button onclick="closeCommandModal()" class="px-4 py-2 bg-base-100 dark:bg-base-700 hover:bg-base-200 dark:hover:bg-base-600 text-font-default-light dark:text-font-default-dark rounded-lg transition-colors font-medium">
|
226
206
|
Close
|
227
207
|
</button>
|
228
208
|
</div>
|
@@ -230,4 +210,148 @@
|
|
230
210
|
</div>
|
231
211
|
</div>
|
232
212
|
|
233
|
-
<!--
|
213
|
+
<!-- JavaScript for command search functionality -->
|
214
|
+
<script>
|
215
|
+
function searchCommands(query) {
|
216
|
+
const searchQuery = query.toLowerCase().trim();
|
217
|
+
const categories = document.querySelectorAll('[id^="content-"]');
|
218
|
+
const clearButton = document.getElementById('clearSearch');
|
219
|
+
const commandsCount = document.getElementById('commandsCount');
|
220
|
+
let visibleCommands = 0;
|
221
|
+
|
222
|
+
// Show/hide clear button
|
223
|
+
if (searchQuery) {
|
224
|
+
clearButton.classList.remove('hidden');
|
225
|
+
} else {
|
226
|
+
clearButton.classList.add('hidden');
|
227
|
+
}
|
228
|
+
|
229
|
+
categories.forEach(category => {
|
230
|
+
const categoryName = category.id.replace('content-', '');
|
231
|
+
const commands = category.querySelectorAll('.command-item');
|
232
|
+
let categoryHasVisibleCommands = false;
|
233
|
+
|
234
|
+
commands.forEach(command => {
|
235
|
+
const commandName = command.querySelector('.command-name').textContent.toLowerCase();
|
236
|
+
const commandDesc = command.querySelector('.command-description')?.textContent.toLowerCase() || '';
|
237
|
+
|
238
|
+
if (!searchQuery || commandName.includes(searchQuery) || commandDesc.includes(searchQuery)) {
|
239
|
+
command.style.display = 'block';
|
240
|
+
categoryHasVisibleCommands = true;
|
241
|
+
visibleCommands++;
|
242
|
+
} else {
|
243
|
+
command.style.display = 'none';
|
244
|
+
}
|
245
|
+
});
|
246
|
+
|
247
|
+
// Show/hide category based on whether it has visible commands
|
248
|
+
const categoryHeader = document.querySelector(`button[onclick="toggleCategory('${categoryName}')"]`);
|
249
|
+
const categoryContainer = categoryHeader.parentElement;
|
250
|
+
|
251
|
+
if (categoryHasVisibleCommands) {
|
252
|
+
categoryContainer.style.display = 'block';
|
253
|
+
|
254
|
+
// Auto-expand categories when searching
|
255
|
+
if (searchQuery) {
|
256
|
+
category.style.display = 'block';
|
257
|
+
const icon = categoryHeader.querySelector('.material-icons');
|
258
|
+
if (icon) {
|
259
|
+
icon.textContent = 'expand_less';
|
260
|
+
icon.style.transform = 'rotate(0deg)';
|
261
|
+
}
|
262
|
+
}
|
263
|
+
} else {
|
264
|
+
categoryContainer.style.display = 'none';
|
265
|
+
}
|
266
|
+
});
|
267
|
+
|
268
|
+
// Update commands count
|
269
|
+
commandsCount.textContent = visibleCommands;
|
270
|
+
|
271
|
+
// Show "no results" message if no commands found
|
272
|
+
showNoResultsMessage(visibleCommands === 0 && searchQuery);
|
273
|
+
}
|
274
|
+
|
275
|
+
function clearSearch() {
|
276
|
+
const searchInput = document.getElementById('commandSearch');
|
277
|
+
const clearButton = document.getElementById('clearSearch');
|
278
|
+
const commandsCount = document.getElementById('commandsCount');
|
279
|
+
|
280
|
+
searchInput.value = '';
|
281
|
+
clearButton.classList.add('hidden');
|
282
|
+
|
283
|
+
// Show all commands and categories
|
284
|
+
const categories = document.querySelectorAll('[id^="content-"]');
|
285
|
+
const allCommands = document.querySelectorAll('.command-item');
|
286
|
+
|
287
|
+
categories.forEach(category => {
|
288
|
+
const categoryName = category.id.replace('content-', '');
|
289
|
+
const categoryHeader = document.querySelector(`button[onclick="toggleCategory('${categoryName}')"]`);
|
290
|
+
categoryHeader.parentElement.style.display = 'block';
|
291
|
+
// Reset to collapsed state
|
292
|
+
category.style.display = 'none';
|
293
|
+
const icon = categoryHeader.querySelector('.material-icons');
|
294
|
+
if (icon) {
|
295
|
+
icon.textContent = 'expand_less';
|
296
|
+
icon.style.transform = 'rotate(-90deg)';
|
297
|
+
}
|
298
|
+
});
|
299
|
+
|
300
|
+
allCommands.forEach(command => {
|
301
|
+
command.style.display = 'block';
|
302
|
+
});
|
303
|
+
|
304
|
+
// Reset commands count
|
305
|
+
commandsCount.textContent = '{{ django_commands.total_commands }}';
|
306
|
+
|
307
|
+
// Hide no results message
|
308
|
+
showNoResultsMessage(false);
|
309
|
+
}
|
310
|
+
|
311
|
+
function showNoResultsMessage(show) {
|
312
|
+
let noResultsDiv = document.getElementById('noSearchResults');
|
313
|
+
|
314
|
+
if (show && !noResultsDiv) {
|
315
|
+
// Create no results message
|
316
|
+
noResultsDiv = document.createElement('div');
|
317
|
+
noResultsDiv.id = 'noSearchResults';
|
318
|
+
noResultsDiv.className = 'text-center py-12';
|
319
|
+
noResultsDiv.innerHTML = `
|
320
|
+
<div class="flex flex-col items-center">
|
321
|
+
<span class="material-icons text-6xl text-base-400 dark:text-base-500 mb-4">search_off</span>
|
322
|
+
<h3 class="text-lg font-medium text-font-important-light dark:text-font-important-dark mb-2">
|
323
|
+
No Commands Found
|
324
|
+
</h3>
|
325
|
+
<p class="text-font-subtle-light dark:text-font-subtle-dark max-w-md mx-auto">
|
326
|
+
No commands match your search criteria. Try different keywords or clear the search.
|
327
|
+
</p>
|
328
|
+
</div>
|
329
|
+
`;
|
330
|
+
|
331
|
+
// Insert after the commands container
|
332
|
+
const commandsContainer = document.querySelector('.space-y-4');
|
333
|
+
commandsContainer.parentNode.insertBefore(noResultsDiv, commandsContainer.nextSibling);
|
334
|
+
} else if (!show && noResultsDiv) {
|
335
|
+
noResultsDiv.remove();
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
// Add search keyboard shortcuts
|
340
|
+
document.addEventListener('DOMContentLoaded', function() {
|
341
|
+
const searchInput = document.getElementById('commandSearch');
|
342
|
+
|
343
|
+
// Focus search with Ctrl+F or Cmd+F
|
344
|
+
document.addEventListener('keydown', function(e) {
|
345
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
346
|
+
e.preventDefault();
|
347
|
+
searchInput.focus();
|
348
|
+
}
|
349
|
+
|
350
|
+
// Clear search with Escape
|
351
|
+
if (e.key === 'Escape' && document.activeElement === searchInput) {
|
352
|
+
clearSearch();
|
353
|
+
searchInput.blur();
|
354
|
+
}
|
355
|
+
});
|
356
|
+
});
|
357
|
+
</script>
|
@@ -1,55 +1,11 @@
|
|
1
1
|
{% load unfold %}
|
2
2
|
|
3
|
-
<!-- Quick Actions Section
|
3
|
+
<!-- Quick Actions Section using reusable components -->
|
4
4
|
{% if quick_actions %}
|
5
5
|
<div class="mt-8 w-full">
|
6
|
-
|
7
|
-
<div class="flex items-center justify-center w-8 h-8 bg-amber-100 dark:bg-amber-900/20 rounded-lg mr-3">
|
8
|
-
<span class="material-icons text-amber-600 dark:text-amber-400 text-lg">flash_on</span>
|
9
|
-
</div>
|
10
|
-
<h2 class="text-xl font-semibold text-font-important-light dark:text-font-important-dark">
|
11
|
-
Quick Actions
|
12
|
-
</h2>
|
13
|
-
</div>
|
6
|
+
{% include 'admin/components/section_header.html' with title='Quick Actions' icon='flash_on' icon_color='amber' %}
|
14
7
|
|
15
|
-
|
16
|
-
{% for action in quick_actions %}
|
17
|
-
<a href="{{ action.link }}"
|
18
|
-
class="group flex items-center p-4 bg-white dark:bg-base-900 rounded-lg border border-base-200 dark:border-base-700 hover:border-primary-600 dark:hover:border-primary-500 hover:shadow-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
|
19
|
-
<div class="flex-shrink-0 mr-4">
|
20
|
-
<div class="p-2 rounded-lg " {% if action.color == 'primary' %}bg-primary-100 dark:bg-primary-900/20 group-hover:bg-primary-200 dark:group-hover:bg-primary-800/30
|
21
|
-
{% elif action.color == 'success' %}bg-green-100 dark:bg-green-900/20 group-hover:bg-green-200 dark:group-hover:bg-green-800/30
|
22
|
-
{% elif action.color == 'warning' %}bg-amber-100 dark:bg-amber-900/20 group-hover:bg-amber-200 dark:group-hover:bg-amber-800/30
|
23
|
-
{% elif action.color == 'danger' %}bg-red-100 dark:bg-red-900/20 group-hover:bg-red-200 dark:group-hover:bg-red-800/30
|
24
|
-
{% else %}bg-base-100 dark:bg-base-800 group-hover:bg-base-200 dark:group-hover:bg-base-700{% endif %}
|
25
|
-
transition-colors duration-200">
|
26
|
-
<span class="material-icons text-lg " {% if action.color == 'primary' %}text-primary-600 dark:text-primary-400
|
27
|
-
{% elif action.color == 'success' %}text-green-600 dark:text-green-400
|
28
|
-
{% elif action.color == 'warning' %}text-amber-600 dark:text-amber-400
|
29
|
-
{% elif action.color == 'danger' %}text-red-600 dark:text-red-400
|
30
|
-
{% else %}text-font-subtle-light dark:text-font-subtle-dark{% endif %}">
|
31
|
-
{{ action.icon }}
|
32
|
-
</span>
|
33
|
-
</div>
|
34
|
-
</div>
|
35
|
-
<div class="flex-1 min-w-0">
|
36
|
-
<h3 class="text-sm font-medium text-font-default-light dark:text-font-default-dark group-hover:text-primary-600 dark:group-hover:text-primary-500 transition-colors duration-200">
|
37
|
-
{{ action.title }}
|
38
|
-
</h3>
|
39
|
-
{% if action.description %}
|
40
|
-
<p class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-1 line-clamp-2">
|
41
|
-
{{ action.description }}
|
42
|
-
</p>
|
43
|
-
{% endif %}
|
44
|
-
</div>
|
45
|
-
<div class="flex-shrink-0 ml-2">
|
46
|
-
<span class="material-icons text-base-400 dark:text-base-500 group-hover:text-primary-600 dark:group-hover:text-primary-500 transition-colors duration-200">
|
47
|
-
arrow_forward
|
48
|
-
</span>
|
49
|
-
</div>
|
50
|
-
</a>
|
51
|
-
{% endfor %}
|
52
|
-
</div>
|
8
|
+
{% include 'admin/components/action_grid.html' with actions=quick_actions cols=3 xl_cols=4 %}
|
53
9
|
|
54
10
|
<!-- Additional Action Categories -->
|
55
11
|
{% if admin_actions or user_actions or system_actions %}
|
@@ -1,45 +1,35 @@
|
|
1
1
|
{% load unfold %}
|
2
2
|
|
3
|
-
<!-- Recent Activity
|
4
|
-
{% if
|
5
|
-
<div class="
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
{{ user.username }}
|
25
|
-
</p>
|
26
|
-
<p class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
27
|
-
{{ user.email }}
|
28
|
-
</p>
|
29
|
-
</div>
|
30
|
-
</div>
|
31
|
-
<div class="flex items-center space-x-2">
|
32
|
-
<span class="px-2 py-1 text-xs font-medium rounded-full " {% if user.is_active %}bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400{% else %}bg-red-100 dark:bg-red-900/20 text-red-600 dark:text-red-400{% endif %}">
|
33
|
-
{% if user.is_active %}Active{% else %}Inactive{% endif %}
|
34
|
-
</span>
|
35
|
-
<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
36
|
-
{{ user.date_joined|date:"M d, Y" }}
|
37
|
-
</span>
|
38
|
-
</div>
|
39
|
-
</div>
|
40
|
-
{% endfor %}
|
3
|
+
<!-- Recent Activity Content -->
|
4
|
+
{% if recent_users %}
|
5
|
+
<div class="space-y-3">
|
6
|
+
{% for user in recent_users %}
|
7
|
+
<div class="flex items-center justify-between p-3 bg-base-50 dark:bg-base-800 rounded-lg">
|
8
|
+
<div class="flex items-center space-x-3">
|
9
|
+
{% include 'admin/components/user_avatar.html' with name=user.username color='blue' size='8' %}
|
10
|
+
<div>
|
11
|
+
<p class="text-sm font-medium text-font-default-light dark:text-font-default-dark">
|
12
|
+
{{ user.username }}
|
13
|
+
</p>
|
14
|
+
<p class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
15
|
+
{{ user.email }}
|
16
|
+
</p>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
<div class="flex items-center space-x-2">
|
20
|
+
{% include 'admin/components/status_badge.html' with status=user.is_active|yesno:'active,inactive' %}
|
21
|
+
<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
22
|
+
{{ user.date_joined|date:"M d, Y" }}
|
23
|
+
</span>
|
41
24
|
</div>
|
42
25
|
</div>
|
26
|
+
{% endfor %}
|
27
|
+
</div>
|
28
|
+
{% else %}
|
29
|
+
<div class="flex items-center justify-center p-8 bg-base-50 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700">
|
30
|
+
<div class="text-center">
|
31
|
+
<span class="material-icons text-4xl text-base-400 dark:text-base-500 mb-2">history</span>
|
32
|
+
<p class="text-font-subtle-light dark:text-font-subtle-dark">No recent activity available</p>
|
43
33
|
</div>
|
44
34
|
</div>
|
45
35
|
{% endif %}
|