django-cfg 1.1.81__py3-none-any.whl → 1.2.0__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.
Files changed (246) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +444 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py → core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +79 -17
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/templates/guide.md +266 -0
  198. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  199. django_cfg/modules/django_unfold/__init__.py +69 -0
  200. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  201. django_cfg/modules/django_unfold/dashboard.py +278 -0
  202. django_cfg/modules/django_unfold/icons/README.md +145 -0
  203. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  204. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  205. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  206. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  207. django_cfg/modules/django_unfold/models/config.py +601 -0
  208. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  209. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  210. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  211. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  212. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  213. django_cfg/modules/django_unfold/utils.py +140 -0
  214. django_cfg/registry/__init__.py +23 -0
  215. django_cfg/registry/core.py +61 -0
  216. django_cfg/registry/exceptions.py +11 -0
  217. django_cfg/registry/modules.py +12 -0
  218. django_cfg/registry/services.py +26 -0
  219. django_cfg/registry/third_party.py +52 -0
  220. django_cfg/routing/__init__.py +19 -0
  221. django_cfg/routing/callbacks.py +198 -0
  222. django_cfg/routing/routers.py +48 -0
  223. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  224. django_cfg/templatetags/__init__.py +0 -0
  225. django_cfg/templatetags/django_cfg.py +33 -0
  226. django_cfg/urls.py +33 -0
  227. django_cfg/utils/path_resolution.py +1 -1
  228. django_cfg/utils/smart_defaults.py +7 -61
  229. django_cfg/utils/toolkit.py +663 -0
  230. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  231. django_cfg-1.2.0.dist-info/RECORD +441 -0
  232. django_cfg/apps/tasks/@docs/README.md +0 -195
  233. django_cfg/archive/django_sample.zip +0 -0
  234. django_cfg/models/unfold.py +0 -271
  235. django_cfg/modules/unfold/__init__.py +0 -29
  236. django_cfg/modules/unfold/dashboard.py +0 -318
  237. django_cfg/pyproject.toml +0 -370
  238. django_cfg/routers.py +0 -83
  239. django_cfg-1.1.81.dist-info/RECORD +0 -278
  240. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  241. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  242. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  243. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  244. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,601 @@
1
+ """
2
+ Unfold Configuration Models
3
+
4
+ Complete configuration models for Django Unfold admin interface.
5
+ """
6
+
7
+ from typing import List, Optional, Dict, Any, Callable
8
+ from pydantic import BaseModel, Field, ConfigDict, field_validator
9
+ from ...base import BaseCfgModule
10
+ from .navigation import NavigationSection
11
+ from .dropdown import SiteDropdownItem
12
+ from .tabs import TabConfiguration
13
+ from ..icons import Icons
14
+ import logging
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+
20
+
21
+ class UnfoldColors(BaseModel):
22
+ """Unfold color theme configuration."""
23
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
24
+
25
+ primary: Optional[str] = Field(None, description="Primary color")
26
+ success: Optional[str] = Field(None, description="Success color")
27
+ warning: Optional[str] = Field(None, description="Warning color")
28
+ danger: Optional[str] = Field(None, description="Danger color")
29
+ info: Optional[str] = Field(None, description="Info color")
30
+
31
+
32
+ class UnfoldSidebar(BaseModel):
33
+ """Unfold sidebar configuration."""
34
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
35
+
36
+ show_search: bool = Field(True, description="Show search in sidebar")
37
+ show_all_applications: bool = Field(True, description="Show all applications")
38
+ navigation: List[Dict[str, Any]] = Field(default_factory=list, description="Custom navigation")
39
+
40
+
41
+ class UnfoldTheme(BaseModel):
42
+ """Complete Unfold theme configuration."""
43
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
44
+
45
+ # Basic theme settings
46
+ site_title: str = Field("Django Admin", description="Site title")
47
+ site_header: str = Field("Django Administration", description="Site header")
48
+ site_url: str = Field("/", description="Site URL")
49
+ site_symbol: str = Field(Icons.ROCKET_LAUNCH, description="Material icon for site")
50
+
51
+ # UI settings
52
+ show_history: bool = Field(True, description="Show history in admin")
53
+ show_view_on_site: bool = Field(True, description="Show view on site links")
54
+ show_back_button: bool = Field(False, description="Show back button")
55
+
56
+ # Theme and appearance
57
+ theme: Optional[str] = Field(None, description="Theme: light, dark, or None for switcher")
58
+ colors: UnfoldColors = Field(default_factory=UnfoldColors, description="Color theme")
59
+ sidebar: UnfoldSidebar = Field(default_factory=UnfoldSidebar, description="Sidebar config")
60
+
61
+ # Dashboard
62
+ dashboard_callback: Optional[str] = Field(None, description="Dashboard callback function")
63
+ environment_callback: Optional[str] = Field(None, description="Environment callback function")
64
+
65
+ # Navigation
66
+ navigation: List[NavigationSection] = Field(default_factory=list, description="Custom navigation")
67
+
68
+ # Site dropdown menu
69
+ site_dropdown: List[SiteDropdownItem] = Field(default_factory=list, description="Site dropdown menu items")
70
+
71
+ def to_django_settings(self) -> Dict[str, Any]:
72
+ """Convert to Django UNFOLD settings."""
73
+ # Try to import colors, fallback to base colors if not available
74
+ try:
75
+ from ..tailwind import get_unfold_colors
76
+ colors = get_unfold_colors()
77
+ except ImportError:
78
+ colors = {
79
+ "primary": {
80
+ "500": "59, 130, 246",
81
+ },
82
+ "base": {
83
+ "500": "107, 114, 128",
84
+ }
85
+ }
86
+
87
+ settings = {
88
+ "SITE_TITLE": self.site_title,
89
+ "SITE_HEADER": self.site_header,
90
+ "SITE_URL": self.site_url,
91
+ "SITE_SYMBOL": self.site_symbol,
92
+ "SHOW_HISTORY": self.show_history,
93
+ "SHOW_VIEW_ON_SITE": self.show_view_on_site,
94
+ "SHOW_BACK_BUTTON": self.show_back_button,
95
+ "COLORS": colors,
96
+ "BORDER_RADIUS": "8px",
97
+ }
98
+
99
+ # Theme settings
100
+ if self.theme:
101
+ settings["THEME"] = self.theme
102
+
103
+ # Sidebar configuration - KEY PART!
104
+ sidebar_config = {
105
+ "show_search": self.sidebar.show_search,
106
+ "command_search": True,
107
+ "show_all_applications": self.sidebar.show_all_applications,
108
+ }
109
+
110
+ # Start with custom navigation from project (if defined)
111
+ nav_items = []
112
+ if self.navigation:
113
+ # Project has custom navigation - add it first
114
+ nav_items.extend([group.to_dict() for group in self.navigation])
115
+
116
+ # Add default navigation from dashboard manager
117
+ try:
118
+ from ..dashboard import DashboardManager
119
+ dashboard = DashboardManager()
120
+ default_nav_items = dashboard.get_navigation_config()
121
+ nav_items.extend(default_nav_items)
122
+ except ImportError:
123
+ pass
124
+
125
+ sidebar_config["navigation"] = nav_items
126
+ settings["SIDEBAR"] = sidebar_config
127
+
128
+ # Command interface
129
+ settings["COMMAND"] = {
130
+ "search_models": True,
131
+ "show_history": True,
132
+ }
133
+
134
+ # Multi-language support - DISABLED
135
+ settings["SHOW_LANGUAGES"] = False
136
+
137
+ # Site dropdown menu
138
+ if self.site_dropdown:
139
+ settings["SITE_DROPDOWN"] = [item.to_dict() for item in self.site_dropdown]
140
+
141
+ # Dashboard callback
142
+ if self.dashboard_callback:
143
+ settings["DASHBOARD_CALLBACK"] = self.dashboard_callback
144
+
145
+ # Environment callback
146
+ if self.environment_callback:
147
+ settings["ENVIRONMENT_CALLBACK"] = self.environment_callback
148
+
149
+ return settings
150
+
151
+
152
+ class UnfoldThemeConfig(UnfoldTheme):
153
+ """Unfold theme configuration."""
154
+ pass
155
+
156
+
157
+ class UnfoldConfig(BaseModel):
158
+ """
159
+ 🎨 Unfold Configuration - Django Unfold admin interface
160
+
161
+ Complete configuration for Django Unfold admin with dashboard,
162
+ navigation, theming, and callback support.
163
+ """
164
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
165
+
166
+ # Site branding
167
+ site_title: str = Field(
168
+ default="ConfigToolkit Admin",
169
+ description="Site title shown in admin"
170
+ )
171
+
172
+ site_header: str = Field(
173
+ default="Django Config Toolkit",
174
+ description="Site header text"
175
+ )
176
+
177
+ site_subheader: str = Field(
178
+ default="🚀 Type-safe Django Configuration",
179
+ description="Site subheader text"
180
+ )
181
+
182
+ site_symbol: str = Field(
183
+ default="settings",
184
+ description="Material icon symbol for site"
185
+ )
186
+
187
+ site_url: str = Field(
188
+ default="/",
189
+ description="Site URL"
190
+ )
191
+
192
+ # UI settings
193
+ show_history: bool = Field(
194
+ default=True,
195
+ description="Show history in admin"
196
+ )
197
+
198
+ show_view_on_site: bool = Field(
199
+ default=True,
200
+ description="Show 'View on site' links"
201
+ )
202
+
203
+ show_back_button: bool = Field(
204
+ default=False,
205
+ description="Show back button in admin"
206
+ )
207
+
208
+ # Theme settings
209
+ theme: Optional[str] = Field(
210
+ default=None,
211
+ description="Theme setting (light/dark/auto)"
212
+ )
213
+
214
+ border_radius: str = Field(
215
+ default="8px",
216
+ description="Border radius for UI elements"
217
+ )
218
+
219
+ # Dashboard settings
220
+ dashboard_enabled: bool = Field(
221
+ default=True,
222
+ description="Enable custom dashboard"
223
+ )
224
+
225
+ dashboard_callback: Optional[str] = Field(
226
+ default="django_cfg.routing.callbacks.dashboard_callback",
227
+ description="Dashboard callback function path"
228
+ )
229
+
230
+ environment_callback: Optional[str] = Field(
231
+ default="django_cfg.routing.callbacks.environment_callback",
232
+ description="Environment callback function path"
233
+ )
234
+
235
+ # Navigation settings
236
+ show_search: bool = Field(
237
+ default=True,
238
+ description="Show search in sidebar"
239
+ )
240
+
241
+ command_search: bool = Field(
242
+ default=True,
243
+ description="Enable command search"
244
+ )
245
+
246
+ show_all_applications: bool = Field(
247
+ default=True,
248
+ description="Show all applications in sidebar"
249
+ )
250
+
251
+ # Multi-language settings
252
+ show_languages: bool = Field(
253
+ default=False,
254
+ description="Show language switcher"
255
+ )
256
+
257
+ # Colors configuration
258
+ colors: Optional[UnfoldColors] = Field(
259
+ default=None,
260
+ description="Color theme configuration"
261
+ )
262
+
263
+ # Sidebar configuration
264
+ sidebar: Optional[UnfoldSidebar] = Field(
265
+ default=None,
266
+ description="Sidebar configuration"
267
+ )
268
+
269
+ # Navigation items
270
+ navigation: List[NavigationSection] = Field(
271
+ default_factory=list,
272
+ description="Custom navigation sections"
273
+ )
274
+
275
+ navigation_items: List[Dict[str, Any]] = Field(
276
+ default_factory=list,
277
+ description="Custom navigation items (legacy)"
278
+ )
279
+
280
+ # Site dropdown items
281
+ site_dropdown: List[SiteDropdownItem] = Field(
282
+ default_factory=list,
283
+ description="Site dropdown menu items"
284
+ )
285
+
286
+ site_dropdown_items: List[Dict[str, Any]] = Field(
287
+ default_factory=list,
288
+ description="Site dropdown menu items (legacy)"
289
+ )
290
+
291
+ # Tab configurations
292
+ tab_configurations: List[Dict[str, Any]] = Field(
293
+ default_factory=list,
294
+ description="Tab configurations for admin"
295
+ )
296
+
297
+ @field_validator('theme')
298
+ @classmethod
299
+ def validate_theme(cls, v: Optional[str]) -> Optional[str]:
300
+ """Validate theme setting."""
301
+ if v and v not in ['light', 'dark', 'auto']:
302
+ raise ValueError("Theme must be 'light', 'dark', 'auto', or None")
303
+ return v
304
+
305
+ def get_color_scheme(self) -> Dict[str, Any]:
306
+ """Get Unfold semantic color scheme configuration with theme support."""
307
+ return {
308
+ # Base semantic colors that auto-adapt to theme
309
+ "base": {
310
+ "50": "248, 250, 252", # Light theme: very light background
311
+ "100": "241, 245, 249", # Light theme: light background
312
+ "200": "226, 232, 240", # Light theme: subtle border
313
+ "300": "203, 213, 225", # Light theme: border
314
+ "400": "148, 163, 184", # Light theme: muted text / Dark theme: text
315
+ "500": "100, 116, 139", # Neutral - works in both themes
316
+ "600": "71, 85, 105", # Dark theme: muted text / Light theme: text
317
+ "700": "51, 65, 85", # Dark theme: border
318
+ "800": "30, 41, 59", # Dark theme: subtle border
319
+ "900": "15, 23, 42", # Dark theme: light background
320
+ "950": "2, 6, 23", # Dark theme: very light background
321
+ },
322
+ # Primary brand - auto-adapts via CSS variables
323
+ "primary": {
324
+ "50": "239, 246, 255",
325
+ "100": "219, 234, 254",
326
+ "200": "191, 219, 254",
327
+ "300": "147, 197, 253",
328
+ "400": "96, 165, 250",
329
+ "500": "59, 130, 246", # Main brand color
330
+ "600": "37, 99, 235",
331
+ "700": "29, 78, 216",
332
+ "800": "30, 64, 175",
333
+ "900": "30, 58, 138",
334
+ "950": "23, 37, 84",
335
+ },
336
+ # Success semantic color
337
+ "success": {
338
+ "50": "236, 253, 245",
339
+ "100": "209, 250, 229",
340
+ "200": "167, 243, 208",
341
+ "300": "110, 231, 183",
342
+ "400": "52, 211, 153",
343
+ "500": "16, 185, 129", # Main success color
344
+ "600": "5, 150, 105",
345
+ "700": "4, 120, 87",
346
+ "800": "6, 95, 70",
347
+ "900": "6, 78, 59",
348
+ "950": "2, 44, 34",
349
+ },
350
+ # Warning semantic color
351
+ "warning": {
352
+ "50": "255, 251, 235",
353
+ "100": "254, 243, 199",
354
+ "200": "253, 230, 138",
355
+ "300": "252, 211, 77",
356
+ "400": "251, 191, 36",
357
+ "500": "245, 158, 11", # Main warning color
358
+ "600": "217, 119, 6",
359
+ "700": "180, 83, 9",
360
+ "800": "146, 64, 14",
361
+ "900": "120, 53, 15",
362
+ "950": "69, 26, 3",
363
+ },
364
+ # Danger semantic color
365
+ "danger": {
366
+ "50": "254, 242, 242",
367
+ "100": "254, 226, 226",
368
+ "200": "254, 202, 202",
369
+ "300": "252, 165, 165",
370
+ "400": "248, 113, 113",
371
+ "500": "239, 68, 68", # Main danger color
372
+ "600": "220, 38, 38",
373
+ "700": "185, 28, 28",
374
+ "800": "153, 27, 27",
375
+ "900": "127, 29, 29",
376
+ "950": "69, 10, 10",
377
+ },
378
+ # Info semantic color
379
+ "info": {
380
+ "50": "236, 254, 255",
381
+ "100": "207, 250, 254",
382
+ "200": "165, 243, 252",
383
+ "300": "103, 232, 249",
384
+ "400": "34, 211, 238",
385
+ "500": "6, 182, 212", # Main info color
386
+ "600": "8, 145, 178",
387
+ "700": "14, 116, 144",
388
+ "800": "21, 94, 117",
389
+ "900": "22, 78, 99",
390
+ "950": "8, 51, 68",
391
+ },
392
+ }
393
+
394
+ def to_django_settings(self) -> Dict[str, Any]:
395
+ """Generate Django settings for Unfold."""
396
+ # Base Unfold configuration
397
+ unfold_settings = {
398
+ "SITE_TITLE": self.site_title,
399
+ "SITE_HEADER": self.site_header,
400
+ "SITE_SUBHEADER": self.site_subheader,
401
+ "SITE_URL": self.site_url,
402
+ "SITE_SYMBOL": self.site_symbol,
403
+ "SHOW_HISTORY": self.show_history,
404
+ "SHOW_VIEW_ON_SITE": self.show_view_on_site,
405
+ "SHOW_BACK_BUTTON": self.show_back_button,
406
+ "THEME": self.theme,
407
+ "BORDER_RADIUS": self.border_radius,
408
+ "SHOW_LANGUAGES": self.show_languages,
409
+ "COLORS": self.get_color_scheme(),
410
+ }
411
+
412
+ # Add callbacks if configured
413
+ if self.dashboard_callback:
414
+ unfold_settings["DASHBOARD_CALLBACK"] = self.dashboard_callback
415
+
416
+ if self.environment_callback:
417
+ unfold_settings["ENVIRONMENT"] = self.environment_callback
418
+
419
+ # Sidebar configuration
420
+ sidebar_config = {
421
+ "show_search": self.show_search,
422
+ "command_search": self.command_search,
423
+ "show_all_applications": self.show_all_applications,
424
+ }
425
+
426
+ # Get default navigation from dashboard manager
427
+ try:
428
+ from ..dashboard import DashboardManager
429
+ dashboard = DashboardManager()
430
+ nav_items = dashboard.get_navigation_config()
431
+ except ImportError:
432
+ nav_items = []
433
+
434
+ # Add custom navigation from project (if defined)
435
+ if self.navigation:
436
+ # Project has custom navigation - add it first
437
+ nav_items.extend([group.to_dict() for group in self.navigation])
438
+
439
+ # Add legacy navigation_items if configured
440
+ if self.navigation_items:
441
+ nav_items.extend(self.navigation_items)
442
+
443
+ sidebar_config["navigation"] = nav_items
444
+ unfold_settings["SIDEBAR"] = sidebar_config
445
+
446
+ # Add site dropdown - combine default from dashboard + project dropdown
447
+ dropdown_items = []
448
+
449
+ # First add default dropdown from dashboard manager
450
+ try:
451
+ from ..dashboard import DashboardManager
452
+ dashboard = DashboardManager()
453
+ dropdown_items.extend(dashboard._get_default_dropdown_items())
454
+ except ImportError:
455
+ pass
456
+
457
+ # Then add project-specific dropdown items
458
+ if self.site_dropdown:
459
+ dropdown_items.extend([item.to_dict() for item in self.site_dropdown])
460
+ elif self.site_dropdown_items:
461
+ dropdown_items.extend(self.site_dropdown_items)
462
+
463
+ if dropdown_items:
464
+ unfold_settings["SITE_DROPDOWN"] = dropdown_items
465
+
466
+ # Add tabs if configured
467
+ if self.tab_configurations:
468
+ unfold_settings["TABS"] = self.tab_configurations
469
+
470
+ # Command interface
471
+ unfold_settings["COMMAND"] = {
472
+ "search_models": True,
473
+ "show_history": True,
474
+ }
475
+
476
+ # Inject universal CSS variables (includes object-tools flex)
477
+ if "STYLES" not in unfold_settings:
478
+ unfold_settings["STYLES"] = []
479
+
480
+ # Add our CSS as inline data URI
481
+ try:
482
+ from ..tailwind import get_css_variables
483
+ import base64
484
+ css_content = get_css_variables()
485
+ css_b64 = base64.b64encode(css_content.encode('utf-8')).decode('utf-8')
486
+ data_uri = f"data:text/css;base64,{css_b64}"
487
+ unfold_settings["STYLES"].append(lambda request: data_uri)
488
+ except ImportError:
489
+ pass
490
+
491
+ return {"UNFOLD": unfold_settings}
492
+
493
+
494
+ class UnfoldDashboardConfig(BaseModel):
495
+ """Complete Unfold dashboard configuration."""
496
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
497
+
498
+ # Site branding
499
+ site_title: str = Field(default="Admin Dashboard", description="Site title")
500
+ site_header: str = Field(default="Admin", description="Site header")
501
+ site_subheader: str = Field(default="Management Interface", description="Site subheader")
502
+ site_url: str = Field(default="/", description="Site URL")
503
+ site_symbol: str = Field(default=Icons.ADMIN_PANEL_SETTINGS, description="Site icon")
504
+
505
+ # UI settings
506
+ show_history: bool = Field(default=True, description="Show history")
507
+ show_view_on_site: bool = Field(default=True, description="Show view on site")
508
+ show_back_button: bool = Field(default=False, description="Show back button")
509
+ theme: Optional[str] = Field(default=None, description="Theme (light/dark) or None for theme switcher")
510
+ show_languages: bool = Field(default=False, description="Show language switcher")
511
+ show_theme_switcher: bool = Field(default=True, description="Show theme switcher (requires theme=None)")
512
+
513
+ # Callbacks
514
+ dashboard_callback: Optional[str] = Field(None, description="Dashboard callback path")
515
+ environment_callback: Optional[str] = Field(None, description="Environment callback path")
516
+
517
+ # Navigation configuration
518
+ navigation_sections: List[NavigationSection] = Field(
519
+ default_factory=list,
520
+ description="Navigation sections"
521
+ )
522
+
523
+ # Site dropdown configuration
524
+ site_dropdown_items: List[SiteDropdownItem] = Field(
525
+ default_factory=list,
526
+ description="Site dropdown items"
527
+ )
528
+
529
+ # Tab configurations
530
+ tab_configurations: List[TabConfiguration] = Field(
531
+ default_factory=list,
532
+ description="Tab configurations"
533
+ )
534
+
535
+ def to_unfold_dict(self) -> Dict[str, Any]:
536
+ """Convert to Unfold configuration dictionary."""
537
+ base_config = {
538
+ "SITE_TITLE": self.site_title,
539
+ "SITE_HEADER": self.site_header,
540
+ "SITE_SUBHEADER": self.site_subheader,
541
+ "SITE_URL": self.site_url,
542
+ "SITE_SYMBOL": self.site_symbol,
543
+ "SHOW_HISTORY": self.show_history,
544
+ "SHOW_VIEW_ON_SITE": self.show_view_on_site,
545
+ "SHOW_BACK_BUTTON": self.show_back_button,
546
+ "SHOW_LANGUAGES": self.show_languages,
547
+ }
548
+
549
+ # Theme configuration: None enables theme switcher, string value forces theme
550
+ if self.show_theme_switcher and self.theme is None:
551
+ # Don't set THEME key - this enables theme switcher
552
+ pass
553
+ else:
554
+ # Set specific theme - this disables theme switcher
555
+ base_config["THEME"] = self.theme
556
+
557
+ # Add callbacks if configured
558
+ if self.dashboard_callback:
559
+ base_config["DASHBOARD_CALLBACK"] = self.dashboard_callback
560
+
561
+ if self.environment_callback:
562
+ base_config["ENVIRONMENT"] = self.environment_callback
563
+
564
+ # Sidebar configuration
565
+ sidebar_config = {
566
+ "show_search": True,
567
+ "command_search": True,
568
+ "show_all_applications": True,
569
+ }
570
+
571
+ # Convert navigation sections
572
+ if self.navigation_sections:
573
+ sidebar_config["navigation"] = [section.to_dict() for section in self.navigation_sections]
574
+
575
+ base_config["SIDEBAR"] = sidebar_config
576
+
577
+ # Convert site dropdown
578
+ if self.site_dropdown_items:
579
+ base_config["SITE_DROPDOWN"] = [item.to_dict() for item in self.site_dropdown_items]
580
+
581
+ # Convert tabs
582
+ if self.tab_configurations:
583
+ tabs = []
584
+ for tab in self.tab_configurations:
585
+ tab_items = []
586
+ for item in tab.items:
587
+ tab_item = {
588
+ "title": item.title,
589
+ "link": item.get_link_for_unfold() if hasattr(item, 'get_link_for_unfold') else item.link,
590
+ }
591
+ if item.permission:
592
+ tab_item["permission"] = item.permission
593
+ tab_items.append(tab_item)
594
+
595
+ tabs.append({
596
+ "models": tab.models,
597
+ "items": tab_items,
598
+ })
599
+ base_config["TABS"] = tabs
600
+
601
+ return base_config