django-cfg 1.4.75__py3-none-any.whl → 1.4.76__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.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (52) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/agents/__init__.py +1 -1
  3. django_cfg/apps/agents/integration/registry.py +1 -1
  4. django_cfg/apps/agents/patterns/content_agents.py +1 -1
  5. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +1 -1
  6. django_cfg/apps/centrifugo/views/dashboard.py +13 -0
  7. django_cfg/apps/centrifugo/views/testing_api.py +74 -15
  8. django_cfg/apps/tasks/views/dashboard.py +4 -4
  9. django_cfg/core/generation/integration_generators/api.py +5 -2
  10. django_cfg/management/commands/check_endpoints.py +1 -1
  11. django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +2 -2
  12. django_cfg/modules/django_unfold/callbacks/main.py +27 -25
  13. django_cfg/pyproject.toml +1 -1
  14. django_cfg/static/admin/css/constance.css +44 -0
  15. django_cfg/static/admin/css/dashboard.css +6 -170
  16. django_cfg/static/admin/css/layout.css +21 -0
  17. django_cfg/static/admin/css/tabs.css +95 -0
  18. django_cfg/static/admin/css/theme.css +74 -0
  19. django_cfg/static/admin/js/alpine/activity-tracker.js +55 -0
  20. django_cfg/static/admin/js/alpine/chart.js +101 -0
  21. django_cfg/static/admin/js/alpine/command-modal.js +159 -0
  22. django_cfg/static/admin/js/alpine/commands-panel.js +139 -0
  23. django_cfg/static/admin/js/alpine/commands-section.js +260 -0
  24. django_cfg/static/admin/js/alpine/dashboard-tabs.js +46 -0
  25. django_cfg/static/admin/js/alpine/system-metrics.js +20 -0
  26. django_cfg/static/admin/js/alpine/toggle-section.js +28 -0
  27. django_cfg/static/admin/js/utils.js +60 -0
  28. django_cfg/templates/admin/components/modal.html +1 -1
  29. django_cfg/templates/admin/constance/change_list.html +3 -42
  30. django_cfg/templates/admin/index.html +0 -8
  31. django_cfg/templates/admin/layouts/base_dashboard.html +4 -2
  32. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +104 -502
  33. django_cfg/templates/admin/sections/commands_section.html +374 -451
  34. django_cfg/templates/admin/sections/documentation_section.html +13 -33
  35. django_cfg/templates/admin/snippets/components/activity_tracker.html +27 -49
  36. django_cfg/templates/admin/snippets/components/charts_section.html +8 -74
  37. django_cfg/templates/admin/snippets/components/django_commands.html +94 -181
  38. django_cfg/templates/admin/snippets/components/system_metrics.html +18 -10
  39. django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +2 -2
  40. django_cfg/templates/admin/snippets/tabs/commands_tab.html +48 -0
  41. django_cfg/templates/admin/snippets/tabs/documentation_tab.html +1 -190
  42. django_cfg/templates/admin/snippets/tabs/overview_tab.html +1 -1
  43. {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/METADATA +1 -1
  44. {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/RECORD +47 -39
  45. django_cfg/static/admin/js/commands.js +0 -171
  46. django_cfg/static/admin/js/dashboard.js +0 -126
  47. django_cfg/templates/admin/components/management_commands.js +0 -375
  48. django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +0 -322
  49. django_cfg/templates/admin/snippets/components/recent_activity.html +0 -35
  50. {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/WHEEL +0 -0
  51. {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/entry_points.txt +0 -0
  52. {django_cfg-1.4.75.dist-info → django_cfg-1.4.76.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Commands Section Alpine.js Component
3
+ *
4
+ * Manages command execution, modals, search, and category toggling
5
+ */
6
+
7
+ function commandsSectionComponent(totalCommands) {
8
+ return {
9
+ // State
10
+ totalCommands: totalCommands,
11
+ visibleCommands: totalCommands,
12
+ searchQuery: '',
13
+ expandedCategories: new Set(),
14
+
15
+ // Modals
16
+ showConfirmModal: false,
17
+ showOutputModal: false,
18
+
19
+ // Current command
20
+ currentCommand: '',
21
+ currentCommandApp: '',
22
+ currentCommandDescription: '',
23
+
24
+ // Output
25
+ commandOutput: '',
26
+ commandStatus: 'idle', // idle, running, success, error
27
+ statusText: '',
28
+ statusClass: '',
29
+
30
+ init() {
31
+ // Keyboard shortcuts
32
+ document.addEventListener('keydown', (e) => {
33
+ // ESC to close modals
34
+ if (e.key === 'Escape') {
35
+ this.showConfirmModal = false;
36
+ this.showOutputModal = false;
37
+ }
38
+ });
39
+ },
40
+
41
+ // Search functionality
42
+ search() {
43
+ const query = this.searchQuery.toLowerCase().trim();
44
+ let visibleCount = 0;
45
+
46
+ // Get all categories
47
+ const categories = document.querySelectorAll('.category-block');
48
+
49
+ categories.forEach(categoryBlock => {
50
+ const commands = categoryBlock.querySelectorAll('.command-item');
51
+ let categoryHasVisibleCommands = false;
52
+
53
+ commands.forEach(command => {
54
+ const commandName = command.querySelector('.command-name')?.textContent.toLowerCase() || '';
55
+ const commandDesc = command.dataset.description?.toLowerCase() || '';
56
+
57
+ if (!query || commandName.includes(query) || commandDesc.includes(query)) {
58
+ command.style.display = 'block';
59
+ categoryHasVisibleCommands = true;
60
+ visibleCount++;
61
+ } else {
62
+ command.style.display = 'none';
63
+ }
64
+ });
65
+
66
+ // Show/hide category
67
+ if (categoryHasVisibleCommands) {
68
+ categoryBlock.style.display = 'block';
69
+
70
+ // Auto-expand when searching
71
+ if (query) {
72
+ const category = categoryBlock.querySelector('.category-toggle')?.dataset.category;
73
+ if (category) {
74
+ this.expandedCategories.add(category);
75
+ }
76
+ }
77
+ } else {
78
+ categoryBlock.style.display = 'none';
79
+ }
80
+ });
81
+
82
+ this.visibleCommands = visibleCount;
83
+ },
84
+
85
+ clearSearch() {
86
+ this.searchQuery = '';
87
+ this.visibleCommands = this.totalCommands;
88
+
89
+ // Show all
90
+ document.querySelectorAll('.category-block').forEach(block => {
91
+ block.style.display = 'block';
92
+ });
93
+ document.querySelectorAll('.command-item').forEach(item => {
94
+ item.style.display = 'block';
95
+ });
96
+
97
+ // Collapse all
98
+ this.expandedCategories.clear();
99
+ },
100
+
101
+ // Category toggle
102
+ toggleCategory(category) {
103
+ if (this.expandedCategories.has(category)) {
104
+ this.expandedCategories.delete(category);
105
+ } else {
106
+ this.expandedCategories.add(category);
107
+ }
108
+ },
109
+
110
+ isCategoryExpanded(category) {
111
+ return this.expandedCategories.has(category);
112
+ },
113
+
114
+ // Command confirmation
115
+ confirmCommand(commandName, commandApp, commandDesc) {
116
+ this.currentCommand = commandName;
117
+ this.currentCommandApp = commandApp || '';
118
+ this.currentCommandDescription = commandDesc || '';
119
+ this.showConfirmModal = true;
120
+ },
121
+
122
+ cancelExecution() {
123
+ this.showConfirmModal = false;
124
+ this.currentCommand = '';
125
+ },
126
+
127
+ // Command execution
128
+ async executeCommand() {
129
+ this.showConfirmModal = false;
130
+ this.showOutputModal = true;
131
+ this.commandOutput = '';
132
+ this.commandStatus = 'running';
133
+ this.statusText = 'Executing...';
134
+ this.statusClass = 'bg-yellow-500 animate-pulse';
135
+
136
+ const commandName = this.currentCommand;
137
+
138
+ try {
139
+ const response = await fetch('/cfg/commands/execute/', {
140
+ method: 'POST',
141
+ headers: {
142
+ 'Content-Type': 'application/json',
143
+ 'X-CSRFToken': window.getCookie('csrftoken')
144
+ },
145
+ body: JSON.stringify({
146
+ command: commandName,
147
+ args: [],
148
+ options: {}
149
+ })
150
+ });
151
+
152
+ if (!response.ok) {
153
+ throw new Error(`HTTP error! status: ${response.status}`);
154
+ }
155
+
156
+ const reader = response.body.getReader();
157
+ const decoder = new TextDecoder();
158
+
159
+ while (true) {
160
+ const {done, value} = await reader.read();
161
+ if (done) break;
162
+
163
+ const chunk = decoder.decode(value);
164
+ const lines = chunk.split('\n');
165
+
166
+ lines.forEach(line => {
167
+ if (line.startsWith('data: ')) {
168
+ try {
169
+ const data = JSON.parse(line.slice(6));
170
+ this.handleCommandData(data);
171
+ } catch (e) {
172
+ console.error('Error parsing SSE data:', e);
173
+ }
174
+ }
175
+ });
176
+ }
177
+ } catch (error) {
178
+ console.error('Error executing command:', error);
179
+ this.addOutput(`\n❌ Error: ${error.message}`, 'error');
180
+ this.commandStatus = 'error';
181
+ this.statusText = 'Error';
182
+ this.statusClass = 'bg-red-500';
183
+ }
184
+ },
185
+
186
+ handleCommandData(data) {
187
+ switch (data.type) {
188
+ case 'start':
189
+ this.addOutput(`🚀 Starting command: ${data.command}\n📝 Arguments: ${data.args.join(' ')}\n\n`, 'info');
190
+ this.commandStatus = 'running';
191
+ this.statusText = 'Executing...';
192
+ this.statusClass = 'bg-yellow-500 animate-pulse';
193
+ break;
194
+
195
+ case 'output':
196
+ this.addOutput(data.line + '\n', 'output');
197
+ break;
198
+
199
+ case 'complete':
200
+ const success = data.return_code === 0;
201
+ this.commandStatus = success ? 'success' : 'error';
202
+ this.statusText = success ? 'Completed' : 'Failed';
203
+ this.statusClass = success ? 'bg-green-500' : 'bg-red-500';
204
+
205
+ let message = `${success ? '✅' : '❌'} Command completed with exit code: ${data.return_code}`;
206
+ if (data.execution_time) {
207
+ message += ` (${data.execution_time}s)`;
208
+ }
209
+ this.addOutput('\n' + message, success ? 'success' : 'error');
210
+ break;
211
+
212
+ case 'error':
213
+ this.addOutput(`❌ Error: ${data.error}\n`, 'error');
214
+ this.commandStatus = 'error';
215
+ this.statusText = 'Error';
216
+ this.statusClass = 'bg-red-500';
217
+ break;
218
+ }
219
+
220
+ // Auto-scroll
221
+ this.$nextTick(() => {
222
+ const outputEl = this.$refs.commandOutput;
223
+ if (outputEl) {
224
+ outputEl.scrollTop = outputEl.scrollHeight;
225
+ }
226
+ });
227
+ },
228
+
229
+ addOutput(text, type = 'output') {
230
+ this.commandOutput += text;
231
+ },
232
+
233
+ closeOutputModal() {
234
+ this.showOutputModal = false;
235
+ },
236
+
237
+ copyOutput() {
238
+ navigator.clipboard.writeText(this.commandOutput).then(() => {
239
+ // Could add a toast notification here
240
+ console.log('Output copied to clipboard');
241
+ }).catch(err => {
242
+ console.error('Failed to copy:', err);
243
+ });
244
+ },
245
+
246
+
247
+ get showClearButton() {
248
+ return this.searchQuery.trim() !== '';
249
+ },
250
+
251
+ get showNoResults() {
252
+ return this.visibleCommands === 0 && this.searchQuery.trim() !== '';
253
+ }
254
+ };
255
+ }
256
+
257
+ // Register component
258
+ document.addEventListener('alpine:init', () => {
259
+ Alpine.data('commandsSection', commandsSectionComponent);
260
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Dashboard Tabs Alpine.js Component
3
+ *
4
+ * Manages dashboard tabs with URL hash navigation and browser history
5
+ */
6
+
7
+ function dashboardTabsComponent() {
8
+ return {
9
+ activeTab: 0,
10
+ tabNames: ['overview', 'zones', 'users', 'system', 'stats', 'app-stats', 'commands', 'widgets'],
11
+
12
+ init() {
13
+ // Initialize from URL hash or default to first tab
14
+ this.activeTab = this.getTabFromHash();
15
+
16
+ // Handle browser back/forward buttons
17
+ window.addEventListener('hashchange', () => {
18
+ this.activeTab = this.getTabFromHash();
19
+ });
20
+ },
21
+
22
+ switchTab(index) {
23
+ this.activeTab = index;
24
+
25
+ // Update URL hash
26
+ if (this.tabNames[index]) {
27
+ history.replaceState(null, null, '#' + this.tabNames[index]);
28
+ }
29
+ },
30
+
31
+ getTabFromHash() {
32
+ const hash = window.location.hash.substring(1); // Remove #
33
+ const tabIndex = this.tabNames.indexOf(hash);
34
+ return tabIndex >= 0 ? tabIndex : 0; // Default to first tab
35
+ },
36
+
37
+ isActive(index) {
38
+ return this.activeTab === index;
39
+ }
40
+ };
41
+ }
42
+
43
+ // Register component when Alpine initializes
44
+ document.addEventListener('alpine:init', () => {
45
+ Alpine.data('dashboardTabs', dashboardTabsComponent);
46
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * System Metrics Alpine.js Component
3
+ *
4
+ * Provides reactive width styling for health percentage bars
5
+ */
6
+
7
+ function systemMetricsComponent(metricsData) {
8
+ return {
9
+ metrics: metricsData || {},
10
+
11
+ getBarStyle(percentage) {
12
+ return `width: ${percentage}%`;
13
+ }
14
+ };
15
+ }
16
+
17
+ // Register component
18
+ document.addEventListener('alpine:init', () => {
19
+ Alpine.data('systemMetrics', systemMetricsComponent);
20
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Toggle Section Alpine.js Component
3
+ *
4
+ * Simple collapsible section toggle
5
+ */
6
+
7
+ function toggleSectionComponent() {
8
+ return {
9
+ expandedSections: new Set(),
10
+
11
+ toggleSection(sectionId) {
12
+ if (this.expandedSections.has(sectionId)) {
13
+ this.expandedSections.delete(sectionId);
14
+ } else {
15
+ this.expandedSections.add(sectionId);
16
+ }
17
+ },
18
+
19
+ isSectionExpanded(sectionId) {
20
+ return this.expandedSections.has(sectionId);
21
+ }
22
+ };
23
+ }
24
+
25
+ // Register component
26
+ document.addEventListener('alpine:init', () => {
27
+ Alpine.data('toggleSection', toggleSectionComponent);
28
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Django CFG Utility Functions
3
+ *
4
+ * Modern ES6+ utility functions for the admin interface
5
+ * These are minimal utilities that don't require Alpine.js
6
+ */
7
+
8
+ /**
9
+ * Get CSRF token from cookies
10
+ * @param {string} name - Cookie name (usually 'csrftoken')
11
+ * @returns {string|null} Cookie value or null if not found
12
+ */
13
+ function getCookie(name) {
14
+ if (!document.cookie) return null;
15
+
16
+ const cookies = document.cookie.split(';');
17
+ for (const cookie of cookies) {
18
+ const [cookieName, cookieValue] = cookie.trim().split('=');
19
+ if (cookieName === name) {
20
+ return decodeURIComponent(cookieValue);
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+
26
+ /**
27
+ * Copy text to clipboard with visual feedback
28
+ * @param {Event} event - Click event from button
29
+ * @param {string} text - Text to copy
30
+ */
31
+ async function copyToClipboard(event, text) {
32
+ try {
33
+ await navigator.clipboard.writeText(text);
34
+
35
+ const button = event?.target?.closest('button');
36
+ if (!button) return;
37
+
38
+ // Store original state
39
+ const originalHTML = button.innerHTML;
40
+ const originalClasses = button.className;
41
+
42
+ // Show success state
43
+ button.innerHTML = '<span class="material-icons text-xs mr-1">check</span>Copied';
44
+ button.className = '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';
45
+
46
+ // Reset after 2 seconds
47
+ setTimeout(() => {
48
+ button.innerHTML = originalHTML;
49
+ button.className = originalClasses;
50
+ }, 2000);
51
+ } catch (err) {
52
+ console.error('Failed to copy to clipboard:', err);
53
+ }
54
+ }
55
+
56
+ // Export to window for global access
57
+ if (typeof window !== 'undefined') {
58
+ window.getCookie = getCookie;
59
+ window.copyToClipboard = copyToClipboard;
60
+ }
@@ -29,7 +29,7 @@
29
29
  {% endif %}
30
30
 
31
31
  {% if closable %}
32
- <button onclick="{{ close_function|default:'closeModal()' }}" 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">
32
+ <button @click="{{ close_function|default:'close' }}" 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">
33
33
  <span class="material-icons">close</span>
34
34
  </button>
35
35
  {% endif %}
@@ -8,48 +8,9 @@
8
8
  {{ block.super }}
9
9
  {{ media.js }}
10
10
  <script type="text/javascript" src="{% static 'admin/js/constance.js' %}"></script>
11
-
12
- <!-- Enhanced Constance UI Styles -->
13
- <style>
14
- /* Smooth transitions for all interactive elements */
15
- .constance-expandable {
16
- transition: all 0.2s ease-in-out;
17
- }
18
-
19
- /* Better hover states */
20
- .constance tr:hover {
21
- background-color: rgba(0, 0, 0, 0.02);
22
- }
23
-
24
- .dark .constance tr:hover {
25
- background-color: rgba(255, 255, 255, 0.02);
26
- }
27
-
28
- /* Improved focus states for accessibility */
29
- .constance a:focus,
30
- .constance button:focus {
31
- outline: 2px solid rgb(59, 130, 246);
32
- outline-offset: 2px;
33
- border-radius: 4px;
34
- }
35
-
36
- /* Better spacing for long text */
37
- .constance .break-words {
38
- word-break: break-word;
39
- overflow-wrap: break-word;
40
- hyphens: auto;
41
- }
42
-
43
- /* Smooth animations for expand/collapse */
44
- .constance [x-show] {
45
- transition: opacity 0.2s ease-in-out;
46
- }
47
-
48
- /* Icon rotation animation */
49
- .constance .material-symbols-outlined {
50
- transition: transform 0.3s ease-in-out;
51
- }
52
- </style>
11
+
12
+ <!-- Constance UI Styles -->
13
+ <link rel="stylesheet" href="{% static 'admin/css/constance.css' %}"}
53
14
  {% endblock %}
54
15
 
55
16
  {% block content %}
@@ -71,14 +71,6 @@
71
71
  {% endif %}
72
72
  {% endblock %}
73
73
 
74
- {% block documentation_tab %}
75
- {% if documentation_section %}
76
- {{ documentation_section|safe }}
77
- {% else %}
78
- {% include 'admin/snippets/tabs/documentation_tab.html' %}
79
- {% endif %}
80
- {% endblock %}
81
-
82
74
  {% block widgets_tab %}
83
75
  {% if widgets_section %}
84
76
  {{ widgets_section|safe }}
@@ -55,6 +55,8 @@
55
55
 
56
56
  {% block footer %}
57
57
  {{ block.super }}
58
- <script src="{% static 'admin/js/dashboard.js' %}"></script>
59
- <script src="{% static 'admin/js/commands.js' %}"></script>
58
+
59
+ <!-- Utils.js is loaded in dashboard_with_tabs.html extrahead -->
60
+ <!-- All legacy Vanilla JS files have been migrated to Alpine.js components -->
61
+ <!-- Old files removed: dashboard.js, commands.js -->
60
62
  {% endblock %}