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.
Files changed (246) 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 +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  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 +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,10 @@
1
1
  """
2
- Toolsets admin interfaces with Unfold optimization.
2
+ Toolsets admin interfaces using Django Admin Utilities.
3
+
4
+ Enhanced toolset management with Material Icons and optimized queries.
3
5
  """
4
6
 
5
7
  from django.contrib import admin, messages
6
- from django.utils.html import format_html
7
8
  from django.urls import reverse
8
9
  from django.utils.safestring import mark_safe
9
10
  from django.db import models
@@ -13,38 +14,51 @@ from django.db.models.fields.json import JSONField
13
14
  from datetime import timedelta
14
15
  from django_json_widget.widgets import JSONEditorWidget
15
16
  from unfold.admin import ModelAdmin, TabularInline
16
- from unfold.decorators import display, action
17
- from unfold.enums import ActionVariant
18
17
  from unfold.contrib.filters.admin import AutocompleteSelectFilter, AutocompleteSelectMultipleFilter
19
18
  from unfold.contrib.forms.widgets import WysiwygWidget
20
- from django_cfg import ExportMixin, ImportExportModelAdmin, ImportForm, ExportForm
19
+ from django_cfg import ExportMixin, ExportForm
20
+
21
+ from django_cfg.modules.django_admin import (
22
+ OptimizedModelAdmin,
23
+ DisplayMixin,
24
+ MoneyDisplayConfig,
25
+ StatusBadgeConfig,
26
+ DateTimeDisplayConfig,
27
+ Icons,
28
+ ActionVariant,
29
+ display,
30
+ action
31
+ )
32
+ from django_cfg.modules.django_admin.utils.badges import StatusBadge
21
33
 
22
34
  from ..models.toolsets import ToolExecution, ApprovalLog, ToolsetConfiguration
23
35
 
24
36
 
25
37
  @admin.register(ToolExecution)
26
- class ToolExecutionAdmin(ModelAdmin, ExportMixin):
27
- """Admin interface for ToolExecution with Unfold styling."""
38
+ class ToolExecutionAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
39
+ """Enhanced admin for ToolExecution model using Django Admin Utilities."""
40
+
41
+ # Performance optimization
42
+ select_related_fields = ['agent_execution', 'approval_log']
28
43
 
29
44
  # Export-only configuration
30
45
  export_form_class = ExportForm
31
46
 
32
47
  list_display = [
33
- 'id_display', 'tool_name_display', 'toolset_badge', 'status_badge', 'user',
34
- 'execution_metrics', 'retry_badge', 'created_at'
48
+ 'id_display', 'tool_name_display', 'toolset_display', 'status_display',
49
+ 'duration_display', 'retry_count_display', 'created_at_display'
35
50
  ]
36
- ordering = ['-created_at']
51
+ list_display_links = ['id_display', 'tool_name_display']
37
52
  list_filter = [
38
- 'status', 'tool_name', 'toolset_name', 'created_at',
39
- ('user', AutocompleteSelectFilter),
53
+ 'status', 'tool_name', 'created_at',
40
54
  ('agent_execution', AutocompleteSelectFilter)
41
55
  ]
42
- search_fields = ['tool_name', 'toolset_name', 'user__username', 'arguments', 'result']
43
- autocomplete_fields = ['user', 'agent_execution']
56
+ search_fields = ['tool_name', 'toolset_name', 'arguments', 'result']
57
+ autocomplete_fields = ['agent_execution']
44
58
  readonly_fields = [
45
- 'id', 'execution_time', 'created_at', 'started_at', 'completed_at',
46
- 'duration_display', 'arguments_preview', 'result_preview', 'error_preview'
59
+ 'id', 'execution_time', 'retry_count', 'created_at', 'started_at', 'completed_at'
47
60
  ]
61
+ ordering = ['-created_at']
48
62
 
49
63
  # Unfold form field overrides
50
64
  formfield_overrides = {
@@ -53,177 +67,146 @@ class ToolExecutionAdmin(ModelAdmin, ExportMixin):
53
67
  }
54
68
 
55
69
  fieldsets = (
56
- ("🛠️ Tool Info", {
57
- 'fields': ('id', 'tool_name', 'toolset_name', 'user', 'status'),
70
+ ("🔧 Tool Info", {
71
+ 'fields': ('id', 'tool_name', 'toolset_class', 'agent_execution'),
58
72
  'classes': ('tab',)
59
73
  }),
60
74
  ("📝 Execution Data", {
61
- 'fields': ('arguments_preview', 'arguments', 'result_preview', 'result', 'error_preview', 'error_message'),
75
+ 'fields': ('arguments', 'result', 'error_message'),
62
76
  'classes': ('tab',)
63
77
  }),
64
78
  ("📊 Metrics", {
65
- 'fields': ('execution_time', 'retry_count'),
79
+ 'fields': ('execution_time', 'retry_count', 'status'),
66
80
  'classes': ('tab',)
67
81
  }),
68
- ("🔗 Context", {
69
- 'fields': ('agent_execution',),
82
+ ("🔐 Approval", {
83
+ 'fields': ('approval_log',),
70
84
  'classes': ('tab', 'collapse')
71
85
  }),
72
86
  ("⏰ Timestamps", {
73
- 'fields': ('created_at', 'started_at', 'completed_at', 'duration_display'),
87
+ 'fields': ('created_at', 'started_at', 'completed_at'),
74
88
  'classes': ('tab', 'collapse')
75
89
  }),
76
90
  )
77
91
 
78
- actions = ['retry_failed_tools', 'clear_errors']
92
+ actions = ['retry_failed_executions', 'clear_errors']
79
93
 
80
94
  @display(description="ID")
81
95
  def id_display(self, obj):
82
96
  """Enhanced ID display."""
83
- return format_html(
84
- '<span class="font-mono text-sm text-gray-600">#{}</span>',
85
- str(obj.id)[:8]
97
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.TAG)
98
+ return StatusBadge.create(
99
+ text=f"#{str(obj.id)[:8]}",
100
+ variant="secondary",
101
+ config=config
86
102
  )
87
103
 
88
104
  @display(description="Tool")
89
105
  def tool_name_display(self, obj):
90
106
  """Enhanced tool name display."""
91
- return format_html(
92
- '<div class="flex items-center space-x-2">'
93
- '<span class="text-orange-600 font-medium">{}</span>'
94
- '</div>',
95
- obj.tool_name
107
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.BUILD)
108
+ return StatusBadge.create(
109
+ text=obj.tool_name,
110
+ variant="primary",
111
+ config=config
96
112
  )
97
113
 
98
114
  @display(description="Toolset")
99
- def toolset_badge(self, obj):
100
- """Toolset badge."""
101
- if not obj.toolset_name:
102
- return "-"
103
- return format_html(
104
- '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-amber-100 text-amber-800">{}</span>',
105
- obj.toolset_name
115
+ def toolset_display(self, obj):
116
+ """Toolset class display with badge."""
117
+ if not obj.toolset_class:
118
+ return ""
119
+
120
+ # Extract class name from full path
121
+ class_name = obj.toolset_class.split('.')[-1] if '.' in obj.toolset_class else obj.toolset_class
122
+
123
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.EXTENSION)
124
+ return StatusBadge.create(
125
+ text=class_name,
126
+ variant="info",
127
+ config=config
106
128
  )
107
129
 
108
130
  @display(description="Status")
109
- def status_badge(self, obj):
110
- """Status badge with color coding."""
111
- colors = {
112
- 'pending': 'bg-yellow-100 text-yellow-800',
113
- 'running': 'bg-blue-100 text-blue-800',
114
- 'completed': 'bg-green-100 text-green-800',
115
- 'failed': 'bg-red-100 text-red-800',
116
- 'cancelled': 'bg-gray-100 text-gray-800'
117
- }
118
- color_class = colors.get(obj.status, 'bg-gray-100 text-gray-800')
119
- return format_html(
120
- '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {}">{}</span>',
121
- color_class, obj.get_status_display()
131
+ def status_display(self, obj):
132
+ """Status display with appropriate icons."""
133
+ status_config = StatusBadgeConfig(
134
+ custom_mappings={
135
+ 'pending': 'warning',
136
+ 'running': 'info',
137
+ 'completed': 'success',
138
+ 'failed': 'danger',
139
+ 'cancelled': 'secondary'
140
+ },
141
+ show_icons=True,
142
+ icon=Icons.PLAY_ARROW if obj.status == 'running' else Icons.CHECK_CIRCLE if obj.status == 'completed' else Icons.ERROR if obj.status == 'failed' else Icons.SCHEDULE
122
143
  )
144
+ return self.display_status_auto(obj, 'status', status_config)
123
145
 
124
- @display(description="Metrics")
125
- def execution_metrics(self, obj):
126
- """Combined execution metrics."""
127
- return format_html(
128
- '<div class="text-sm space-y-1">'
129
- '<div><span class="font-medium">Time:</span> {}</div>'
130
- '</div>',
131
- f"{obj.execution_time:.3f}s" if obj.execution_time else "-"
132
- )
146
+ @display(description="Duration")
147
+ def duration_display(self, obj):
148
+ """Execution duration display."""
149
+ if obj.execution_time:
150
+ return f"{obj.execution_time:.3f}s"
151
+ return ""
133
152
 
134
153
  @display(description="Retries")
135
- def retry_badge(self, obj):
136
- """Retry count badge."""
154
+ def retry_count_display(self, obj):
155
+ """Retry count display with badge."""
137
156
  if obj.retry_count > 0:
138
- return format_html(
139
- '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-orange-100 text-orange-800">{}</span>',
140
- obj.retry_count
157
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.REFRESH)
158
+ variant = "warning" if obj.retry_count > 2 else "info"
159
+ return StatusBadge.create(
160
+ text=str(obj.retry_count),
161
+ variant=variant,
162
+ config=config
141
163
  )
142
- return format_html(
143
- '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">0</span>'
144
- )
164
+ return "0"
145
165
 
146
- @display(description="Duration")
147
- def duration_display(self, obj):
148
- """Display execution duration."""
149
- if obj.duration:
150
- return f"{obj.duration:.3f}s"
151
- return "-"
152
-
153
- @display(description="Arguments Preview")
154
- def arguments_preview(self, obj):
155
- """Preview of arguments."""
156
- if not obj.arguments:
157
- return "-"
158
- preview = str(obj.arguments)[:200] + "..." if len(str(obj.arguments)) > 200 else str(obj.arguments)
159
- return format_html(
160
- '<div class="text-sm text-gray-600 max-w-md">{}</div>',
161
- preview
162
- )
163
-
164
- @display(description="Result Preview")
165
- def result_preview(self, obj):
166
- """Preview of result."""
167
- if not obj.result:
168
- return "-"
169
- preview = str(obj.result)[:200] + "..." if len(str(obj.result)) > 200 else str(obj.result)
170
- return format_html(
171
- '<div class="text-sm text-gray-600 max-w-md">{}</div>',
172
- preview
173
- )
166
+ @display(description="Created")
167
+ def created_at_display(self, obj):
168
+ """Created time with relative display."""
169
+ config = DateTimeDisplayConfig(show_relative=True)
170
+ return self.display_datetime_relative(obj, 'created_at', config)
174
171
 
175
- @display(description="Error Preview")
176
- def error_preview(self, obj):
177
- """Preview of error message."""
178
- if not obj.error_message:
179
- return "-"
180
- preview = obj.error_message[:200] + "..." if len(obj.error_message) > 200 else obj.error_message
181
- return format_html(
182
- '<div class="text-sm text-red-600 max-w-md">{}</div>',
183
- preview
184
- )
185
-
186
- @action(description="Retry failed tools", icon="refresh", variant=ActionVariant.WARNING)
187
- def retry_failed_tools(self, request, queryset):
172
+ @action(description="Retry failed executions", variant=ActionVariant.WARNING)
173
+ def retry_failed_executions(self, request, queryset):
188
174
  """Retry failed tool executions."""
189
175
  failed_count = queryset.filter(status='failed').count()
190
- messages.warning(request, f"Retry functionality not implemented yet. {failed_count} failed tools selected.")
176
+ messages.warning(request, f"Retry functionality not implemented yet. {failed_count} failed executions selected.")
191
177
 
192
- @action(description="Clear errors", icon="clear", variant=ActionVariant.INFO)
178
+ @action(description="Clear error messages", variant=ActionVariant.INFO)
193
179
  def clear_errors(self, request, queryset):
194
- """Clear error messages."""
195
- error_count = queryset.exclude(error_message__isnull=True).exclude(error_message='').count()
196
- messages.info(request, f"Error clearing not implemented yet. {error_count} tools with errors selected.")
197
-
198
- def get_queryset(self, request):
199
- """Optimize queryset."""
200
- return super().get_queryset(request).select_related('user', 'agent_execution')
180
+ """Clear error messages from executions."""
181
+ updated = queryset.update(error_message=None)
182
+ messages.info(request, f"Cleared error messages from {updated} executions.")
201
183
 
202
184
 
203
185
  @admin.register(ApprovalLog)
204
- class ApprovalLogAdmin(ModelAdmin, ExportMixin):
205
- """Admin interface for ApprovalLog with Unfold styling."""
186
+ class ApprovalLogAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
187
+ """Enhanced admin for ApprovalLog model using Django Admin Utilities."""
188
+
189
+ # Performance optimization
190
+ select_related_fields = ['approved_by']
206
191
 
207
192
  # Export-only configuration
208
193
  export_form_class = ExportForm
209
194
 
210
195
  list_display = [
211
- 'approval_id_display', 'tool_name_display', 'status_badge', 'user',
212
- 'decision_info', 'time_metrics', 'expiry_status', 'requested_at'
196
+ 'approval_id_display', 'tool_name_display', 'status_display',
197
+ 'approved_by_display', 'decision_time_display', 'expires_at_display'
213
198
  ]
214
- ordering = ['-requested_at']
199
+ list_display_links = ['approval_id_display', 'tool_name_display']
215
200
  list_filter = [
216
- 'status', 'tool_name', 'requested_at',
217
- ('user', AutocompleteSelectFilter),
218
- ('approved_by', AutocompleteSelectFilter),
219
- ('rejected_by', AutocompleteSelectFilter)
201
+ 'status', 'tool_name', 'requested_at', 'expires_at',
202
+ ('approved_by', AutocompleteSelectFilter)
220
203
  ]
221
- search_fields = ['approval_id', 'tool_name', 'user__username', 'justification']
222
- autocomplete_fields = ['user', 'approved_by', 'rejected_by']
204
+ search_fields = ['tool_name', 'tool_args', 'justification']
205
+ autocomplete_fields = ['approved_by']
223
206
  readonly_fields = [
224
- 'approval_id', 'requested_at', 'decided_at', 'time_to_decision',
225
- 'is_expired', 'tool_args_preview', 'justification_preview'
207
+ 'id', 'requested_at', 'decided_at', 'expires_at'
226
208
  ]
209
+ ordering = ['-requested_at']
227
210
 
228
211
  # Unfold form field overrides
229
212
  formfield_overrides = {
@@ -232,170 +215,134 @@ class ApprovalLogAdmin(ModelAdmin, ExportMixin):
232
215
  }
233
216
 
234
217
  fieldsets = (
235
- (" Approval Info", {
236
- 'fields': ('approval_id', 'tool_name', 'status', 'user'),
218
+ ("🔐 Approval Info", {
219
+ 'fields': ('id', 'tool_name', 'status', 'approved_by'),
237
220
  'classes': ('tab',)
238
221
  }),
239
222
  ("📝 Request Details", {
240
- 'fields': ('tool_args_preview', 'tool_args', 'justification_preview', 'justification'),
223
+ 'fields': ('tool_arguments', 'justification'),
241
224
  'classes': ('tab',)
242
225
  }),
243
- ("🎯 Decision", {
244
- 'fields': ('approved_by', 'rejected_by', 'rejection_reason'),
226
+ (" Timing", {
227
+ 'fields': ('created_at', 'decision_time', 'expires_at'),
245
228
  'classes': ('tab',)
246
229
  }),
247
- ("⏰ Timestamps", {
248
- 'fields': ('requested_at', 'decided_at', 'expires_at', 'time_to_decision', 'is_expired'),
249
- 'classes': ('tab', 'collapse')
250
- }),
251
230
  )
252
231
 
253
- actions = ['approve_selected', 'reject_selected', 'extend_expiry']
232
+ actions = ['approve_pending', 'reject_pending', 'extend_expiry']
254
233
 
255
234
  @display(description="Approval ID")
256
235
  def approval_id_display(self, obj):
257
236
  """Enhanced approval ID display."""
258
- return format_html(
259
- '<span class="font-mono text-sm text-blue-600">#{}</span>',
260
- obj.approval_id[:8] if obj.approval_id else "N/A"
237
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.VERIFIED)
238
+ return StatusBadge.create(
239
+ text=f"#{str(obj.id)[:8]}",
240
+ variant="secondary",
241
+ config=config
261
242
  )
262
243
 
263
244
  @display(description="Tool")
264
245
  def tool_name_display(self, obj):
265
246
  """Enhanced tool name display."""
266
- return format_html(
267
- '<div class="flex items-center space-x-2">'
268
- '<span class="text-purple-600 font-medium">{}</span>'
269
- '</div>',
270
- obj.tool_name
247
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.BUILD)
248
+ return StatusBadge.create(
249
+ text=obj.tool_name,
250
+ variant="primary",
251
+ config=config
271
252
  )
272
253
 
273
254
  @display(description="Status")
274
- def status_badge(self, obj):
275
- """Status badge with color coding."""
276
- colors = {
277
- 'pending': 'bg-yellow-100 text-yellow-800',
278
- 'approved': 'bg-green-100 text-green-800',
279
- 'rejected': 'bg-red-100 text-red-800',
280
- 'expired': 'bg-gray-100 text-gray-800'
281
- }
282
- color_class = colors.get(obj.status, 'bg-gray-100 text-gray-800')
283
- return format_html(
284
- '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {}">{}</span>',
285
- color_class, obj.get_status_display()
255
+ def status_display(self, obj):
256
+ """Status display with appropriate icons."""
257
+ status_config = StatusBadgeConfig(
258
+ custom_mappings={
259
+ 'pending': 'warning',
260
+ 'approved': 'success',
261
+ 'rejected': 'danger',
262
+ 'expired': 'secondary'
263
+ },
264
+ show_icons=True,
265
+ icon=Icons.CHECK_CIRCLE if obj.status == 'approved' else Icons.CANCEL if obj.status == 'rejected' else Icons.SCHEDULE if obj.status == 'pending' else Icons.TIMER_OFF
286
266
  )
287
-
288
- @display(description="Decision")
289
- def decision_info(self, obj):
290
- """Decision information."""
291
- if obj.approved_by:
292
- return format_html(
293
- '<div class="text-sm">'
294
- '<div class="text-green-600 font-medium">✓ Approved by</div>'
295
- '<div class="text-gray-600">{}</div>'
296
- '</div>',
297
- obj.approved_by.username
298
- )
299
- elif obj.rejected_by:
300
- return format_html(
301
- '<div class="text-sm">'
302
- '<div class="text-red-600 font-medium">✗ Rejected by</div>'
303
- '<div class="text-gray-600">{}</div>'
304
- '</div>',
305
- obj.rejected_by.username
306
- )
307
- return format_html(
308
- '<div class="text-sm text-yellow-600">⏳ Pending</div>'
309
- )
310
-
311
- @display(description="Time Metrics")
312
- def time_metrics(self, obj):
313
- """Time-related metrics."""
314
- decision_time = f"{obj.time_to_decision:.1f}s" if obj.time_to_decision else "N/A"
315
- return format_html(
316
- '<div class="text-sm space-y-1">'
317
- '<div><span class="font-medium">Decision:</span> {}</div>'
318
- '</div>',
319
- decision_time
320
- )
321
-
322
- @display(description="Expiry", boolean=True)
323
- def expiry_status(self, obj):
324
- """Expiry status."""
325
- return not obj.is_expired
326
-
327
- @display(description="Tool Args Preview")
328
- def tool_args_preview(self, obj):
329
- """Preview of tool arguments."""
330
- if not obj.tool_args:
331
- return "-"
332
- preview = str(obj.tool_args)[:200] + "..." if len(str(obj.tool_args)) > 200 else str(obj.tool_args)
333
- return format_html(
334
- '<div class="text-sm text-gray-600 max-w-md">{}</div>',
335
- preview
267
+ return self.display_status_auto(obj, 'status', status_config)
268
+
269
+ @display(description="Approved By")
270
+ def approved_by_display(self, obj):
271
+ """Approved by user display."""
272
+ if not obj.approved_by:
273
+ return ""
274
+ return self.display_user_simple(obj.approved_by)
275
+
276
+ @display(description="Decision Time")
277
+ def decision_time_display(self, obj):
278
+ """Decision time display."""
279
+ if obj.decision_time:
280
+ return f"{obj.decision_time:.2f}s"
281
+ return ""
282
+
283
+ @display(description="Expires")
284
+ def expires_at_display(self, obj):
285
+ """Expiry time with relative display."""
286
+ if not obj.expires_at:
287
+ return "—"
288
+
289
+ config = DateTimeDisplayConfig(show_relative=True)
290
+ return self.display_datetime_relative(obj, 'expires_at', config)
291
+
292
+ @action(description="Approve pending", variant=ActionVariant.SUCCESS)
293
+ def approve_pending(self, request, queryset):
294
+ """Approve pending approvals."""
295
+ updated = queryset.filter(status='pending').update(
296
+ status='approved',
297
+ approved_by=request.user,
298
+ decision_time=timezone.now()
336
299
  )
337
-
338
- @display(description="Justification Preview")
339
- def justification_preview(self, obj):
340
- """Preview of justification."""
341
- if not obj.justification:
342
- return "-"
343
- preview = obj.justification[:200] + "..." if len(obj.justification) > 200 else obj.justification
344
- return format_html(
345
- '<div class="text-sm text-gray-600 max-w-md">{}</div>',
346
- preview
300
+ messages.success(request, f"Approved {updated} pending requests.")
301
+
302
+ @action(description="Reject pending", variant=ActionVariant.DANGER)
303
+ def reject_pending(self, request, queryset):
304
+ """Reject pending approvals."""
305
+ updated = queryset.filter(status='pending').update(
306
+ status='rejected',
307
+ approved_by=request.user,
308
+ decision_time=timezone.now()
347
309
  )
310
+ messages.warning(request, f"Rejected {updated} pending requests.")
348
311
 
349
- @action(description="Approve selected requests", icon="check", variant=ActionVariant.SUCCESS)
350
- def approve_selected(self, request, queryset):
351
- """Approve selected requests."""
352
- count = 0
353
- for approval in queryset.filter(status='pending'):
354
- approval.approve(request.user)
355
- count += 1
356
-
357
- messages.success(request, f"Approved {count} requests.")
358
-
359
- @action(description="Reject selected requests", icon="close", variant=ActionVariant.DANGER)
360
- def reject_selected(self, request, queryset):
361
- """Reject selected requests."""
362
- count = 0
363
- for approval in queryset.filter(status='pending'):
364
- approval.reject(request.user, "Bulk rejection by admin")
365
- count += 1
366
-
367
- messages.warning(request, f"Rejected {count} requests.")
368
-
369
- @action(description="Extend expiry", icon="schedule", variant=ActionVariant.INFO)
312
+ @action(description="Extend expiry", variant=ActionVariant.INFO)
370
313
  def extend_expiry(self, request, queryset):
371
- """Extend expiry time for selected requests."""
372
- pending_count = queryset.filter(status='pending').count()
373
- messages.info(request, f"Expiry extension not implemented yet. {pending_count} pending requests selected.")
374
-
375
- def get_queryset(self, request):
376
- """Optimize queryset."""
377
- return super().get_queryset(request).select_related('user', 'approved_by', 'rejected_by')
314
+ """Extend expiry time for pending approvals."""
315
+ from datetime import timedelta
316
+ new_expiry = timezone.now() + timedelta(hours=24)
317
+ updated = queryset.filter(status='pending').update(expires_at=new_expiry)
318
+ messages.info(request, f"Extended expiry for {updated} approvals by 24 hours.")
378
319
 
379
320
 
380
321
  @admin.register(ToolsetConfiguration)
381
- class ToolsetConfigurationAdmin(ModelAdmin, ImportExportModelAdmin):
382
- """Admin interface for ToolsetConfiguration with Unfold styling."""
322
+ class ToolsetConfigurationAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin, ExportMixin):
323
+ """Enhanced admin for ToolsetConfiguration model using Django Admin Utilities."""
324
+
325
+ # Performance optimization
326
+ select_related_fields = ['created_by']
383
327
 
384
- # Import/Export configuration
385
- import_form_class = ImportForm
328
+ # Export-only configuration
386
329
  export_form_class = ExportForm
387
330
 
388
331
  list_display = [
389
- 'name_display', 'toolset_class_badge', 'status_badge', 'usage_info', 'created_by', 'created_at'
332
+ 'name_display', 'toolset_class_display', 'status_display',
333
+ 'usage_count_display', 'created_by_display', 'created_at_display'
390
334
  ]
391
- ordering = ['-created_at']
335
+ list_display_links = ['name_display']
392
336
  list_filter = [
393
- 'is_active', 'created_at',
337
+ 'is_active', 'toolset_class', 'created_at',
394
338
  ('created_by', AutocompleteSelectFilter)
395
339
  ]
396
340
  search_fields = ['name', 'description', 'toolset_class']
397
- autocomplete_fields = ['created_by', 'allowed_users', 'allowed_groups']
398
- readonly_fields = ['created_at', 'updated_at', 'config_preview']
341
+ autocomplete_fields = ['created_by']
342
+ readonly_fields = [
343
+ 'id', 'created_at', 'updated_at'
344
+ ]
345
+ ordering = ['-created_at']
399
346
 
400
347
  # Unfold form field overrides
401
348
  formfield_overrides = {
@@ -404,90 +351,96 @@ class ToolsetConfigurationAdmin(ModelAdmin, ImportExportModelAdmin):
404
351
  }
405
352
 
406
353
  fieldsets = (
407
- ("🔧 Basic Information", {
408
- 'fields': ('name', 'description', 'toolset_class'),
354
+ ("⚙️ Configuration Info", {
355
+ 'fields': ('id', 'name', 'description', 'toolset_class'),
409
356
  'classes': ('tab',)
410
357
  }),
411
- ("⚙️ Configuration", {
412
- 'fields': ('config_preview', 'config'),
358
+ ("🔧 Settings", {
359
+ 'fields': ('configuration', 'is_active'),
413
360
  'classes': ('tab',)
414
361
  }),
415
- ("🔐 Access Control", {
416
- 'fields': ('is_active', 'allowed_users', 'allowed_groups'),
362
+ ("📊 Usage", {
363
+ 'fields': ('usage_count',),
417
364
  'classes': ('tab',)
418
365
  }),
419
- ("📝 Metadata", {
420
- 'fields': ('created_by', 'created_at', 'updated_at'),
366
+ ("👤 Metadata", {
367
+ 'fields': ('created_by', 'updated_by', 'created_at', 'updated_at'),
421
368
  'classes': ('tab', 'collapse')
422
369
  }),
423
370
  )
424
371
 
425
- actions = ['activate_toolsets', 'deactivate_toolsets']
372
+ actions = ['activate_configurations', 'deactivate_configurations', 'reset_usage']
426
373
 
427
- @display(description="Toolset Name")
374
+ @display(description="Configuration Name")
428
375
  def name_display(self, obj):
429
- """Enhanced name display."""
430
- return format_html(
431
- '<div class="flex items-center space-x-2">'
432
- '<span class="text-teal-600 font-medium">{}</span>'
433
- '</div>',
434
- obj.name
376
+ """Enhanced configuration name display."""
377
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.SETTINGS)
378
+ return StatusBadge.create(
379
+ text=obj.name,
380
+ variant="primary",
381
+ config=config
435
382
  )
436
383
 
437
- @display(description="Class")
438
- def toolset_class_badge(self, obj):
439
- """Toolset class badge."""
384
+ @display(description="Toolset Class")
385
+ def toolset_class_display(self, obj):
386
+ """Toolset class display with badge."""
440
387
  if not obj.toolset_class:
441
- return "-"
388
+ return ""
389
+
390
+ # Extract class name from full path
442
391
  class_name = obj.toolset_class.split('.')[-1] if '.' in obj.toolset_class else obj.toolset_class
443
- return format_html(
444
- '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-cyan-100 text-cyan-800">{}</span>',
445
- class_name
446
- )
447
-
448
- @display(description="Status", boolean=True)
449
- def status_badge(self, obj):
450
- """Status badge."""
451
- return obj.is_active
452
-
453
- @display(description="Usage")
454
- def usage_info(self, obj):
455
- """Usage information."""
456
- users_count = obj.allowed_users.count() if obj.allowed_users.exists() else 0
457
- groups_count = obj.allowed_groups.count() if obj.allowed_groups.exists() else 0
458
392
 
459
- return format_html(
460
- '<div class="text-sm space-y-1">'
461
- '<div><span class="font-medium">Users:</span> {}</div>'
462
- '<div><span class="font-medium">Groups:</span> {}</div>'
463
- '</div>',
464
- users_count,
465
- groups_count
393
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.EXTENSION)
394
+ return StatusBadge.create(
395
+ text=class_name,
396
+ variant="info",
397
+ config=config
466
398
  )
467
399
 
468
- @display(description="Config Preview")
469
- def config_preview(self, obj):
470
- """Preview of configuration."""
471
- if not obj.config:
472
- return "-"
473
- preview = str(obj.config)[:200] + "..." if len(str(obj.config)) > 200 else str(obj.config)
474
- return format_html(
475
- '<div class="text-sm text-gray-600 max-w-md">{}</div>',
476
- preview
477
- )
400
+ @display(description="Status")
401
+ def status_display(self, obj):
402
+ """Status display based on active state."""
403
+ if obj.is_active:
404
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.CHECK_CIRCLE)
405
+ return StatusBadge.create(text="Active", variant="success", config=config)
406
+ else:
407
+ config = StatusBadgeConfig(show_icons=True, icon=Icons.PAUSE_CIRCLE)
408
+ return StatusBadge.create(text="Inactive", variant="secondary", config=config)
478
409
 
479
- @action(description="Activate toolsets", icon="play_arrow", variant=ActionVariant.SUCCESS)
480
- def activate_toolsets(self, request, queryset):
481
- """Activate selected toolsets."""
410
+ @display(description="Usage")
411
+ def usage_count_display(self, obj):
412
+ """Usage count display."""
413
+ if not obj.usage_count:
414
+ return "Not used"
415
+ return f"{obj.usage_count} times"
416
+
417
+ @display(description="Created By")
418
+ def created_by_display(self, obj):
419
+ """Created by user display."""
420
+ if not obj.created_by:
421
+ return "—"
422
+ return self.display_user_simple(obj.created_by)
423
+
424
+ @display(description="Created")
425
+ def created_at_display(self, obj):
426
+ """Created time with relative display."""
427
+ config = DateTimeDisplayConfig(show_relative=True)
428
+ return self.display_datetime_relative(obj, 'created_at', config)
429
+
430
+ @action(description="Activate configurations", variant=ActionVariant.SUCCESS)
431
+ def activate_configurations(self, request, queryset):
432
+ """Activate selected configurations."""
482
433
  updated = queryset.update(is_active=True)
483
- messages.success(request, f"Activated {updated} toolsets.")
434
+ messages.success(request, f"Activated {updated} configurations.")
484
435
 
485
- @action(description="Deactivate toolsets", icon="pause", variant=ActionVariant.WARNING)
486
- def deactivate_toolsets(self, request, queryset):
487
- """Deactivate selected toolsets."""
436
+ @action(description="Deactivate configurations", variant=ActionVariant.WARNING)
437
+ def deactivate_configurations(self, request, queryset):
438
+ """Deactivate selected configurations."""
488
439
  updated = queryset.update(is_active=False)
489
- messages.warning(request, f"Deactivated {updated} toolsets.")
440
+ messages.warning(request, f"Deactivated {updated} configurations.")
490
441
 
491
- def get_queryset(self, request):
492
- """Optimize queryset."""
493
- return super().get_queryset(request).select_related('created_by').prefetch_related('allowed_users', 'allowed_groups')
442
+ @action(description="Reset usage count", variant=ActionVariant.INFO)
443
+ def reset_usage(self, request, queryset):
444
+ """Reset usage count for selected configurations."""
445
+ updated = queryset.update(usage_count=0)
446
+ messages.info(request, f"Reset usage count for {updated} configurations.")