django-cfg 1.4.83__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.

Files changed (169) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/dashboard/__init__.py +8 -0
  3. django_cfg/apps/dashboard/apps.py +23 -0
  4. django_cfg/apps/dashboard/serializers/__init__.py +55 -0
  5. django_cfg/apps/dashboard/serializers/activity.py +38 -0
  6. django_cfg/apps/dashboard/serializers/apizones.py +26 -0
  7. django_cfg/apps/dashboard/serializers/base.py +16 -0
  8. django_cfg/apps/dashboard/serializers/charts.py +44 -0
  9. django_cfg/apps/dashboard/serializers/commands.py +26 -0
  10. django_cfg/apps/dashboard/serializers/overview.py +34 -0
  11. django_cfg/apps/dashboard/serializers/statistics.py +46 -0
  12. django_cfg/apps/dashboard/serializers/system.py +58 -0
  13. django_cfg/apps/dashboard/services/__init__.py +20 -0
  14. django_cfg/apps/dashboard/services/apizones_service.py +119 -0
  15. django_cfg/apps/dashboard/services/charts_service.py +266 -0
  16. django_cfg/apps/dashboard/services/commands_service.py +142 -0
  17. django_cfg/apps/dashboard/services/statistics_service.py +393 -0
  18. django_cfg/apps/dashboard/services/system_health_service.py +280 -0
  19. django_cfg/apps/dashboard/urls.py +42 -0
  20. django_cfg/apps/dashboard/views/__init__.py +23 -0
  21. django_cfg/apps/dashboard/views/activity_views.py +83 -0
  22. django_cfg/apps/dashboard/views/apizones_views.py +73 -0
  23. django_cfg/apps/dashboard/views/charts_views.py +159 -0
  24. django_cfg/apps/dashboard/views/commands_views.py +73 -0
  25. django_cfg/apps/dashboard/views/overview_views.py +92 -0
  26. django_cfg/apps/dashboard/views/statistics_views.py +105 -0
  27. django_cfg/apps/dashboard/views/system_views.py +73 -0
  28. django_cfg/apps/frontend/views.py +5 -0
  29. django_cfg/apps/urls.py +2 -1
  30. django_cfg/core/builders/apps_builder.py +1 -0
  31. django_cfg/modules/django_client/core/parser/openapi30.py +12 -5
  32. django_cfg/modules/django_client/core/parser/openapi31.py +12 -5
  33. django_cfg/modules/django_unfold/callbacks/main.py +7 -6
  34. django_cfg/modules/django_unfold/dashboard.py +1 -36
  35. django_cfg/modules/django_unfold/models/config.py +102 -73
  36. django_cfg/modules/django_unfold/tailwind.py +31 -79
  37. django_cfg/pyproject.toml +1 -1
  38. django_cfg/static/frontend/admin/404.html +1 -1
  39. django_cfg/static/frontend/admin/500.html +1 -1
  40. django_cfg/static/frontend/admin/_next/static/BembwiEtlu4eFl3OX7n1k/_buildManifest.js +1 -0
  41. django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +1 -0
  42. django_cfg/static/frontend/admin/_next/static/chunks/{25033.d626f78bc99bc4a1.js → 25033.ee3e206d5a2877b6.js} +2 -2
  43. django_cfg/static/frontend/admin/_next/static/chunks/{25892.964150a58f94ce06.js → 25892.5cbed319f9226fdc.js} +1 -1
  44. django_cfg/static/frontend/admin/_next/static/chunks/{2d7a934f.dfef67639279d59d.js → 2d7a934f.329c61f23af1a7ec.js} +1 -1
  45. django_cfg/static/frontend/admin/_next/static/chunks/{30649.00c679812a56aee3.js → 30649.963cfb7268b5864a.js} +1 -1
  46. django_cfg/static/frontend/admin/_next/static/chunks/{30875.784491146c38dbcb.js → 30875.82c3741757b8aa32.js} +1 -1
  47. django_cfg/static/frontend/admin/_next/static/chunks/{32163.ab0ca435b3f26c04.js → 32163.109a03a7252f1508.js} +1 -1
  48. django_cfg/static/frontend/admin/_next/static/chunks/{49978.fb8ba7ee52ffe666.js → 49978.db5a86a8eb233f35.js} +1 -1
  49. django_cfg/static/frontend/admin/_next/static/chunks/50314-3b9d15242191c8bc.js +1 -0
  50. django_cfg/static/frontend/admin/_next/static/chunks/{50319.f786248384877960.js → 50319.fd78c7f7e3f1966e.js} +1 -1
  51. django_cfg/static/frontend/admin/_next/static/chunks/{52908.b690e323d8f8efdd.js → 52908.da5b850b0bc0970c.js} +1 -1
  52. django_cfg/static/frontend/admin/_next/static/chunks/{53710.80ca863525d137db.js → 53710.7176bbee6c7b78be.js} +1 -1
  53. django_cfg/static/frontend/admin/_next/static/chunks/{57982.251fed8d58adcf53.js → 57982.2c90b33b0934522a.js} +2 -2
  54. django_cfg/static/frontend/admin/_next/static/chunks/{60181.86e18057c4caaa97.js → 60181.c94d78d10eb5da37.js} +1 -1
  55. django_cfg/static/frontend/admin/_next/static/chunks/{60374.bde0ec1249aa79c6.js → 60374.5d80cfc45439b2b0.js} +1 -1
  56. django_cfg/static/frontend/admin/_next/static/chunks/{64330.2ef79bccd7d4e363.js → 64330.41858e98c0e5173b.js} +1 -1
  57. django_cfg/static/frontend/admin/_next/static/chunks/6766.8d01e44e83070e83.js +1 -0
  58. django_cfg/static/frontend/admin/_next/static/chunks/{6884.7b1db804c88280ed.js → 6884.624d563508cf6db4.js} +1 -1
  59. django_cfg/static/frontend/admin/_next/static/chunks/{69436.9515b854cdf4b57a.js → 69436.be44021e3d7c99c7.js} +1 -1
  60. django_cfg/static/frontend/admin/_next/static/chunks/{70628.00cdd98f672e684f.js → 70628.58e8c38a66543d5e.js} +1 -1
  61. django_cfg/static/frontend/admin/_next/static/chunks/{73218.a826c2248612b37f.js → 73218.d712e7bd678e23a8.js} +1 -1
  62. django_cfg/static/frontend/admin/_next/static/chunks/{76334.64fbaa923d9ac293.js → 76334.f43f2d8b4bbf8dd6.js} +1 -1
  63. django_cfg/static/frontend/admin/_next/static/chunks/{7799.2b280f8ddf067d49.js → 7799.1575cc212bc750c7.js} +1 -1
  64. django_cfg/static/frontend/admin/_next/static/chunks/{80574.620a8a5b4eb91c25.js → 80574.92638dd7b9979664.js} +1 -1
  65. django_cfg/static/frontend/admin/_next/static/chunks/{81127.a0603c3394892d4e.js → 81127.3ead500eec887152.js} +1 -1
  66. django_cfg/static/frontend/admin/_next/static/chunks/{8383.eb6188b22c453e14.js → 8383.e25a442df26b2e26.js} +1 -1
  67. django_cfg/static/frontend/admin/_next/static/chunks/{85833.35e6ca25ac32a7d2.js → 85833.b0dead4fbcbfdd1b.js} +1 -1
  68. django_cfg/static/frontend/admin/_next/static/chunks/{95365.fc9d7653a78839d0.js → 95365.2b430045fc2e5acf.js} +1 -1
  69. django_cfg/static/frontend/admin/_next/static/chunks/{96168.eb7fdb721b9cdb00.js → 96168.b7197f890097df6e.js} +1 -1
  70. django_cfg/static/frontend/admin/_next/static/chunks/{96424.0793b94836eb13a6.js → 96424.11d76570e9a94b85.js} +1 -1
  71. django_cfg/static/frontend/admin/_next/static/chunks/pages/{404-c283223d1afd02a2.js → 404-cf71cd7b3cb005e5.js} +1 -1
  72. django_cfg/static/frontend/admin/_next/static/chunks/pages/{500-389d6d3e1f2f7fda.js → 500-ff19c7842e3df415.js} +1 -1
  73. django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f62e5528fbcbb6b3.js +272 -0
  74. django_cfg/static/frontend/admin/_next/static/chunks/pages/{_error-5291033275c26d09.js → _error-87f3fdc2aa131e77.js} +1 -1
  75. django_cfg/static/frontend/admin/_next/static/chunks/pages/index-69f737d4802cc5b7.js +1 -0
  76. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-bb5507a122775f30.js → cookies-b39c7f22c066e2c6.js} +1 -1
  77. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-f8a3d8db1a197be3.js → privacy-5aedad0cf3a4f80f.js} +1 -1
  78. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-aba50addd2179f8f.js → security-dbd854d0d5d483e2.js} +1 -1
  79. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-4aa35cd30b5c08ad.js → terms-f3e1d2b9e5edf12f.js} +1 -1
  80. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-f24beb6ed3955aa8.js +1 -0
  81. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{profile-e93a65e8e7d9022b.js → profile-b8045f993287f1a7.js} +1 -1
  82. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/ui-373fff8b42878e64.js +1 -0
  83. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +1 -0
  84. django_cfg/static/frontend/admin/_next/static/chunks/{webpack-905bba30877f6490.js → webpack-7c456a65e96eb97e.js} +1 -1
  85. django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +3 -0
  86. django_cfg/static/frontend/admin/auth.html +1 -1
  87. django_cfg/static/frontend/admin/index.html +1 -1
  88. django_cfg/static/frontend/admin/legal/cookies.html +1 -1
  89. django_cfg/static/frontend/admin/legal/privacy.html +1 -1
  90. django_cfg/static/frontend/admin/legal/security.html +1 -1
  91. django_cfg/static/frontend/admin/legal/terms.html +1 -1
  92. django_cfg/static/frontend/admin/private/centrifugo.html +1 -1
  93. django_cfg/static/frontend/admin/private/profile.html +1 -1
  94. django_cfg/static/frontend/admin/private/ui.html +1 -0
  95. django_cfg/static/frontend/admin/private.html +1 -1
  96. django_cfg/templates/admin/index.html +328 -62
  97. django_cfg/templates/admin/sections/commands_section.html +5 -549
  98. django_cfg/templates/admin/sections/documentation_section.html +5 -152
  99. django_cfg/templates/admin/sections/overview_section.html +5 -112
  100. django_cfg/templates/admin/sections/stats_section.html +5 -35
  101. django_cfg/templates/admin/sections/system_section.html +5 -99
  102. django_cfg/templates/admin/sections/widgets_section.html +10 -128
  103. django_cfg/templates/admin_old/index.html +80 -0
  104. django_cfg/templates/admin_old/sections/commands_section.html +549 -0
  105. django_cfg/templates/admin_old/sections/documentation_section.html +152 -0
  106. django_cfg/templates/admin_old/sections/overview_section.html +112 -0
  107. django_cfg/templates/admin_old/sections/stats_section.html +35 -0
  108. django_cfg/templates/admin_old/sections/system_section.html +99 -0
  109. django_cfg/templates/admin_old/sections/widgets_section.html +129 -0
  110. django_cfg/templates/unfold/layouts/skeleton.html +27 -0
  111. django_cfg/templatetags/django_cfg.py +53 -0
  112. {django_cfg-1.4.83.dist-info → django_cfg-1.4.85.dist-info}/METADATA +1 -1
  113. {django_cfg-1.4.83.dist-info → django_cfg-1.4.85.dist-info}/RECORD +160 -124
  114. django_cfg/static/frontend/admin/_next/static/chunks/6766.d62fed7cd4761148.js +0 -1
  115. django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-16701a4e1bc3e6ac.js +0 -272
  116. django_cfg/static/frontend/admin/_next/static/chunks/pages/index-88751d9f44a32105.js +0 -1
  117. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-1c5f00c26c77a47b.js +0 -1
  118. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-2f58633ddf63a5bc.js +0 -1
  119. django_cfg/static/frontend/admin/_next/static/chunks/pages/ui-0e6c0e35862789ec.js +0 -1
  120. django_cfg/static/frontend/admin/_next/static/css/806300fb98c42afb.css +0 -3
  121. django_cfg/static/frontend/admin/_next/static/ibMHm1p66p0UGKsKnDWxn/_buildManifest.js +0 -1
  122. django_cfg/static/frontend/admin/ui.html +0 -92
  123. /django_cfg/static/frontend/admin/_next/static/{ibMHm1p66p0UGKsKnDWxn → BembwiEtlu4eFl3OX7n1k}/_ssgManifest.js +0 -0
  124. /django_cfg/templates/{admin → admin_old}/components/action_grid.html +0 -0
  125. /django_cfg/templates/{admin → admin_old}/components/card.html +0 -0
  126. /django_cfg/templates/{admin → admin_old}/components/data_table.html +0 -0
  127. /django_cfg/templates/{admin → admin_old}/components/metric_card.html +0 -0
  128. /django_cfg/templates/{admin → admin_old}/components/modal.html +0 -0
  129. /django_cfg/templates/{admin → admin_old}/components/progress_bar.html +0 -0
  130. /django_cfg/templates/{admin → admin_old}/components/section_header.html +0 -0
  131. /django_cfg/templates/{admin → admin_old}/components/stat_item.html +0 -0
  132. /django_cfg/templates/{admin → admin_old}/components/stats_grid.html +0 -0
  133. /django_cfg/templates/{admin → admin_old}/components/status_badge.html +0 -0
  134. /django_cfg/templates/{admin → admin_old}/components/user_avatar.html +0 -0
  135. /django_cfg/templates/{admin → admin_old}/constance/change_list.html +0 -0
  136. /django_cfg/templates/{admin → admin_old}/constance/includes/default_value.html +0 -0
  137. /django_cfg/templates/{admin → admin_old}/constance/includes/fieldset_header.html +0 -0
  138. /django_cfg/templates/{admin → admin_old}/constance/includes/results_list.html +0 -0
  139. /django_cfg/templates/{admin → admin_old}/constance/includes/setting_row.html +0 -0
  140. /django_cfg/templates/{admin → admin_old}/constance/includes/table_headers.html +0 -0
  141. /django_cfg/templates/{admin → admin_old}/examples/component_class_example.html +0 -0
  142. /django_cfg/templates/{admin → admin_old}/import_export/change_list_export.html +0 -0
  143. /django_cfg/templates/{admin → admin_old}/import_export/change_list_import.html +0 -0
  144. /django_cfg/templates/{admin → admin_old}/import_export/change_list_import_export.html +0 -0
  145. /django_cfg/templates/{admin → admin_old}/index_new.html +0 -0
  146. /django_cfg/templates/{admin → admin_old}/layouts/base_dashboard.html +0 -0
  147. /django_cfg/templates/{admin → admin_old}/layouts/dashboard_with_tabs.html +0 -0
  148. /django_cfg/templates/{admin → admin_old}/snippets/components/activity_tracker.html +0 -0
  149. /django_cfg/templates/{admin → admin_old}/snippets/components/charts_section.html +0 -0
  150. /django_cfg/templates/{admin → admin_old}/snippets/components/django_commands.html +0 -0
  151. /django_cfg/templates/{admin → admin_old}/snippets/components/quick_actions.html +0 -0
  152. /django_cfg/templates/{admin → admin_old}/snippets/components/recent_activity_improved.html +0 -0
  153. /django_cfg/templates/{admin → admin_old}/snippets/components/recent_users_table.html +0 -0
  154. /django_cfg/templates/{admin → admin_old}/snippets/components/stats_cards.html +0 -0
  155. /django_cfg/templates/{admin → admin_old}/snippets/components/stats_tiles.html +0 -0
  156. /django_cfg/templates/{admin → admin_old}/snippets/components/system_health.html +0 -0
  157. /django_cfg/templates/{admin → admin_old}/snippets/components/system_metrics.html +0 -0
  158. /django_cfg/templates/{admin → admin_old}/snippets/components/user_permissions.html +0 -0
  159. /django_cfg/templates/{admin → admin_old}/snippets/tabs/app_stats_tab.html +0 -0
  160. /django_cfg/templates/{admin → admin_old}/snippets/tabs/commands_tab.html +0 -0
  161. /django_cfg/templates/{admin → admin_old}/snippets/tabs/documentation_tab.html +0 -0
  162. /django_cfg/templates/{admin → admin_old}/snippets/tabs/overview_tab.html +0 -0
  163. /django_cfg/templates/{admin → admin_old}/snippets/tabs/stats_tab.html +0 -0
  164. /django_cfg/templates/{admin → admin_old}/snippets/tabs/users_tab.html +0 -0
  165. /django_cfg/templates/{admin → admin_old}/snippets/tabs/widgets_tab.html +0 -0
  166. /django_cfg/templates/{admin → admin_old}/snippets/zones/zones_table.html +0 -0
  167. {django_cfg-1.4.83.dist-info → django_cfg-1.4.85.dist-info}/WHEEL +0 -0
  168. {django_cfg-1.4.83.dist-info → django_cfg-1.4.85.dist-info}/entry_points.txt +0 -0
  169. {django_cfg-1.4.83.dist-info → django_cfg-1.4.85.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,73 @@
1
+ """
2
+ System ViewSet
3
+
4
+ Endpoints for system monitoring:
5
+ - GET /system/health/ - System health status
6
+ - GET /system/metrics/ - System performance metrics
7
+ """
8
+
9
+ import logging
10
+
11
+ from drf_spectacular.utils import extend_schema
12
+ from rest_framework import status, viewsets
13
+ from rest_framework.authentication import BasicAuthentication, SessionAuthentication
14
+ from rest_framework.decorators import action
15
+ from rest_framework.permissions import IsAdminUser
16
+ from rest_framework.response import Response
17
+
18
+ from ..services import SystemHealthService, StatisticsService
19
+ from ..serializers import SystemHealthSerializer, SystemMetricsSerializer
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class SystemViewSet(viewsets.GenericViewSet):
25
+ """
26
+ System Monitoring ViewSet
27
+
28
+ Provides endpoints for system health and performance metrics.
29
+ """
30
+
31
+ authentication_classes = [SessionAuthentication, BasicAuthentication]
32
+ permission_classes = [IsAdminUser]
33
+ serializer_class = SystemHealthSerializer
34
+
35
+ @extend_schema(
36
+ summary="Get system health status",
37
+ description="Retrieve overall system health including all component checks",
38
+ responses={200: SystemHealthSerializer},
39
+ tags=["Dashboard - System"]
40
+ )
41
+ @action(detail=False, methods=['get'], url_path='health', serializer_class=SystemHealthSerializer)
42
+ def health(self, request):
43
+ """Get overall system health status."""
44
+ try:
45
+ health_service = SystemHealthService()
46
+ health_data = health_service.get_overall_health_status()
47
+ return Response(health_data)
48
+
49
+ except Exception as e:
50
+ logger.error(f"System health API error: {e}")
51
+ return Response({
52
+ 'error': str(e)
53
+ }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
54
+
55
+ @extend_schema(
56
+ summary="Get system metrics",
57
+ description="Retrieve system performance metrics (CPU, memory, disk, etc.)",
58
+ responses={200: SystemMetricsSerializer},
59
+ tags=["Dashboard - System"]
60
+ )
61
+ @action(detail=False, methods=['get'], url_path='metrics', serializer_class=SystemMetricsSerializer)
62
+ def metrics(self, request):
63
+ """Get system performance metrics."""
64
+ try:
65
+ stats_service = StatisticsService()
66
+ metrics = stats_service.get_system_metrics()
67
+ return Response(metrics)
68
+
69
+ except Exception as e:
70
+ logger.error(f"System metrics API error: {e}")
71
+ return Response({
72
+ 'error': str(e)
73
+ }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@@ -40,6 +40,11 @@ class NextJSStaticView(View):
40
40
  if not path or path == '/':
41
41
  path = 'index.html'
42
42
 
43
+ # Handle trailing slash (Next.js static export behavior)
44
+ # /private/ -> private.html
45
+ if path.endswith('/') and path != '/':
46
+ path = path.rstrip('/') + '.html'
47
+
43
48
  # For routes without extension, try .html (Next.js static export behavior)
44
49
  file_path = base_dir / path
45
50
  if not file_path.exists() and not path.endswith('.html') and '.' not in Path(path).name:
django_cfg/apps/urls.py CHANGED
@@ -84,7 +84,7 @@ def get_default_cfg_group():
84
84
  name="cfg",
85
85
  apps=get_enabled_cfg_apps(),
86
86
  title="Django-CFG API",
87
- description="Authentication (OTP), Support, Newsletter, Leads, Knowledge Base, AI Agents, Tasks, Payments",
87
+ description="Authentication (OTP), Support, Newsletter, Leads, Knowledge Base, AI Agents, Tasks, Payments, Dashboard",
88
88
  version="1.0.0",
89
89
  )
90
90
 
@@ -134,6 +134,7 @@ urlpatterns = [
134
134
  path('cfg/commands/', include('django_cfg.apps.api.commands.urls')),
135
135
  path('cfg/openapi/', include('django_cfg.modules.django_client.urls')),
136
136
  path('cfg/admin/', include('django_cfg.apps.frontend.urls')), # Next.js admin panel
137
+ path('cfg/dashboard/', include('django_cfg.apps.dashboard.urls')), # Dashboard API
137
138
  ]
138
139
 
139
140
  # Django-CFG apps - conditionally registered based on config
@@ -109,6 +109,7 @@ class InstalledAppsBuilder:
109
109
  "django_cfg.modules.django_tailwind", # Universal Tailwind layouts
110
110
  "django_cfg.apps.api.health",
111
111
  "django_cfg.apps.api.commands",
112
+ "django_cfg.apps.dashboard", # Dashboard API
112
113
  ]
113
114
 
114
115
  if self.config.enable_frontend:
@@ -59,16 +59,23 @@ class OpenAPI30Parser(BaseParser):
59
59
  return True
60
60
 
61
61
  # Check anyOf: [{"type": "X"}, {"type": "null"}] format (Pydantic)
62
+ # or anyOf: [{"$ref": "..."}, {"type": "null"}] format
62
63
  if schema.anyOf and len(schema.anyOf) == 2:
63
- types = []
64
+ has_null = False
65
+ has_actual_type = False
66
+
64
67
  for item in schema.anyOf:
65
68
  if not isinstance(item, SchemaObject):
66
69
  continue
67
- if item.base_type:
68
- types.append(item.base_type)
69
70
 
70
- # If one type is 'null' and another is actual type, it's nullable
71
- if 'null' in types and len(types) == 2:
71
+ if item.base_type == 'null':
72
+ has_null = True
73
+ elif item.base_type or item.ref:
74
+ # Has actual type (either base_type or $ref)
75
+ has_actual_type = True
76
+
77
+ # If one is null and another is actual type, it's nullable
78
+ if has_null and has_actual_type:
72
79
  return True
73
80
 
74
81
  return False
@@ -67,16 +67,23 @@ class OpenAPI31Parser(BaseParser):
67
67
  return True
68
68
 
69
69
  # Check anyOf: [{"type": "X"}, {"type": "null"}] format (Pydantic)
70
+ # or anyOf: [{"$ref": "..."}, {"type": "null"}] format
70
71
  if schema.anyOf and len(schema.anyOf) == 2:
71
- types = []
72
+ has_null = False
73
+ has_actual_type = False
74
+
72
75
  for item in schema.anyOf:
73
76
  if not isinstance(item, SchemaObject):
74
77
  continue
75
- if item.base_type:
76
- types.append(item.base_type)
77
78
 
78
- # If one type is 'null' and another is actual type, it's nullable
79
- if 'null' in types and len(types) == 2:
79
+ if item.base_type == 'null':
80
+ has_null = True
81
+ elif item.base_type or item.ref:
82
+ # Has actual type (either base_type or $ref)
83
+ has_actual_type = True
84
+
85
+ # If one is null and another is actual type, it's nullable
86
+ if has_null and has_actual_type:
80
87
  return True
81
88
 
82
89
  return False
@@ -291,12 +291,13 @@ class UnfoldCallbacks(
291
291
  # logger.info(f"Activity tracker data count: {len(activity_tracker_data)}")
292
292
 
293
293
  # Debug: save full rendered page (only in debug mode)
294
- if config and config.debug:
295
- try:
296
- full_html = render_to_string('admin/index.html', context, request)
297
- save_dashboard_render(full_html, name='dashboard_full_page', context=context)
298
- except Exception as e:
299
- logger.error(f"Failed to save full dashboard render: {e}", exc_info=True)
294
+ # DISABLED: Commenting out dashboard render saving to disk
295
+ # if config and config.debug:
296
+ # try:
297
+ # full_html = render_to_string('admin/index.html', context, request)
298
+ # save_dashboard_render(full_html, name='dashboard_full_page', context=context)
299
+ # except Exception as e:
300
+ # logger.error(f"Failed to save full dashboard render: {e}", exc_info=True)
300
301
 
301
302
  return context
302
303
 
@@ -246,42 +246,7 @@ class DashboardManager(BaseCfgModule):
246
246
 
247
247
  # Design system
248
248
  "BORDER_RADIUS": "8px",
249
- "COLORS": {
250
- "base": {
251
- "50": "249, 250, 251",
252
- "100": "243, 244, 246",
253
- "200": "229, 231, 235",
254
- "300": "209, 213, 219",
255
- "400": "156, 163, 175",
256
- "500": "107, 114, 128",
257
- "600": "75, 85, 99",
258
- "700": "55, 65, 81",
259
- "800": "31, 41, 55",
260
- "900": "17, 24, 39",
261
- "950": "3, 7, 18",
262
- },
263
- "primary": {
264
- "50": "239, 246, 255",
265
- "100": "219, 234, 254",
266
- "200": "191, 219, 254",
267
- "300": "147, 197, 253",
268
- "400": "96, 165, 250",
269
- "500": "59, 130, 246",
270
- "600": "37, 99, 235",
271
- "700": "29, 78, 216",
272
- "800": "30, 64, 175",
273
- "900": "30, 58, 138",
274
- "950": "23, 37, 84",
275
- },
276
- "font": {
277
- "subtle-light": "var(--color-base-500)",
278
- "subtle-dark": "var(--color-base-400)",
279
- "default-light": "var(--color-base-600)",
280
- "default-dark": "var(--color-base-300)",
281
- "important-light": "var(--color-base-900)",
282
- "important-dark": "var(--color-base-100)",
283
- },
284
- },
249
+ # COLORS removed - use UnfoldConfig.get_color_scheme() instead
285
250
 
286
251
  # Sidebar navigation - KEY STRUCTURE!
287
252
  "SIDEBAR": {
@@ -305,92 +305,121 @@ class UnfoldConfig(BaseModel):
305
305
  return v
306
306
 
307
307
  def get_color_scheme(self) -> Dict[str, Any]:
308
- """Get Unfold semantic color scheme configuration with theme support."""
308
+ """
309
+ Get Unfold semantic color scheme configuration matching Next.js UI package.
310
+
311
+ Colors are synchronized with:
312
+ - packages/ui/src/styles/theme/light.css
313
+ - packages/ui/src/styles/theme/dark.css
314
+
315
+ This ensures consistent theming between Django Unfold and Next.js iframe.
316
+
317
+ IMPORTANT: Colors must be in OKLCH format for Unfold's color-mix() CSS to work!
318
+ Format: "oklch(lightness% chroma hue)"
319
+ """
309
320
  return {
310
- # Base semantic colors that auto-adapt to theme
321
+ # Base semantic colors - matches Next.js UI package
322
+ # Light theme: Clean whites and neutral grays (Vercel-inspired)
323
+ # Dark theme: True black backgrounds with subtle grays
324
+ # Converted from RGB to OKLCH for color-mix() compatibility
311
325
  "base": {
312
- "50": "248, 250, 252", # Light theme: very light background
313
- "100": "241, 245, 249", # Light theme: light background
314
- "200": "226, 232, 240", # Light theme: subtle border
315
- "300": "203, 213, 225", # Light theme: border
316
- "400": "148, 163, 184", # Light theme: muted text / Dark theme: text
317
- "500": "100, 116, 139", # Neutral - works in both themes
318
- "600": "71, 85, 105", # Dark theme: muted text / Light theme: text
319
- "700": "51, 65, 85", # Dark theme: border
320
- "800": "30, 41, 59", # Dark theme: subtle border
321
- "900": "15, 23, 42", # Dark theme: light background
322
- "950": "2, 6, 23", # Dark theme: very light background
326
+ "50": "oklch(98.5% .002 247.839)", # #f9fafb - Very light background
327
+ "100": "oklch(96.7% .003 264.542)", # #f3f4f6 - Light background (96%)
328
+ "200": "oklch(92.8% .006 264.531)", # #e5e7eb - Subtle border (90%)
329
+ "300": "oklch(87.2% .010 258.338)", # #d1d5db - Border
330
+ "400": "oklch(70.7% .022 261.325)", # #9ca3af - Muted text
331
+ "500": "oklch(55.1% .027 264.364)", # #6b7280 - Neutral
332
+ "600": "oklch(44.6% .030 256.802)", # #4b5563 - Text (9%)
333
+ "700": "oklch(37.3% 0 0)", # Neutral dark gray (no hue)
334
+ "800": "oklch(20.0% 0 0)", # Dark card background (no hue)
335
+ "900": "oklch(14.0% 0 0)", # Main background - near black (14%)
336
+ "950": "oklch(10.0% 0 0)", # Deepest black (10%)
323
337
  },
324
- # Primary brand - auto-adapts via CSS variables
338
+ # Primary brand color - Blue (#3b82f6 / hsl(217 91% 60%))
339
+ # Matches Next.js UI primary color
340
+ # OKLCH format for color-mix() compatibility
325
341
  "primary": {
326
- "50": "239, 246, 255",
327
- "100": "219, 234, 254",
328
- "200": "191, 219, 254",
329
- "300": "147, 197, 253",
330
- "400": "96, 165, 250",
331
- "500": "59, 130, 246", # Main brand color
332
- "600": "37, 99, 235",
333
- "700": "29, 78, 216",
334
- "800": "30, 64, 175",
335
- "900": "30, 58, 138",
336
- "950": "23, 37, 84",
342
+ "50": "oklch(97.0% .014 254.604)", # #eff6ff
343
+ "100": "oklch(93.2% .032 255.585)", # #dbeafe
344
+ "200": "oklch(88.2% .059 254.128)", # #bfdbfe
345
+ "300": "oklch(79.0% .099 253.800)", # #93c5fd
346
+ "400": "oklch(70.7% .165 254.624)", # #60a5fa
347
+ "500": "oklch(62.3% .214 259.815)", # #3b82f6 - Main brand color
348
+ "600": "oklch(54.6% .245 262.881)", # #2563eb
349
+ "700": "oklch(48.8% .243 264.376)", # #1d4ed8
350
+ "800": "oklch(43.0% .223 265.500)", # #1e40af
351
+ "900": "oklch(37.5% .195 266.000)", # #1e3a8a
352
+ "950": "oklch(30.0% .150 267.000)", # #172554
337
353
  },
338
- # Success semantic color
354
+ # Success color - Green
355
+ # OKLCH format for color-mix() compatibility
339
356
  "success": {
340
- "50": "236, 253, 245",
341
- "100": "209, 250, 229",
342
- "200": "167, 243, 208",
343
- "300": "110, 231, 183",
344
- "400": "52, 211, 153",
345
- "500": "16, 185, 129", # Main success color
346
- "600": "5, 150, 105",
347
- "700": "4, 120, 87",
348
- "800": "6, 95, 70",
349
- "900": "6, 78, 59",
350
- "950": "2, 44, 34",
357
+ "50": "oklch(98.0% .029 156.743)", # #f0fdf4
358
+ "100": "oklch(96.2% .044 156.743)", # #dcfce7
359
+ "200": "oklch(92.5% .084 155.995)", # #bbf7d0
360
+ "300": "oklch(87.0% .139 154.500)", # #86efac
361
+ "400": "oklch(79.2% .209 151.711)", # #4ade80
362
+ "500": "oklch(72.3% .219 149.579)", # #22c55e - Main success
363
+ "600": "oklch(62.7% .194 149.214)", # #16a34a
364
+ "700": "oklch(52.7% .154 150.069)", # #15803d
365
+ "800": "oklch(45.0% .125 151.000)", # #166534
366
+ "900": "oklch(38.0% .100 151.500)", # #14532d
367
+ "950": "oklch(25.0% .060 152.000)", # #052e16
351
368
  },
352
- # Warning semantic color
369
+ # Warning color - Amber/Yellow
370
+ # OKLCH format for color-mix() compatibility
353
371
  "warning": {
354
- "50": "255, 251, 235",
355
- "100": "254, 243, 199",
356
- "200": "253, 230, 138",
357
- "300": "252, 211, 77",
358
- "400": "251, 191, 36",
359
- "500": "245, 158, 11", # Main warning color
360
- "600": "217, 119, 6",
361
- "700": "180, 83, 9",
362
- "800": "146, 64, 14",
363
- "900": "120, 53, 15",
364
- "950": "69, 26, 3",
372
+ "50": "oklch(99.0% .020 95.617)", # #fffbeb
373
+ "100": "oklch(96.2% .059 95.617)", # #fef3c7
374
+ "200": "oklch(94.5% .129 101.54)", # #fde68a
375
+ "300": "oklch(89.0% .178 100.000)", # #fcd34d
376
+ "400": "oklch(83.0% .198 95.000)", # #fbbf24
377
+ "500": "oklch(70.5% .213 47.604)", # #f59e0b - Main warning
378
+ "600": "oklch(64.6% .222 41.116)", # #d97706
379
+ "700": "oklch(55.3% .195 38.402)", # #b45309
380
+ "800": "oklch(48.0% .170 37.000)", # #92400e
381
+ "900": "oklch(41.0% .145 38.000)", # #78350f
382
+ "950": "oklch(30.0% .100 40.000)", # #451a03
365
383
  },
366
- # Danger semantic color
384
+ # Danger/Error color - Red (matches destructive color)
385
+ # OKLCH format for color-mix() compatibility
367
386
  "danger": {
368
- "50": "254, 242, 242",
369
- "100": "254, 226, 226",
370
- "200": "254, 202, 202",
371
- "300": "252, 165, 165",
372
- "400": "248, 113, 113",
373
- "500": "239, 68, 68", # Main danger color
374
- "600": "220, 38, 38",
375
- "700": "185, 28, 28",
376
- "800": "153, 27, 27",
377
- "900": "127, 29, 29",
378
- "950": "69, 10, 10",
387
+ "50": "oklch(98.0% .011 17.38)", # #fef2f2
388
+ "100": "oklch(95.5% .027 17.717)", # #fee2e2
389
+ "200": "oklch(93.6% .032 17.717)", # #fecaca
390
+ "300": "oklch(88.5% .062 18.334)", # #fca5a5
391
+ "400": "oklch(80.8% .114 19.571)", # #f87171
392
+ "500": "oklch(63.7% .237 25.331)", # #ef4444 - Main danger
393
+ "600": "oklch(57.7% .245 27.325)", # #dc2626
394
+ "700": "oklch(50.5% .213 27.518)", # #b91c1c
395
+ "800": "oklch(45.0% .190 28.000)", # #991b1b
396
+ "900": "oklch(40.0% .165 28.500)", # #7f1d1d
397
+ "950": "oklch(30.0% .120 29.000)", # #450a0a
379
398
  },
380
- # Info semantic color
399
+ # Info color - Cyan/Sky blue
400
+ # OKLCH format for color-mix() compatibility
381
401
  "info": {
382
- "50": "236, 254, 255",
383
- "100": "207, 250, 254",
384
- "200": "165, 243, 252",
385
- "300": "103, 232, 249",
386
- "400": "34, 211, 238",
387
- "500": "6, 182, 212", # Main info color
388
- "600": "8, 145, 178",
389
- "700": "14, 116, 144",
390
- "800": "21, 94, 117",
391
- "900": "22, 78, 99",
392
- "950": "8, 51, 68",
402
+ "50": "oklch(97.5% .015 230.000)", # #f0f9ff
403
+ "100": "oklch(95.0% .035 230.000)", # #e0f2fe
404
+ "200": "oklch(90.0% .070 225.000)", # #bae6fd
405
+ "300": "oklch(82.0% .120 220.000)", # #7dd3fc
406
+ "400": "oklch(74.0% .155 217.000)", # #38bdf8
407
+ "500": "oklch(67.0% .184 215.000)", # #0ea5e9 - Main info
408
+ "600": "oklch(58.0% .185 218.000)", # #0284c7
409
+ "700": "oklch(49.0% .165 220.000)", # #0369a1
410
+ "800": "oklch(42.0% .140 222.000)", # #075985
411
+ "900": "oklch(36.0% .115 224.000)", # #0c4a6e
412
+ "950": "oklch(28.0% .085 226.000)", # #082f49
393
413
  },
414
+ # Font semantic colors (using OKLCH format)
415
+ "font": {
416
+ "subtle-light": "oklch(55.1% .027 264.364)", # base-500 #6b7280
417
+ "subtle-dark": "oklch(70.7% .022 261.325)", # base-400 #9ca3af
418
+ "default-light": "oklch(44.6% .030 256.802)", # base-600 #4b5563
419
+ "default-dark": "oklch(87.2% .010 258.338)", # base-300 #d1d5db
420
+ "important-light": "oklch(14.0% 0 0)", # base-900 (near black)
421
+ "important-dark": "oklch(96.7% .003 264.542)", # base-100 #f3f4f6
422
+ }
394
423
  }
395
424
 
396
425
  def to_django_settings(self) -> Dict[str, Any]:
@@ -145,52 +145,28 @@ def get_tailwind_config() -> Dict[str, Any]:
145
145
 
146
146
  def get_css_variables() -> str:
147
147
  """
148
- Get CSS variables for semantic colors.
149
-
148
+ Get CSS variables for semantic colors matching Next.js UI package.
149
+
150
+ NOTE: Background color overrides are now in templates/unfold/layouts/skeleton.html
151
+ for better CSS specificity. This function now only provides base color definitions
152
+ and is kept for backward compatibility.
153
+
150
154
  Returns:
151
155
  str: CSS variables as string
152
156
  """
153
157
  return """
154
- /* CSS variables for semantic colors */
155
- :root {
156
- /* Base colors */
157
- --color-base-50: 249, 250, 251;
158
- --color-base-100: 243, 244, 246;
159
- --color-base-200: 229, 231, 235;
160
- --color-base-300: 209, 213, 219;
161
- --color-base-400: 156, 163, 175;
162
- --color-base-500: 107, 114, 128;
163
- --color-base-600: 75, 85, 99;
164
- --color-base-700: 55, 65, 81;
165
- --color-base-800: 31, 41, 55;
166
- --color-base-900: 17, 24, 39;
167
- --color-base-950: 3, 7, 18;
168
-
169
- /* Primary colors */
170
- --color-primary-50: 239, 246, 255;
171
- --color-primary-100: 219, 234, 254;
172
- --color-primary-200: 191, 219, 254;
173
- --color-primary-300: 147, 197, 253;
174
- --color-primary-400: 96, 165, 250;
175
- --color-primary-500: 59, 130, 246;
176
- --color-primary-600: 37, 99, 235;
177
- --color-primary-700: 29, 78, 216;
178
- --color-primary-800: 30, 64, 175;
179
- --color-primary-900: 30, 58, 138;
180
- --color-primary-950: 23, 37, 84;
181
-
182
- /* Font colors for light theme */
183
- --color-font-subtle-light: var(--color-base-500);
184
- --color-font-default-light: var(--color-base-600);
185
- --color-font-important-light: var(--color-base-900);
158
+ /* ============================================== */
159
+ /* CSS Variables - Base Color Definitions */
160
+ /* NOTE: Background overrides in skeleton.html */
161
+ /* ============================================== */
162
+
163
+ /* Tailwind Dark Mode Class Support */
164
+ html.dark {
165
+ color-scheme: dark;
186
166
  }
187
167
 
188
- /* Dark theme */
189
- .dark {
190
- /* Font colors for dark theme */
191
- --color-font-subtle-dark: var(--color-base-400);
192
- --color-font-default-dark: var(--color-base-300);
193
- --color-font-important-dark: var(--color-base-100);
168
+ html:not(.dark) {
169
+ color-scheme: light;
194
170
  }
195
171
 
196
172
  """
@@ -266,44 +242,20 @@ def get_modal_fix_css() -> str:
266
242
 
267
243
  def get_unfold_colors() -> Dict[str, Any]:
268
244
  """
269
- Get color configuration for Unfold settings.
270
-
245
+ Get color configuration for Unfold settings matching Next.js UI package.
246
+
247
+ Colors synchronized with:
248
+ - packages/ui/src/styles/theme/light.css
249
+ - packages/ui/src/styles/theme/dark.css
250
+
251
+ IMPORTANT: Returns OKLCH format for Unfold's color-mix() CSS compatibility.
252
+
271
253
  Returns:
272
- Dict[str, Any]: Color configuration for Unfold
254
+ Dict[str, Any]: Color configuration for Unfold in OKLCH format
273
255
  """
274
- return {
275
- "primary": {
276
- "50": "239, 246, 255",
277
- "100": "219, 234, 254",
278
- "200": "191, 219, 254",
279
- "300": "147, 197, 253",
280
- "400": "96, 165, 250",
281
- "500": "59, 130, 246",
282
- "600": "37, 99, 235",
283
- "700": "29, 78, 216",
284
- "800": "30, 64, 175",
285
- "900": "30, 58, 138",
286
- "950": "23, 37, 84",
287
- },
288
- "base": {
289
- "50": "249, 250, 251",
290
- "100": "243, 244, 246",
291
- "200": "229, 231, 235",
292
- "300": "209, 213, 219",
293
- "400": "156, 163, 175",
294
- "500": "107, 114, 128",
295
- "600": "75, 85, 99",
296
- "700": "55, 65, 81",
297
- "800": "31, 41, 55",
298
- "900": "17, 24, 39",
299
- "950": "3, 7, 18",
300
- },
301
- "font": {
302
- "subtle-light": "var(--color-base-500)",
303
- "subtle-dark": "var(--color-base-400)",
304
- "default-light": "var(--color-base-600)",
305
- "default-dark": "var(--color-base-300)",
306
- "important-light": "var(--color-base-900)",
307
- "important-dark": "var(--color-base-100)",
308
- }
309
- }
256
+ # Import from UnfoldConfig to keep colors in sync
257
+ from .models.config import UnfoldConfig
258
+
259
+ # Create temporary config to get color scheme
260
+ temp_config = UnfoldConfig(site_title="temp")
261
+ return temp_config.get_color_scheme()
django_cfg/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.83"
7
+ version = "1.4.85"
8
8
  description = "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."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "ai-agents", "enterprise-django", "django-settings", "type-safe-config",]