django-cfg 1.3.5__py3-none-any.whl → 1.3.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/apps/urls.py +1 -2
  97. django_cfg/config.py +1 -1
  98. django_cfg/core/config.py +10 -5
  99. django_cfg/core/generation.py +1 -1
  100. django_cfg/management/commands/__init__.py +13 -1
  101. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  102. django_cfg/management/commands/app_agent_generate.py +342 -0
  103. django_cfg/management/commands/app_agent_info.py +308 -0
  104. django_cfg/management/commands/migrate_all.py +9 -3
  105. django_cfg/management/commands/migrator.py +11 -6
  106. django_cfg/management/commands/rundramatiq.py +3 -2
  107. django_cfg/middleware/__init__.py +0 -2
  108. django_cfg/models/api_keys.py +115 -0
  109. django_cfg/modules/django_admin/__init__.py +64 -0
  110. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  111. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  112. django_cfg/modules/django_admin/decorators/display.py +106 -0
  113. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  114. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  115. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  116. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  117. django_cfg/modules/django_admin/models/__init__.py +20 -0
  118. django_cfg/modules/django_admin/models/action_models.py +33 -0
  119. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  120. django_cfg/modules/django_admin/models/base.py +26 -0
  121. django_cfg/modules/django_admin/models/display_models.py +31 -0
  122. django_cfg/modules/django_admin/utils/badges.py +159 -0
  123. django_cfg/modules/django_admin/utils/displays.py +247 -0
  124. django_cfg/modules/django_app_agent/__init__.py +87 -0
  125. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  126. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  127. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  128. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  129. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  130. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  135. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  136. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  137. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  138. django_cfg/modules/django_app_agent/core/config.py +300 -0
  139. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  140. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  141. django_cfg/modules/django_app_agent/models/base.py +283 -0
  142. django_cfg/modules/django_app_agent/models/context.py +496 -0
  143. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  144. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  145. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  146. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  147. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  153. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  154. django_cfg/modules/django_app_agent/services/base.py +437 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  160. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  165. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  171. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  172. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  188. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  195. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  196. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  197. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  198. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  199. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  200. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  201. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  202. django_cfg/modules/django_currency/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  204. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  205. django_cfg/modules/django_currency/core/converter.py +12 -12
  206. django_cfg/modules/django_currency/database/__init__.py +2 -2
  207. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  208. django_cfg/modules/django_llm/llm/client.py +10 -2
  209. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  210. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  211. django_cfg/modules/django_unfold/dashboard.py +14 -13
  212. django_cfg/modules/django_unfold/models/config.py +1 -1
  213. django_cfg/registry/core.py +3 -0
  214. django_cfg/registry/third_party.py +2 -2
  215. django_cfg/template_archive/django_sample.zip +0 -0
  216. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  217. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/RECORD +224 -118
  218. django_cfg/apps/accounts/admin/activity.py +0 -96
  219. django_cfg/apps/accounts/admin/group.py +0 -17
  220. django_cfg/apps/accounts/admin/otp.py +0 -59
  221. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  222. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  223. django_cfg/apps/accounts/admin/user.py +0 -300
  224. django_cfg/apps/agents/core/agent.py +0 -281
  225. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  226. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  227. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  228. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  229. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  230. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  231. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  232. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  236. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  237. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  239. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  242. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  243. django_cfg/apps/tasks/admin.py +0 -320
  244. django_cfg/middleware/static_nocache.py +0 -55
  245. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  249. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  250. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  251. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  252. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -256,12 +256,29 @@ class KnowledgeBaseConfig(BaseModel):
256
256
  return self.embedding.batch_size
257
257
 
258
258
  def get_openai_api_key(self) -> Optional[str]:
259
- """Get OpenAI API key from environment."""
260
- return self.embedding.openai_api_key
259
+ """Get OpenAI API key from django-cfg configuration."""
260
+ try:
261
+ from django_cfg.core.config import get_current_config
262
+ config = get_current_config()
263
+ if config and hasattr(config, 'api_keys') and config.api_keys:
264
+ return config.api_keys.get_openai_key()
265
+ except (ImportError, AttributeError):
266
+ pass
267
+
268
+ return None
269
+
261
270
 
262
271
  def get_openrouter_api_key(self) -> Optional[str]:
263
- """Get OpenRouter API key from environment."""
264
- return self.embedding.openrouter_api_key
272
+ """Get OpenRouter API key from django-cfg configuration."""
273
+ try:
274
+ from django_cfg.core.config import get_current_config
275
+ config = get_current_config()
276
+ if config and hasattr(config, 'api_keys') and config.api_keys:
277
+ return config.api_keys.get_openrouter_key()
278
+ except (ImportError, AttributeError):
279
+ pass
280
+
281
+ return None
265
282
 
266
283
  def get_cache_dir(self) -> Path:
267
284
  """Get cache directory path and ensure it exists."""
@@ -69,6 +69,9 @@ class ChatSessionViewSet(BaseKnowledgeViewSet):
69
69
  is_active_bool = is_active.lower() in ('true', '1', 'yes')
70
70
  queryset = queryset.filter(is_active=is_active_bool)
71
71
 
72
+ # Order by updated_at to avoid pagination warning
73
+ queryset = queryset.order_by('-updated_at')
74
+
72
75
  # Apply pagination
73
76
  page = self.paginate_queryset(queryset)
74
77
  if page is not None:
@@ -1,9 +1,11 @@
1
1
  """
2
- Admin configuration for Leads app.
2
+ Admin configuration for Leads app using Django-CFG admin system.
3
3
  """
4
4
 
5
5
  from .leads_admin import LeadAdmin
6
+ from .resources import LeadResource
6
7
 
7
8
  __all__ = [
8
9
  'LeadAdmin',
10
+ 'LeadResource',
9
11
  ]
@@ -1,77 +1,277 @@
1
+ """
2
+ Leads admin interfaces using Django Admin Utilities.
3
+
4
+ Enhanced lead management with Material Icons and optimized queries.
5
+ """
6
+
1
7
  from django.contrib import admin, messages
2
8
  from django.urls import reverse
3
- from django.utils.html import format_html
9
+ from django.utils.safestring import mark_safe
4
10
  from django.http import HttpResponseRedirect
11
+ from django.db import models
12
+ from django.db.models import Count, Q
5
13
  from unfold.admin import ModelAdmin
6
- from unfold.decorators import action
14
+ from unfold.contrib.filters.admin import AutocompleteSelectFilter
7
15
  from django_cfg import ImportExportModelAdmin, ImportForm, ExportForm
8
16
 
17
+ from django_cfg.modules.django_admin import (
18
+ OptimizedModelAdmin,
19
+ DisplayMixin,
20
+ StatusBadgeConfig,
21
+ DateTimeDisplayConfig,
22
+ Icons,
23
+ ActionVariant,
24
+ display,
25
+ action
26
+ )
27
+ from django_cfg.modules.django_admin.utils.badges import StatusBadge
28
+
9
29
  from ..models import Lead
10
30
  from .resources import LeadResource
11
31
 
12
32
 
13
33
  @admin.register(Lead)
14
- class LeadAdmin(ModelAdmin, ImportExportModelAdmin):
34
+ class LeadAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ImportExportModelAdmin):
35
+ """Admin interface for Lead model using Django Admin Utilities."""
36
+
37
+ # Performance optimization
38
+ select_related_fields = ['user']
39
+
15
40
  # Import/Export configuration
16
41
  resource_class = LeadResource
17
42
  import_form_class = ImportForm
18
43
  export_form_class = ExportForm
44
+
19
45
  list_display = [
20
- 'name', 'email', 'company', 'contact_type', 'contact_value',
21
- 'subject', 'status_display', 'created_at'
46
+ 'name_display', 'email_display', 'company_display', 'contact_type_display',
47
+ 'contact_value_display', 'subject_display', 'status_display', 'user_display', 'created_at_display'
22
48
  ]
23
- list_display_links = ['name', 'email']
49
+ list_display_links = ['name_display', 'email_display']
50
+ ordering = ['-created_at']
24
51
  list_filter = [
25
- 'status', 'contact_type', 'company', 'created_at'
52
+ 'status', 'contact_type', 'company', 'created_at',
53
+ ('user', AutocompleteSelectFilter)
26
54
  ]
27
55
  search_fields = [
28
56
  'name', 'email', 'company', 'company_site',
29
57
  'message', 'subject', 'admin_notes'
30
58
  ]
59
+ autocomplete_fields = ['user']
31
60
  readonly_fields = [
32
61
  'created_at', 'updated_at', 'ip_address', 'user_agent'
33
62
  ]
34
63
 
35
64
  fieldsets = (
36
- ('Basic Information', {
37
- 'fields': ('name', 'email', 'company', 'company_site')
65
+ ('👤 Basic Information', {
66
+ 'fields': ('name', 'email', 'company', 'company_site'),
67
+ 'classes': ('tab',)
38
68
  }),
39
- ('Contact Information', {
40
- 'fields': ('contact_type', 'contact_value')
69
+ ('📞 Contact Information', {
70
+ 'fields': ('contact_type', 'contact_value'),
71
+ 'classes': ('tab',)
41
72
  }),
42
- ('Message', {
43
- 'fields': ('subject', 'message', 'extra')
73
+ ('💬 Message', {
74
+ 'fields': ('subject', 'message', 'extra'),
75
+ 'classes': ('tab',)
44
76
  }),
45
- ('Metadata', {
77
+ ('🔧 Metadata', {
46
78
  'fields': ('site_url', 'ip_address', 'user_agent'),
47
- 'classes': ('collapse',)
79
+ 'classes': ('tab', 'collapse')
48
80
  }),
49
- ('Status and Processing', {
50
- 'fields': ('status', 'user', 'admin_notes')
81
+ ('⚙️ Status and Processing', {
82
+ 'fields': ('status', 'user', 'admin_notes'),
83
+ 'classes': ('tab',)
51
84
  }),
52
- ('Timestamps', {
85
+ ('Timestamps', {
53
86
  'fields': ('created_at', 'updated_at'),
54
- 'classes': ('collapse',)
87
+ 'classes': ('tab', 'collapse')
55
88
  }),
56
89
  )
57
90
 
58
- def status_display(self, obj):
59
- status_colors = {
60
- 'new': '#17a2b8',
61
- 'contacted': '#ffc107',
62
- 'qualified': '#28a745',
63
- 'converted': '#007bff',
64
- 'rejected': '#dc3545'
91
+ list_per_page = 50
92
+ date_hierarchy = 'created_at'
93
+
94
+ actions = ['mark_as_contacted', 'mark_as_qualified', 'mark_as_converted', 'mark_as_rejected']
95
+
96
+ @display(description="Name", ordering="name")
97
+ def name_display(self, obj):
98
+ """Display lead name."""
99
+ if not obj.name:
100
+ return "—"
101
+
102
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.PERSON)
103
+ return StatusBadge.create(
104
+ text=obj.name,
105
+ variant="primary",
106
+ config=config
107
+ )
108
+
109
+ @display(description="Email", ordering="email")
110
+ def email_display(self, obj):
111
+ """Display lead email."""
112
+ if not obj.email:
113
+ return "—"
114
+
115
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.EMAIL)
116
+ return StatusBadge.create(
117
+ text=obj.email,
118
+ variant="info",
119
+ config=config
120
+ )
121
+
122
+ @display(description="Company", ordering="company")
123
+ def company_display(self, obj):
124
+ """Display company name."""
125
+ if not obj.company:
126
+ return "—"
127
+
128
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.BUSINESS)
129
+ return StatusBadge.create(
130
+ text=obj.company,
131
+ variant="secondary",
132
+ config=config
133
+ )
134
+
135
+ @display(description="Contact Type")
136
+ def contact_type_display(self, obj):
137
+ """Display contact type with badge."""
138
+ if not obj.contact_type:
139
+ return "—"
140
+
141
+ type_variants = {
142
+ 'email': 'info',
143
+ 'phone': 'success',
144
+ 'telegram': 'primary',
145
+ 'whatsapp': 'success',
146
+ 'other': 'secondary'
65
147
  }
66
- color = status_colors.get(obj.status, '#6c757d')
67
- return format_html(
68
- '<span style="background: {}; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;">{}</span>',
69
- color, obj.get_status_display()
148
+ variant = type_variants.get(obj.contact_type, 'secondary')
149
+
150
+ type_icons = {
151
+ 'email': Icons.EMAIL,
152
+ 'phone': Icons.PHONE,
153
+ 'telegram': Icons.TELEGRAM,
154
+ 'whatsapp': Icons.WHATSAPP,
155
+ 'other': Icons.CONTACT_PHONE
156
+ }
157
+ icon = type_icons.get(obj.contact_type, Icons.CONTACT_PHONE)
158
+
159
+ config = StatusBadgeConfig(show_icons=True, icon=icon)
160
+ return StatusBadge.create(
161
+ text=obj.get_contact_type_display(),
162
+ variant=variant,
163
+ config=config
70
164
  )
71
- status_display.short_description = 'Status'
72
165
 
73
- list_per_page = 50
74
- date_hierarchy = 'created_at'
166
+ @display(description="Contact Value")
167
+ def contact_value_display(self, obj):
168
+ """Display contact value."""
169
+ if not obj.contact_value:
170
+ return "—"
171
+ return obj.contact_value
172
+
173
+ @display(description="Subject", ordering="subject")
174
+ def subject_display(self, obj):
175
+ """Display subject with truncation."""
176
+ if not obj.subject:
177
+ return "—"
178
+
179
+ subject = obj.subject
180
+ if len(subject) > 50:
181
+ subject = subject[:47] + "..."
182
+
183
+ return subject
184
+
185
+ @display(description="Status")
186
+ def status_display(self, obj):
187
+ """Display lead status with color coding."""
188
+ status_config = StatusBadgeConfig(
189
+ custom_mappings={
190
+ 'new': 'info',
191
+ 'contacted': 'warning',
192
+ 'qualified': 'primary',
193
+ 'converted': 'success',
194
+ 'rejected': 'danger'
195
+ },
196
+ show_icons=True,
197
+ icon=Icons.FLAG if obj.status == 'new' else Icons.PHONE if obj.status == 'contacted' else Icons.VERIFIED if obj.status == 'qualified' else Icons.CHECK_CIRCLE if obj.status == 'converted' else Icons.CANCEL
198
+ )
199
+ return self.display_status_auto(obj, 'status', status_config)
200
+
201
+ @display(description="Assigned User")
202
+ def user_display(self, obj):
203
+ """Display assigned user."""
204
+ if not obj.user:
205
+ return "—"
206
+ return self.display_user_simple(obj.user)
207
+
208
+ @display(description="Created")
209
+ def created_at_display(self, obj):
210
+ """Created time with relative display."""
211
+ config = DateTimeDisplayConfig(show_relative=True)
212
+ return self.display_datetime_relative(obj, 'created_at', config)
75
213
 
76
- def get_queryset(self, request):
77
- return super().get_queryset(request).select_related('user')
214
+ @action(description="Mark as contacted", variant=ActionVariant.WARNING)
215
+ def mark_as_contacted(self, request, queryset):
216
+ """Mark selected leads as contacted."""
217
+ updated = queryset.update(status='contacted')
218
+ messages.warning(request, f"Marked {updated} leads as contacted.")
219
+
220
+ @action(description="Mark as qualified", variant=ActionVariant.PRIMARY)
221
+ def mark_as_qualified(self, request, queryset):
222
+ """Mark selected leads as qualified."""
223
+ updated = queryset.update(status='qualified')
224
+ messages.info(request, f"Marked {updated} leads as qualified.")
225
+
226
+ @action(description="Mark as converted", variant=ActionVariant.SUCCESS)
227
+ def mark_as_converted(self, request, queryset):
228
+ """Mark selected leads as converted."""
229
+ updated = queryset.update(status='converted')
230
+ messages.success(request, f"Marked {updated} leads as converted.")
231
+
232
+ @action(description="Mark as rejected", variant=ActionVariant.DANGER)
233
+ def mark_as_rejected(self, request, queryset):
234
+ """Mark selected leads as rejected."""
235
+ updated = queryset.update(status='rejected')
236
+ messages.error(request, f"Marked {updated} leads as rejected.")
237
+
238
+ def changelist_view(self, request, extra_context=None):
239
+ """Add lead statistics to changelist."""
240
+ extra_context = extra_context or {}
241
+
242
+ queryset = self.get_queryset(request)
243
+ stats = queryset.aggregate(
244
+ total_leads=Count('id'),
245
+ new_leads=Count('id', filter=Q(status='new')),
246
+ contacted_leads=Count('id', filter=Q(status='contacted')),
247
+ qualified_leads=Count('id', filter=Q(status='qualified')),
248
+ converted_leads=Count('id', filter=Q(status='converted')),
249
+ rejected_leads=Count('id', filter=Q(status='rejected'))
250
+ )
251
+
252
+ # Contact type breakdown
253
+ contact_type_counts = dict(
254
+ queryset.values_list('contact_type').annotate(
255
+ count=Count('id')
256
+ )
257
+ )
258
+
259
+ # Company breakdown (top 10)
260
+ company_counts = dict(
261
+ queryset.exclude(company__isnull=True).exclude(company='')
262
+ .values_list('company').annotate(count=Count('id'))
263
+ .order_by('-count')[:10]
264
+ )
265
+
266
+ extra_context['lead_stats'] = {
267
+ 'total_leads': stats['total_leads'] or 0,
268
+ 'new_leads': stats['new_leads'] or 0,
269
+ 'contacted_leads': stats['contacted_leads'] or 0,
270
+ 'qualified_leads': stats['qualified_leads'] or 0,
271
+ 'converted_leads': stats['converted_leads'] or 0,
272
+ 'rejected_leads': stats['rejected_leads'] or 0,
273
+ 'contact_type_counts': contact_type_counts,
274
+ 'company_counts': company_counts
275
+ }
276
+
277
+ return super().changelist_view(request, extra_context)
@@ -1,7 +1,7 @@
1
1
  """
2
- Maintenance admin interfaces.
2
+ Maintenance admin interfaces using Django-CFG admin system.
3
3
 
4
- Decomposed admin interfaces with Unfold styling and action buttons.
4
+ Refactored admin interfaces with Material Icons and optimized queries.
5
5
  """
6
6
 
7
7
  from .api_key_admin import CloudflareApiKeyAdmin