django-cfg 1.3.7__py3-none-any.whl → 1.3.11__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/__init__.py +24 -8
- django_cfg/apps/accounts/admin/activity_admin.py +146 -0
- django_cfg/apps/accounts/admin/filters.py +98 -22
- django_cfg/apps/accounts/admin/group_admin.py +86 -0
- django_cfg/apps/accounts/admin/inlines.py +42 -13
- django_cfg/apps/accounts/admin/otp_admin.py +115 -0
- django_cfg/apps/accounts/admin/registration_admin.py +173 -0
- django_cfg/apps/accounts/admin/resources.py +123 -19
- django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
- django_cfg/apps/accounts/admin/user_admin.py +362 -0
- django_cfg/apps/agents/admin/__init__.py +17 -4
- django_cfg/apps/agents/admin/execution_admin.py +204 -183
- django_cfg/apps/agents/admin/registry_admin.py +230 -255
- django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
- django_cfg/apps/agents/core/__init__.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +221 -0
- django_cfg/apps/agents/core/exceptions.py +14 -0
- django_cfg/apps/agents/core/orchestrator.py +18 -3
- django_cfg/apps/knowbase/admin/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
- django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
- django_cfg/apps/knowbase/admin/document_admin.py +269 -262
- django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
- django_cfg/apps/knowbase/config/settings.py +21 -4
- django_cfg/apps/knowbase/views/chat_views.py +3 -0
- django_cfg/apps/leads/admin/__init__.py +3 -1
- django_cfg/apps/leads/admin/leads_admin.py +235 -35
- django_cfg/apps/maintenance/admin/__init__.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
- django_cfg/apps/maintenance/admin/log_admin.py +143 -61
- django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
- django_cfg/apps/maintenance/admin/site_admin.py +213 -352
- django_cfg/apps/newsletter/admin/__init__.py +29 -2
- django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
- django_cfg/apps/payments/admin/__init__.py +18 -27
- django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
- django_cfg/apps/payments/admin/balance_admin.py +166 -632
- django_cfg/apps/payments/admin/currencies_admin.py +235 -607
- django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
- django_cfg/apps/payments/admin/filters.py +83 -3
- django_cfg/apps/payments/admin/networks_admin.py +269 -0
- django_cfg/apps/payments/admin/payments_admin.py +183 -460
- django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
- django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
- django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
- django_cfg/apps/payments/middleware/api_access.py +32 -6
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/balance.py +12 -0
- django_cfg/apps/payments/models/currencies.py +106 -32
- django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +266 -39
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +303 -41
- django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
- django_cfg/apps/payments/services/providers/models/base.py +145 -0
- django_cfg/apps/payments/services/providers/models/providers.py +87 -0
- django_cfg/apps/payments/services/providers/models/universal.py +48 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
- django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +9 -37
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
- django_cfg/apps/payments/tasks/__init__.py +39 -0
- django_cfg/apps/payments/tasks/types.py +73 -0
- django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
- django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
- django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
- django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
- django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
- django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +8 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +22 -8
- django_cfg/apps/support/admin/__init__.py +10 -1
- django_cfg/apps/support/admin/support_admin.py +338 -141
- django_cfg/apps/tasks/admin/__init__.py +11 -0
- django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/migrate_all.py +9 -3
- django_cfg/management/commands/migrator.py +11 -6
- django_cfg/management/commands/rundramatiq.py +3 -2
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_admin/__init__.py +64 -0
- django_cfg/modules/django_admin/decorators/__init__.py +13 -0
- django_cfg/modules/django_admin/decorators/actions.py +106 -0
- django_cfg/modules/django_admin/decorators/display.py +106 -0
- django_cfg/modules/django_admin/mixins/__init__.py +14 -0
- django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
- django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
- django_cfg/modules/django_admin/models/__init__.py +20 -0
- django_cfg/modules/django_admin/models/action_models.py +33 -0
- django_cfg/modules/django_admin/models/badge_models.py +20 -0
- django_cfg/modules/django_admin/models/base.py +26 -0
- django_cfg/modules/django_admin/models/display_models.py +31 -0
- django_cfg/modules/django_admin/utils/badges.py +159 -0
- django_cfg/modules/django_admin/utils/displays.py +247 -0
- django_cfg/modules/django_currency/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
- django_cfg/modules/django_currency/core/converter.py +12 -12
- django_cfg/modules/django_currency/database/__init__.py +2 -2
- django_cfg/modules/django_currency/database/database_loader.py +93 -42
- django_cfg/modules/django_llm/llm/client.py +10 -2
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
- django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
- django_cfg/modules/django_unfold/dashboard.py +14 -13
- django_cfg/modules/django_unfold/models/config.py +1 -1
- django_cfg/registry/core.py +7 -9
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
- django_cfg/apps/accounts/admin/activity.py +0 -96
- django_cfg/apps/accounts/admin/group.py +0 -17
- django_cfg/apps/accounts/admin/otp.py +0 -59
- django_cfg/apps/accounts/admin/registration_source.py +0 -97
- django_cfg/apps/accounts/admin/twilio_response.py +0 -227
- django_cfg/apps/accounts/admin/user.py +0 -300
- django_cfg/apps/agents/core/agent.py +0 -281
- django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
- django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
- django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
- django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
- django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
- django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
- django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
- django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
- django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
- django_cfg/apps/tasks/admin.py +0 -320
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/middleware/static_nocache.py +0 -55
- django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
- /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.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
|
260
|
-
|
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
|
264
|
-
|
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,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.
|
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.
|
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
|
-
'
|
21
|
-
'
|
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 = ['
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
74
|
-
|
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
|
-
|
77
|
-
|
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
|
-
|
4
|
+
Refactored admin interfaces with Material Icons and optimized queries.
|
5
5
|
"""
|
6
6
|
|
7
7
|
from .api_key_admin import CloudflareApiKeyAdmin
|