django-cfg 1.4.84__py3-none-any.whl → 1.4.85__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/dashboard/serializers/__init__.py +55 -0
- django_cfg/apps/dashboard/serializers/activity.py +38 -0
- django_cfg/apps/dashboard/serializers/apizones.py +26 -0
- django_cfg/apps/dashboard/serializers/base.py +16 -0
- django_cfg/apps/dashboard/serializers/charts.py +44 -0
- django_cfg/apps/dashboard/serializers/commands.py +26 -0
- django_cfg/apps/dashboard/serializers/overview.py +34 -0
- django_cfg/apps/dashboard/serializers/statistics.py +46 -0
- django_cfg/apps/dashboard/serializers/system.py +58 -0
- django_cfg/apps/dashboard/services/__init__.py +10 -1
- django_cfg/apps/dashboard/services/apizones_service.py +119 -0
- django_cfg/apps/dashboard/services/charts_service.py +266 -0
- django_cfg/apps/dashboard/services/commands_service.py +142 -0
- django_cfg/apps/dashboard/services/statistics_service.py +262 -104
- django_cfg/apps/dashboard/urls.py +25 -6
- django_cfg/apps/dashboard/views/__init__.py +23 -0
- django_cfg/apps/dashboard/views/activity_views.py +83 -0
- django_cfg/apps/dashboard/views/apizones_views.py +73 -0
- django_cfg/apps/dashboard/views/charts_views.py +159 -0
- django_cfg/apps/dashboard/views/commands_views.py +73 -0
- django_cfg/apps/dashboard/views/overview_views.py +92 -0
- django_cfg/apps/dashboard/views/statistics_views.py +105 -0
- django_cfg/apps/dashboard/views/system_views.py +73 -0
- django_cfg/modules/django_unfold/callbacks/main.py +7 -6
- django_cfg/modules/django_unfold/dashboard.py +1 -36
- django_cfg/modules/django_unfold/models/config.py +102 -73
- django_cfg/modules/django_unfold/tailwind.py +31 -79
- django_cfg/pyproject.toml +1 -1
- django_cfg/static/frontend/admin/404.html +1 -1
- django_cfg/static/frontend/admin/500.html +1 -1
- django_cfg/static/frontend/admin/_next/static/BembwiEtlu4eFl3OX7n1k/_buildManifest.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{20695.a7d37b6c40ad3f58.js → 43076.55dd23b6cd68edb0.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-3b9d15242191c8bc.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{64330.2ef79bccd7d4e363.js → 64330.41858e98c0e5173b.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/6766.8d01e44e83070e83.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{96168.eb7fdb721b9cdb00.js → 96168.b7197f890097df6e.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{404-c283223d1afd02a2.js → 404-cf71cd7b3cb005e5.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{500-389d6d3e1f2f7fda.js → 500-ff19c7842e3df415.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f62e5528fbcbb6b3.js +272 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{_error-5291033275c26d09.js → _error-87f3fdc2aa131e77.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{index-d7bc30185f52cbca.js → index-69f737d4802cc5b7.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-f24beb6ed3955aa8.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{profile-e93a65e8e7d9022b.js → profile-b8045f993287f1a7.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{ui-669e8f2a785beba2.js → ui-373fff8b42878e64.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{webpack-92add5f95c66e349.js → webpack-7c456a65e96eb97e.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +3 -0
- django_cfg/static/frontend/admin/auth.html +1 -1
- django_cfg/static/frontend/admin/index.html +1 -1
- django_cfg/static/frontend/admin/legal/cookies.html +1 -1
- django_cfg/static/frontend/admin/legal/privacy.html +1 -1
- django_cfg/static/frontend/admin/legal/security.html +1 -1
- django_cfg/static/frontend/admin/legal/terms.html +1 -1
- django_cfg/static/frontend/admin/private/centrifugo.html +1 -1
- django_cfg/static/frontend/admin/private/profile.html +1 -1
- django_cfg/static/frontend/admin/private/ui.html +1 -1
- django_cfg/static/frontend/admin/private.html +1 -1
- django_cfg/templates/admin/index.html +237 -5
- django_cfg/templates/admin/sections/commands_section.html +5 -0
- django_cfg/templates/admin/sections/documentation_section.html +5 -0
- django_cfg/templates/admin/sections/overview_section.html +5 -0
- django_cfg/templates/admin/sections/stats_section.html +5 -0
- django_cfg/templates/admin/sections/system_section.html +5 -0
- django_cfg/templates/admin/sections/widgets_section.html +11 -0
- django_cfg/templates/unfold/layouts/skeleton.html +27 -0
- django_cfg/templatetags/django_cfg.py +53 -0
- {django_cfg-1.4.84.dist-info → django_cfg-1.4.85.dist-info}/METADATA +1 -1
- {django_cfg-1.4.84.dist-info → django_cfg-1.4.85.dist-info}/RECORD +74 -51
- django_cfg/apps/dashboard/api/__init__.py +0 -27
- django_cfg/apps/dashboard/api/serializers.py +0 -165
- django_cfg/apps/dashboard/api/viewsets.py +0 -257
- django_cfg/static/frontend/admin/_next/static/-Zk0eDB7OJOEFrFyR5BwZ/_buildManifest.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/43076-4be6a9794e9c3e8b.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-79c02212788f1ec7.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/6766.d62fed7cd4761148.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/82296-a2c8d38f62224be5.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f25bec36bbdc9625.js +0 -272
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-22532c65971225eb.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-a8a9ba76f2c75354.js +0 -1
- django_cfg/static/frontend/admin/_next/static/css/78d677ac1677c210.css +0 -3
- /django_cfg/static/frontend/admin/_next/static/{-Zk0eDB7OJOEFrFyR5BwZ → BembwiEtlu4eFl3OX7n1k}/_ssgManifest.js +0 -0
- {django_cfg-1.4.84.dist-info → django_cfg-1.4.85.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.84.dist-info → django_cfg-1.4.85.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.84.dist-info → django_cfg-1.4.85.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
{% load static %}
|
|
4
4
|
{% load unfold %}
|
|
5
|
+
{% load django_cfg %}
|
|
5
6
|
|
|
6
7
|
{% block extrahead %}
|
|
7
8
|
{{ block.super }}
|
|
@@ -10,14 +11,23 @@
|
|
|
10
11
|
<style>
|
|
11
12
|
#nextjs-dashboard-iframe {
|
|
12
13
|
width: 100%;
|
|
13
|
-
height:
|
|
14
|
+
min-height: 600px;
|
|
15
|
+
height: auto;
|
|
14
16
|
border: none;
|
|
15
17
|
display: block;
|
|
18
|
+
visibility: hidden !important;
|
|
19
|
+
opacity: 0 !important;
|
|
20
|
+
transition: height 0.2s ease-in-out, opacity 0.3s ease-in-out;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#nextjs-dashboard-iframe.loaded {
|
|
24
|
+
visibility: visible !important;
|
|
25
|
+
opacity: 1 !important;
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
.iframe-container {
|
|
19
29
|
position: relative;
|
|
20
|
-
background:
|
|
30
|
+
background: transparent;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
.iframe-loading {
|
|
@@ -63,16 +73,31 @@
|
|
|
63
73
|
{% block content %}
|
|
64
74
|
<!-- Main Container -->
|
|
65
75
|
{% component "unfold/components/container.html" %}
|
|
76
|
+
{% if is_frontend_dev_mode %}
|
|
77
|
+
<!-- Development Mode Badge -->
|
|
78
|
+
<div class="bg-yellow-100 dark:bg-yellow-900 border-l-4 border-yellow-500 text-yellow-700 dark:text-yellow-300 p-4 mb-4" role="alert">
|
|
79
|
+
<div class="flex items-center">
|
|
80
|
+
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
|
81
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
|
|
82
|
+
</svg>
|
|
83
|
+
<div>
|
|
84
|
+
<p class="font-bold">Frontend Development Mode Active</p>
|
|
85
|
+
<p class="text-sm">Loading Next.js from <code class="bg-yellow-200 dark:bg-yellow-800 px-1 rounded">{% nextjs_admin_url %}</code></p>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
{% endif %}
|
|
90
|
+
|
|
66
91
|
<!-- Next.js Dashboard iframe -->
|
|
67
92
|
<div class="iframe-container">
|
|
68
93
|
<div class="iframe-loading" id="iframe-loading">
|
|
69
94
|
<div class="spinner"></div>
|
|
70
|
-
<p>Loading Next.js Dashboard...</p>
|
|
95
|
+
<p id="loading-text">Loading Next.js Dashboard...</p>
|
|
71
96
|
</div>
|
|
72
97
|
|
|
73
98
|
<iframe
|
|
74
99
|
id="nextjs-dashboard-iframe"
|
|
75
|
-
src="
|
|
100
|
+
src="{% nextjs_admin_url 'private' %}"
|
|
76
101
|
title="Next.js Dashboard"
|
|
77
102
|
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-modals"
|
|
78
103
|
></iframe>
|
|
@@ -100,15 +125,222 @@
|
|
|
100
125
|
(function() {
|
|
101
126
|
const iframe = document.getElementById('nextjs-dashboard-iframe');
|
|
102
127
|
const loading = document.getElementById('iframe-loading');
|
|
128
|
+
const loadingText = document.getElementById('loading-text');
|
|
129
|
+
|
|
130
|
+
// Log initial state
|
|
131
|
+
console.log('[Parent] 🎬 Initial iframe state:', {
|
|
132
|
+
visibility: window.getComputedStyle(iframe).visibility,
|
|
133
|
+
opacity: window.getComputedStyle(iframe).opacity,
|
|
134
|
+
hasLoadedClass: iframe.classList.contains('loaded')
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Detect iframe origin based on src
|
|
138
|
+
const iframeUrl = new URL(iframe.src);
|
|
139
|
+
const iframeOrigin = iframeUrl.origin;
|
|
140
|
+
const isDevMode = iframeOrigin !== window.location.origin;
|
|
141
|
+
|
|
142
|
+
// Debug logging (uncomment for debugging)
|
|
143
|
+
// console.log('[Parent] 🚀 Initializing iframe communication');
|
|
144
|
+
// console.log('[Parent] 📍 Parent origin:', window.location.origin);
|
|
145
|
+
// console.log('[Parent] 📍 iframe origin:', iframeOrigin);
|
|
146
|
+
// console.log('[Parent] 🔧 Dev mode:', isDevMode);
|
|
103
147
|
|
|
104
148
|
iframe.addEventListener('load', function() {
|
|
105
|
-
|
|
149
|
+
// console.log('[Parent] 🎬 iframe load event fired');
|
|
150
|
+
|
|
151
|
+
// Send initial data (theme + auth) to iframe after a small delay
|
|
152
|
+
// to ensure iframe is fully initialized
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
// console.log('[Parent] ⏱️ Sending initial data after iframe load');
|
|
155
|
+
sendDataToIframe();
|
|
156
|
+
}, 100);
|
|
106
157
|
});
|
|
107
158
|
|
|
108
159
|
// Hide loading after 5 seconds if iframe doesn't load
|
|
109
160
|
setTimeout(() => {
|
|
110
161
|
loading.classList.add('hidden');
|
|
111
162
|
}, 5000);
|
|
163
|
+
|
|
164
|
+
// Function to send theme and auth to iframe
|
|
165
|
+
function sendDataToIframe() {
|
|
166
|
+
// console.log('[Parent] 📤 sendDataToIframe() called');
|
|
167
|
+
|
|
168
|
+
if (!iframe || !iframe.contentWindow) {
|
|
169
|
+
console.error('[Parent] iframe or contentWindow not available');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const htmlElement = document.documentElement;
|
|
174
|
+
const isDark = htmlElement.classList.contains('dark');
|
|
175
|
+
// console.log('[Parent] 🎨 HTML element classes:', htmlElement.className);
|
|
176
|
+
// console.log('[Parent] 🎨 isDark:', isDark);
|
|
177
|
+
|
|
178
|
+
// Get adminTheme value from Alpine.js data if available
|
|
179
|
+
let themeMode = 'auto';
|
|
180
|
+
// console.log('[Parent] 🔍 Checking Alpine.js:', {
|
|
181
|
+
// hasAlpine: !!window.Alpine,
|
|
182
|
+
// hasAlpineData: !!(window.Alpine && window.Alpine.$data)
|
|
183
|
+
// });
|
|
184
|
+
|
|
185
|
+
if (window.Alpine && window.Alpine.$data) {
|
|
186
|
+
try {
|
|
187
|
+
const alpineData = window.Alpine.$data(htmlElement);
|
|
188
|
+
// console.log('[Parent] 🔍 Alpine data:', alpineData);
|
|
189
|
+
if (alpineData && alpineData.adminTheme) {
|
|
190
|
+
themeMode = alpineData.adminTheme;
|
|
191
|
+
// console.log('[Parent] ✅ Got adminTheme from Alpine:', themeMode);
|
|
192
|
+
}
|
|
193
|
+
} catch (e) {
|
|
194
|
+
console.warn('[Parent] Failed to get Alpine data:', e);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Get JWT tokens from localStorage (Django injected them)
|
|
199
|
+
const authToken = localStorage.getItem('auth_token');
|
|
200
|
+
const refreshToken = localStorage.getItem('refresh_token');
|
|
201
|
+
// console.log('[Parent] 🔑 Tokens in localStorage:', {
|
|
202
|
+
// hasAuthToken: !!authToken,
|
|
203
|
+
// hasRefreshToken: !!refreshToken,
|
|
204
|
+
// authTokenLength: authToken ? authToken.length : 0
|
|
205
|
+
// });
|
|
206
|
+
|
|
207
|
+
// Send theme
|
|
208
|
+
const themeMessage = {
|
|
209
|
+
type: 'parent-theme',
|
|
210
|
+
data: {
|
|
211
|
+
theme: isDark ? 'dark' : 'light',
|
|
212
|
+
themeMode: themeMode
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
// console.log('[Parent] 📨 Sending theme message:', themeMessage);
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
iframe.contentWindow.postMessage(themeMessage, iframeOrigin);
|
|
219
|
+
// console.log('[Parent] ✅ Theme message sent successfully to', iframeOrigin);
|
|
220
|
+
} catch (e) {
|
|
221
|
+
console.error('[Parent] Failed to send theme message:', e);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Send auth tokens if available
|
|
225
|
+
if (authToken) {
|
|
226
|
+
const authMessage = {
|
|
227
|
+
type: 'parent-auth',
|
|
228
|
+
data: {
|
|
229
|
+
authToken: authToken,
|
|
230
|
+
refreshToken: refreshToken
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
// console.log('[Parent] 📨 Sending auth message:', {
|
|
234
|
+
// type: authMessage.type,
|
|
235
|
+
// hasAuthToken: !!authMessage.data.authToken,
|
|
236
|
+
// hasRefreshToken: !!authMessage.data.refreshToken
|
|
237
|
+
// });
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
iframe.contentWindow.postMessage(authMessage, iframeOrigin);
|
|
241
|
+
// console.log('[Parent] ✅ Auth message sent successfully to', iframeOrigin);
|
|
242
|
+
} catch (e) {
|
|
243
|
+
console.error('[Parent] Failed to send auth message:', e);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// else {
|
|
247
|
+
// console.warn('[Parent] ⚠️ No auth token found, skipping auth message');
|
|
248
|
+
// }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Watch for theme changes on HTML element
|
|
252
|
+
// console.log('[Parent] 👀 Setting up MutationObserver for theme changes');
|
|
253
|
+
const observer = new MutationObserver((mutations) => {
|
|
254
|
+
mutations.forEach((mutation) => {
|
|
255
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
256
|
+
// console.log('[Parent] 🔄 HTML class changed, sending updated theme');
|
|
257
|
+
sendDataToIframe();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
observer.observe(document.documentElement, {
|
|
263
|
+
attributes: true,
|
|
264
|
+
attributeFilter: ['class']
|
|
265
|
+
});
|
|
266
|
+
// console.log('[Parent] ✅ MutationObserver active');
|
|
267
|
+
|
|
268
|
+
// Listen for messages from iframe
|
|
269
|
+
// console.log('[Parent] 👂 Setting up message listener for iframe');
|
|
270
|
+
window.addEventListener('message', (event) => {
|
|
271
|
+
// console.log('[Parent] 📨 Message received from iframe:', {
|
|
272
|
+
// origin: event.origin,
|
|
273
|
+
// expectedOrigin: iframeOrigin,
|
|
274
|
+
// type: event.data?.type,
|
|
275
|
+
// data: event.data?.data
|
|
276
|
+
// });
|
|
277
|
+
|
|
278
|
+
if (event.origin !== iframeOrigin) {
|
|
279
|
+
// console.log('[Parent] ⚠️ Message from unexpected origin, ignoring');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const { type, data } = event.data || {};
|
|
284
|
+
|
|
285
|
+
switch (type) {
|
|
286
|
+
case 'iframe-ready':
|
|
287
|
+
// console.log('[Parent] ✅ iframe-ready received:', data);
|
|
288
|
+
// Send data immediately when iframe is ready
|
|
289
|
+
// console.log('[Parent] 📤 Responding to iframe-ready by sending data');
|
|
290
|
+
sendDataToIframe();
|
|
291
|
+
break;
|
|
292
|
+
|
|
293
|
+
case 'iframe-auth-status':
|
|
294
|
+
// iframe sent auth status update
|
|
295
|
+
console.log('[Parent] 🔐 Auth status received:', data);
|
|
296
|
+
console.log('[Parent] 📊 Current iframe state BEFORE:', {
|
|
297
|
+
visibility: window.getComputedStyle(iframe).visibility,
|
|
298
|
+
opacity: window.getComputedStyle(iframe).opacity,
|
|
299
|
+
hasLoadedClass: iframe.classList.contains('loaded')
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (data?.isAuthenticated && !data?.isLoading) {
|
|
303
|
+
// User is authenticated, show iframe
|
|
304
|
+
console.log('[Parent] ✅ Adding .loaded class');
|
|
305
|
+
loading.classList.add('hidden');
|
|
306
|
+
iframe.classList.add('loaded');
|
|
307
|
+
|
|
308
|
+
setTimeout(() => {
|
|
309
|
+
console.log('[Parent] 📊 iframe state AFTER .loaded:', {
|
|
310
|
+
visibility: window.getComputedStyle(iframe).visibility,
|
|
311
|
+
opacity: window.getComputedStyle(iframe).opacity,
|
|
312
|
+
hasLoadedClass: iframe.classList.contains('loaded')
|
|
313
|
+
});
|
|
314
|
+
}, 100);
|
|
315
|
+
} else if (data?.isLoading) {
|
|
316
|
+
// Still loading
|
|
317
|
+
console.log('[Parent] ⏳ Auth loading...');
|
|
318
|
+
loadingText.textContent = 'Loading authentication...';
|
|
319
|
+
} else {
|
|
320
|
+
// Not authenticated
|
|
321
|
+
console.log('[Parent] ⚠️ Not authenticated');
|
|
322
|
+
loadingText.textContent = 'Waiting for authentication...';
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
case 'iframe-resize':
|
|
326
|
+
// Update iframe height based on content (both increase and decrease)
|
|
327
|
+
if (data?.height && typeof data.height === 'number') {
|
|
328
|
+
// Add padding to avoid scroll bars (50px buffer for safety)
|
|
329
|
+
const newHeight = Math.max(600, data.height + 50); // Min 600px
|
|
330
|
+
iframe.style.height = newHeight + 'px';
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
case 'iframe-navigation':
|
|
334
|
+
// console.log('[Parent] 🧭 iframe navigated to:', data.path);
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
// if (type) {
|
|
338
|
+
// console.log('[Parent] ℹ️ Unknown message type:', type);
|
|
339
|
+
// }
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
// console.log('[Parent] ✅ Message listener active');
|
|
112
344
|
})();
|
|
113
345
|
</script>
|
|
114
346
|
{% endblock %}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{% comment %}
|
|
2
|
+
Widgets Section - Legacy Template
|
|
3
|
+
This template is kept for backward compatibility with django_dashboard callbacks.
|
|
4
|
+
The actual dashboard is now rendered by Next.js in the iframe.
|
|
5
|
+
{% endcomment %}
|
|
6
|
+
|
|
7
|
+
<!-- Empty section - widgets are rendered in Next.js iframe -->
|
|
8
|
+
<div class="hidden">
|
|
9
|
+
<!-- This section is intentionally empty -->
|
|
10
|
+
<!-- Dashboard widgets are now rendered by Next.js Admin Panel -->
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{% extends "unfold/layouts/skeleton.html" %}
|
|
2
|
+
|
|
3
|
+
{% block extrastyle %}
|
|
4
|
+
{{ block.super }}
|
|
5
|
+
<style id="django-cfg-theme-sync">
|
|
6
|
+
/* ============================================== */
|
|
7
|
+
/* Django CFG - Force Color Variables */
|
|
8
|
+
/* Ensures COLORS from get_color_scheme() apply */
|
|
9
|
+
/* ============================================== */
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* NOTE: Colors are already defined via UNFOLD['COLORS'] in:
|
|
13
|
+
* django_cfg/modules/django_unfold/models/config.py:get_color_scheme()
|
|
14
|
+
*
|
|
15
|
+
* This CSS just ensures Tailwind classes use those variables correctly.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* Ensure color-scheme is set for proper dark mode rendering */
|
|
19
|
+
html.dark {
|
|
20
|
+
color-scheme: dark;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
html:not(.dark) {
|
|
24
|
+
color-scheme: light;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
{% endblock %}
|
|
@@ -4,6 +4,7 @@ Django-CFG Template Tags
|
|
|
4
4
|
Provides template tags for accessing django-cfg configuration constants.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import os
|
|
7
8
|
from django import template
|
|
8
9
|
from django.utils.safestring import mark_safe
|
|
9
10
|
from rest_framework_simplejwt.tokens import RefreshToken
|
|
@@ -134,3 +135,55 @@ def inject_jwt_tokens_script(context):
|
|
|
134
135
|
}})();
|
|
135
136
|
</script>"""
|
|
136
137
|
return mark_safe(script)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@register.simple_tag
|
|
141
|
+
def nextjs_admin_url(path=''):
|
|
142
|
+
"""
|
|
143
|
+
Get the URL for Next.js Admin Panel.
|
|
144
|
+
|
|
145
|
+
In development mode (when DJANGO_CFG_FRONTEND_DEV_MODE=true):
|
|
146
|
+
Returns http://localhost:3000/{path}
|
|
147
|
+
|
|
148
|
+
In production mode:
|
|
149
|
+
Returns /cfg/admin/{path}
|
|
150
|
+
|
|
151
|
+
Usage in template:
|
|
152
|
+
{% load django_cfg %}
|
|
153
|
+
<iframe src="{% nextjs_admin_url 'private' %}"></iframe>
|
|
154
|
+
|
|
155
|
+
Environment variable:
|
|
156
|
+
DJANGO_CFG_FRONTEND_DEV_MODE=true # Enable dev mode
|
|
157
|
+
DJANGO_CFG_FRONTEND_DEV_PORT=3000 # Custom dev port (default: 3000)
|
|
158
|
+
"""
|
|
159
|
+
# Check if frontend dev mode is enabled
|
|
160
|
+
is_dev_mode = os.environ.get('DJANGO_CFG_FRONTEND_DEV_MODE', '').lower() in ('true', '1', 'yes')
|
|
161
|
+
|
|
162
|
+
# Normalize path - ensure trailing slash for Django static view
|
|
163
|
+
if path and not path.endswith('/'):
|
|
164
|
+
path = f'{path}/'
|
|
165
|
+
|
|
166
|
+
if is_dev_mode:
|
|
167
|
+
# Development mode: use Next.js dev server
|
|
168
|
+
dev_port = os.environ.get('DJANGO_CFG_FRONTEND_DEV_PORT', '3000')
|
|
169
|
+
base_url = f'http://localhost:{dev_port}'
|
|
170
|
+
return f'{base_url}/{path}' if path else base_url
|
|
171
|
+
else:
|
|
172
|
+
# Production mode: use Django static files
|
|
173
|
+
return f'/cfg/admin/{path}' if path else '/cfg/admin/'
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@register.simple_tag
|
|
177
|
+
def is_frontend_dev_mode():
|
|
178
|
+
"""
|
|
179
|
+
Check if frontend is in development mode.
|
|
180
|
+
|
|
181
|
+
Returns True if DJANGO_CFG_FRONTEND_DEV_MODE environment variable is set to true.
|
|
182
|
+
|
|
183
|
+
Usage in template:
|
|
184
|
+
{% load django_cfg %}
|
|
185
|
+
{% if is_frontend_dev_mode %}
|
|
186
|
+
<div class="dev-badge">Dev Mode</div>
|
|
187
|
+
{% endif %}
|
|
188
|
+
"""
|
|
189
|
+
return os.environ.get('DJANGO_CFG_FRONTEND_DEV_MODE', '').lower() in ('true', '1', 'yes')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cfg
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.85
|
|
4
4
|
Summary: Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django.
|
|
5
5
|
Project-URL: Homepage, https://djangocfg.com
|
|
6
6
|
Project-URL: Documentation, https://djangocfg.com
|