django-cfg 1.2.7__py3-none-any.whl → 1.2.8__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 (45) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/urls.py +2 -2
  3. django_cfg/modules/django_unfold/callbacks/__init__.py +9 -0
  4. django_cfg/modules/django_unfold/callbacks/actions.py +50 -0
  5. django_cfg/modules/django_unfold/callbacks/base.py +98 -0
  6. django_cfg/modules/django_unfold/callbacks/charts.py +224 -0
  7. django_cfg/modules/django_unfold/callbacks/commands.py +40 -0
  8. django_cfg/modules/django_unfold/callbacks/main.py +191 -0
  9. django_cfg/modules/django_unfold/callbacks/revolution.py +76 -0
  10. django_cfg/modules/django_unfold/callbacks/statistics.py +240 -0
  11. django_cfg/modules/django_unfold/callbacks/system.py +180 -0
  12. django_cfg/modules/django_unfold/callbacks/users.py +65 -0
  13. django_cfg/modules/django_unfold/models/config.py +10 -3
  14. django_cfg/modules/django_unfold/tailwind.py +68 -0
  15. django_cfg/templates/admin/components/action_grid.html +49 -0
  16. django_cfg/templates/admin/components/card.html +50 -0
  17. django_cfg/templates/admin/components/data_table.html +67 -0
  18. django_cfg/templates/admin/components/metric_card.html +39 -0
  19. django_cfg/templates/admin/components/modal.html +58 -0
  20. django_cfg/templates/admin/components/progress_bar.html +25 -0
  21. django_cfg/templates/admin/components/section_header.html +26 -0
  22. django_cfg/templates/admin/components/stat_item.html +32 -0
  23. django_cfg/templates/admin/components/stats_grid.html +72 -0
  24. django_cfg/templates/admin/components/status_badge.html +28 -0
  25. django_cfg/templates/admin/components/user_avatar.html +27 -0
  26. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +7 -7
  27. django_cfg/templates/admin/snippets/components/activity_tracker.html +48 -11
  28. django_cfg/templates/admin/snippets/components/charts_section.html +63 -13
  29. django_cfg/templates/admin/snippets/components/django_commands.html +18 -18
  30. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -47
  31. django_cfg/templates/admin/snippets/components/recent_activity.html +28 -38
  32. django_cfg/templates/admin/snippets/components/recent_users_table.html +22 -53
  33. django_cfg/templates/admin/snippets/components/stats_cards.html +2 -66
  34. django_cfg/templates/admin/snippets/components/system_health.html +13 -63
  35. django_cfg/templates/admin/snippets/components/system_metrics.html +8 -25
  36. django_cfg/templates/admin/snippets/tabs/commands_tab.html +1 -1
  37. django_cfg/templates/admin/snippets/tabs/overview_tab.html +4 -4
  38. django_cfg/templates/admin/snippets/zones/zones_table.html +12 -33
  39. django_cfg/templatetags/django_cfg.py +2 -1
  40. {django_cfg-1.2.7.dist-info → django_cfg-1.2.8.dist-info}/METADATA +2 -1
  41. {django_cfg-1.2.7.dist-info → django_cfg-1.2.8.dist-info}/RECORD +44 -24
  42. django_cfg/modules/django_unfold/callbacks.py +0 -795
  43. {django_cfg-1.2.7.dist-info → django_cfg-1.2.8.dist-info}/WHEEL +0 -0
  44. {django_cfg-1.2.7.dist-info → django_cfg-1.2.8.dist-info}/entry_points.txt +0 -0
  45. {django_cfg-1.2.7.dist-info → django_cfg-1.2.8.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,49 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Action Grid Component -->
4
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-{{ cols|default:'3' }} xl:grid-cols-{{ xl_cols|default:'4' }} gap-4">
5
+ {% for action in actions %}
6
+ <a href="{{ action.link }}"
7
+ class="group flex items-center p-4 bg-white dark:bg-base-900 rounded-lg border border-base-200 dark:border-base-700 hover:border-primary-600 dark:hover:border-primary-500 hover:shadow-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
8
+
9
+ <!-- Icon -->
10
+ <div class="flex-shrink-0 mr-4">
11
+ <div class="p-2 rounded-lg transition-colors duration-200
12
+ {% if action.color == 'primary' %}bg-primary-100 dark:bg-primary-900/20 group-hover:bg-primary-200 dark:group-hover:bg-primary-800/30
13
+ {% elif action.color == 'success' %}bg-green-100 dark:bg-green-900/20 group-hover:bg-green-200 dark:group-hover:bg-green-800/30
14
+ {% elif action.color == 'warning' %}bg-amber-100 dark:bg-amber-900/20 group-hover:bg-amber-200 dark:group-hover:bg-amber-800/30
15
+ {% elif action.color == 'danger' %}bg-red-100 dark:bg-red-900/20 group-hover:bg-red-200 dark:group-hover:bg-red-800/30
16
+ {% else %}bg-base-100 dark:bg-base-800 group-hover:bg-base-200 dark:group-hover:bg-base-700{% endif %}">
17
+
18
+ <span class="material-icons text-lg
19
+ {% if action.color == 'primary' %}text-primary-600 dark:text-primary-400
20
+ {% elif action.color == 'success' %}text-green-600 dark:text-green-400
21
+ {% elif action.color == 'warning' %}text-amber-600 dark:text-amber-400
22
+ {% elif action.color == 'danger' %}text-red-600 dark:text-red-400
23
+ {% else %}text-font-subtle-light dark:text-font-subtle-dark{% endif %}">
24
+ {{ action.icon }}
25
+ </span>
26
+ </div>
27
+ </div>
28
+
29
+ <!-- Content -->
30
+ <div class="flex-1 min-w-0">
31
+ <h3 class="text-sm font-medium text-font-default-light dark:text-font-default-dark group-hover:text-primary-600 dark:group-hover:text-primary-500 transition-colors duration-200">
32
+ {{ action.title }}
33
+ </h3>
34
+ {% if action.description %}
35
+ <p class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-1 line-clamp-2">
36
+ {{ action.description }}
37
+ </p>
38
+ {% endif %}
39
+ </div>
40
+
41
+ <!-- Arrow -->
42
+ <div class="flex-shrink-0 ml-2">
43
+ <span class="material-icons text-base-400 dark:text-base-500 group-hover:text-primary-600 dark:group-hover:text-primary-500 transition-colors duration-200">
44
+ arrow_forward
45
+ </span>
46
+ </div>
47
+ </a>
48
+ {% endfor %}
49
+ </div>
@@ -0,0 +1,50 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Enhanced Card Component following Unfold patterns -->
4
+ <div class="bg-white dark:bg-base-900 rounded-lg border border-base-200 dark:border-base-700 shadow-xs overflow-hidden {% if hover %}hover:shadow-md hover:border-base-300 dark:hover:border-base-600 transition-all duration-200{% endif %}{% if class %} {{ class }}{% endif %}">
5
+
6
+ <!-- Card Header -->
7
+ {% if title or header_actions %}
8
+ <div class="p-6 border-b border-base-200 dark:border-base-700 {% if header_bg %}{{ header_bg }}{% else %}bg-base-50 dark:bg-base-800{% endif %}">
9
+ <div class="flex items-center justify-between">
10
+ {% if title %}
11
+ <div class="flex items-center space-x-3">
12
+ {% if icon %}
13
+ <div class="flex items-center justify-center w-8 h-8 bg-{{ icon_color|default:'primary' }}-100 dark:bg-{{ icon_color|default:'primary' }}-900/20 rounded-lg">
14
+ <span class="material-icons text-{{ icon_color|default:'primary' }}-600 dark:text-{{ icon_color|default:'primary' }}-400 text-lg">{{ icon }}</span>
15
+ </div>
16
+ {% endif %}
17
+ <div>
18
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">
19
+ {{ title }}
20
+ </h3>
21
+ {% if subtitle %}
22
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark mt-1">
23
+ {{ subtitle }}
24
+ </p>
25
+ {% endif %}
26
+ </div>
27
+ </div>
28
+ {% endif %}
29
+
30
+ {% if header_actions %}
31
+ <div class="flex items-center space-x-2">
32
+ {{ header_actions }}
33
+ </div>
34
+ {% endif %}
35
+ </div>
36
+ </div>
37
+ {% endif %}
38
+
39
+ <!-- Card Body -->
40
+ <div class="p-6{% if compact %} p-4{% endif %}">
41
+ {{ content }}
42
+ </div>
43
+
44
+ <!-- Card Footer -->
45
+ {% if footer %}
46
+ <div class="px-6 py-4 border-t border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
47
+ {{ footer }}
48
+ </div>
49
+ {% endif %}
50
+ </div>
@@ -0,0 +1,67 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Data Table Component -->
4
+ <div class="bg-white dark:bg-base-900 rounded-lg border border-base-200 dark:border-base-700 overflow-hidden shadow-xs">
5
+
6
+ <!-- Table Header -->
7
+ {% if title or actions %}
8
+ <div class="px-6 py-4 border-b border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
9
+ <div class="flex items-center justify-between">
10
+ {% if title %}
11
+ <div class="flex items-center">
12
+ {% if icon %}
13
+ <span class="material-icons text-primary-600 mr-2">{{ icon }}</span>
14
+ {% endif %}
15
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">{{ title }}</h3>
16
+ </div>
17
+ {% endif %}
18
+
19
+ {% if actions %}
20
+ <div class="flex items-center space-x-2">
21
+ {{ actions }}
22
+ </div>
23
+ {% endif %}
24
+ </div>
25
+ </div>
26
+ {% endif %}
27
+
28
+ <!-- Table Content -->
29
+ <div class="overflow-x-auto">
30
+ <table class="w-full">
31
+ {% if headers %}
32
+ <thead class="bg-base-50 dark:bg-base-800">
33
+ <tr>
34
+ {% for header in headers %}
35
+ <th class="px-6 py-3 text-left text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider">
36
+ {{ header }}
37
+ </th>
38
+ {% endfor %}
39
+ </tr>
40
+ </thead>
41
+ {% endif %}
42
+
43
+ <tbody class="bg-white dark:bg-base-900 divide-y divide-base-200 dark:divide-base-700">
44
+ {% if rows %}
45
+ {% for row in rows %}
46
+ <tr class="hover:bg-base-100 dark:hover:bg-base-800 transition-colors duration-150">
47
+ {% for cell in row %}
48
+ <td class="px-6 py-4 whitespace-nowrap">
49
+ {{ cell }}
50
+ </td>
51
+ {% endfor %}
52
+ </tr>
53
+ {% endfor %}
54
+ {% else %}
55
+ <tr>
56
+ <td colspan="{{ headers|length|default:'5' }}" class="px-6 py-8 text-center">
57
+ <div class="flex flex-col items-center">
58
+ <span class="material-icons text-4xl text-base-400 dark:text-base-500 mb-2">{{ empty_icon|default:'inbox' }}</span>
59
+ <p class="text-font-subtle-light dark:text-font-subtle-dark">{{ empty_message|default:'No data available' }}</p>
60
+ </div>
61
+ </td>
62
+ </tr>
63
+ {% endif %}
64
+ </tbody>
65
+ </table>
66
+ </div>
67
+ </div>
@@ -0,0 +1,39 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Metric Card Component -->
4
+ <div class="bg-white dark:bg-base-900 rounded-lg p-5 border border-base-200 dark:border-base-700 hover:shadow-md transition-shadow duration-200">
5
+ <div class="flex items-center justify-between">
6
+ <div class="flex items-center space-x-3">
7
+ <div class="flex-shrink-0">
8
+ <span class="material-icons
9
+ {% if status == 'healthy' or status == 'success' %}text-green-600 dark:text-green-400
10
+ {% elif status == 'warning' %}text-amber-600 dark:text-amber-400
11
+ {% elif status == 'error' or status == 'failed' %}text-red-600 dark:text-red-400
12
+ {% elif status == 'info' %}text-blue-600 dark:text-blue-400
13
+ {% else %}text-primary-600 dark:text-primary-400{% endif %}">
14
+ {{ icon }}
15
+ </span>
16
+ </div>
17
+ <div>
18
+ <div class="text-sm font-medium text-font-default-light dark:text-font-default-dark">
19
+ {{ title }}
20
+ </div>
21
+ {% if description %}
22
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-1">
23
+ {{ description }}
24
+ </div>
25
+ {% endif %}
26
+ </div>
27
+ </div>
28
+
29
+ <div class="flex-shrink-0">
30
+ {% include 'admin/components/status_badge.html' with status=status text=status_text %}
31
+ </div>
32
+ </div>
33
+
34
+ {% if progress %}
35
+ <div class="mt-4">
36
+ {% include 'admin/components/progress_bar.html' with value=progress title=progress_title description=progress_description %}
37
+ </div>
38
+ {% endif %}
39
+ </div>
@@ -0,0 +1,58 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Reusable Modal Component based on Unfold patterns -->
4
+ <div id="{{ modal_id|default:'modal' }}" class="fixed inset-0 bg-black/80 backdrop-blur-sm hidden z-50">
5
+ <div class="flex items-center justify-center min-h-screen p-4">
6
+ <div class="bg-white dark:bg-base-900 rounded-lg shadow-2xl max-w-{{ max_width|default:'4xl' }} w-full max-h-[80vh] flex flex-col border border-base-200 dark:border-base-700">
7
+
8
+ <!-- Modal Header -->
9
+ {% if title or closable %}
10
+ <div class="flex items-center justify-between p-4 border-b border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
11
+ {% if title %}
12
+ <div class="flex items-center space-x-3">
13
+ {% if icon %}
14
+ <div class="flex items-center justify-center w-10 h-10 bg-primary-100 dark:bg-primary-900/20 rounded-lg">
15
+ <span class="material-icons text-primary-600 dark:text-primary-400">{{ icon }}</span>
16
+ </div>
17
+ {% endif %}
18
+ <div>
19
+ <h3 class="text-lg font-semibold text-font-important-light dark:text-font-important-dark">
20
+ {{ title }}
21
+ </h3>
22
+ {% if subtitle %}
23
+ <p class="text-sm text-font-subtle-light dark:text-font-subtle-dark">
24
+ {{ subtitle }}
25
+ </p>
26
+ {% endif %}
27
+ </div>
28
+ </div>
29
+ {% endif %}
30
+
31
+ {% if closable %}
32
+ <button onclick="{{ close_function|default:'closeModal()' }}" class="p-2 text-font-subtle-light dark:text-font-subtle-dark hover:text-font-default-light dark:hover:text-font-default-dark hover:bg-base-100 dark:hover:bg-base-700 rounded-lg transition-colors">
33
+ <span class="material-icons">close</span>
34
+ </button>
35
+ {% endif %}
36
+ </div>
37
+ {% endif %}
38
+
39
+ <!-- Modal Body -->
40
+ <div class="flex-1 overflow-hidden p-4">
41
+ {% if scrollable %}
42
+ <div class="bg-base-100 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700 h-full overflow-y-auto p-4">
43
+ {{ content }}
44
+ </div>
45
+ {% else %}
46
+ {{ content }}
47
+ {% endif %}
48
+ </div>
49
+
50
+ <!-- Modal Footer -->
51
+ {% if footer %}
52
+ <div class="flex items-center justify-between p-4 border-t border-base-200 dark:border-base-700 bg-base-50 dark:bg-base-800">
53
+ {{ footer }}
54
+ </div>
55
+ {% endif %}
56
+ </div>
57
+ </div>
58
+ </div>
@@ -0,0 +1,25 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Progress Bar Component -->
4
+ <div class="w-full">
5
+ {% if title %}
6
+ <div class="flex items-center justify-between mb-2">
7
+ <span class="text-sm font-medium text-font-default-light dark:text-font-default-dark">{{ title }}</span>
8
+ <span class="text-sm text-font-subtle-light dark:text-font-subtle-dark">{{ value }}%</span>
9
+ </div>
10
+ {% endif %}
11
+
12
+ <div class="w-full bg-base-200 dark:bg-base-700 rounded-full h-2">
13
+ <div class="h-2 rounded-full transition-all duration-300
14
+ {% if value >= 80 %}bg-green-500
15
+ {% elif value >= 60 %}bg-amber-500
16
+ {% elif value >= 40 %}bg-orange-500
17
+ {% else %}bg-red-500{% endif %}"
18
+ style="width: {{ value|default:0 }}%">
19
+ </div>
20
+ </div>
21
+
22
+ {% if description %}
23
+ <p class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-1">{{ description }}</p>
24
+ {% endif %}
25
+ </div>
@@ -0,0 +1,26 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Section Header Component -->
4
+ <div class="flex items-center mb-6">
5
+ {% if icon %}
6
+ <div class="flex items-center justify-center w-8 h-8 bg-{{ icon_color|default:'primary' }}-100 dark:bg-{{ icon_color|default:'primary' }}-900/20 rounded-lg mr-3">
7
+ <span class="material-icons text-{{ icon_color|default:'primary' }}-600 dark:text-{{ icon_color|default:'primary' }}-400 text-lg">{{ icon }}</span>
8
+ </div>
9
+ {% endif %}
10
+
11
+ <h2 class="text-xl font-semibold text-font-important-light dark:text-font-important-dark">
12
+ {{ title }}
13
+ </h2>
14
+
15
+ {% if badge %}
16
+ <span class="ml-auto text-xs text-font-subtle-light dark:text-font-subtle-dark bg-base-100 dark:bg-base-800 px-2 py-1 rounded-full">
17
+ {{ badge }}
18
+ </span>
19
+ {% endif %}
20
+
21
+ {% if actions %}
22
+ <div class="ml-auto flex items-center space-x-2">
23
+ {{ actions }}
24
+ </div>
25
+ {% endif %}
26
+ </div>
@@ -0,0 +1,32 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Stat Item Component -->
4
+ <div class="flex items-center justify-between p-3 bg-base-50 dark:bg-base-800 rounded-lg hover:bg-base-100 dark:hover:bg-base-700 transition-colors duration-150 group">
5
+ <div class="flex items-center">
6
+ {% if icon %}
7
+ <div class="w-8 h-8 rounded-lg flex items-center justify-center mr-3 bg-{{ color|default:'primary' }}-100 dark:bg-{{ color|default:'primary' }}-900/20">
8
+ <span class="material-icons text-xs text-{{ color|default:'primary' }}-600 dark:text-{{ color|default:'primary' }}-400">{{ icon }}</span>
9
+ </div>
10
+ {% endif %}
11
+ <div>
12
+ <div class="text-sm font-medium text-font-default-light dark:text-font-default-dark">{{ title }}</div>
13
+ {% if description %}
14
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">{{ description }}</div>
15
+ {% endif %}
16
+ </div>
17
+ </div>
18
+
19
+ <div class="flex items-center gap-3">
20
+ <span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-{{ color|default:'primary' }}-100 dark:bg-{{ color|default:'primary' }}-900/20 text-{{ color|default:'primary' }}-600 dark:text-{{ color|default:'primary' }}-400">
21
+ {{ count|default:0 }}
22
+ </span>
23
+
24
+ {% if url %}
25
+ <a href="{{ url }}"
26
+ class="inline-flex items-center gap-1 px-3 py-1 bg-primary-600 hover:bg-primary-700 dark:bg-primary-500 dark:hover:bg-primary-600 text-white rounded-lg text-xs font-medium transition-colors duration-150 group-hover:shadow-sm">
27
+ <span class="material-icons text-xs">visibility</span>
28
+ <span>View</span>
29
+ </a>
30
+ {% endif %}
31
+ </div>
32
+ </div>
@@ -0,0 +1,72 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Statistics Grid Component -->
4
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-{{ cols|default:'4' }} gap-{{ gap|default:'4' }} {% if class %}{{ class }}{% endif %}">
5
+ {% for stat in stats %}
6
+ <div class="bg-white dark:bg-base-900 rounded-lg shadow-sm hover:shadow-md transition-all duration-300 border border-base-200 dark:border-base-700 overflow-hidden group">
7
+ <div class="p-5">
8
+ <div class="flex items-start justify-between">
9
+ <div class="flex-1">
10
+ <!-- Title -->
11
+ <div class="text-xs font-semibold text-font-subtle-light dark:text-font-subtle-dark uppercase tracking-wider mb-2">
12
+ {{ stat.title }}
13
+ </div>
14
+
15
+ <!-- Value -->
16
+ <div class="text-2xl font-bold text-font-important-light dark:text-font-important-dark mb-1">
17
+ {{ stat.value }}
18
+ </div>
19
+
20
+ <!-- Change indicator -->
21
+ {% if stat.change %}
22
+ <div class="flex items-center">
23
+ <div class="flex items-center px-2 py-1 rounded-full text-xs font-medium
24
+ {% if stat.change_type == 'positive' %}bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400
25
+ {% elif stat.change_type == 'negative' %}bg-red-100 dark:bg-red-900/20 text-red-600 dark:text-red-400
26
+ {% else %}bg-base-100 dark:bg-base-800 text-font-default-light dark:text-font-default-dark{% endif %}">
27
+
28
+ <span class="material-icons text-xs mr-1">
29
+ {% if stat.change_type == 'positive' %}arrow_upward
30
+ {% elif stat.change_type == 'negative' %}arrow_downward
31
+ {% else %}remove{% endif %}
32
+ </span>
33
+ {{ stat.change }}
34
+ </div>
35
+ </div>
36
+ {% endif %}
37
+
38
+ <!-- Description -->
39
+ {% if stat.description %}
40
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-1">
41
+ {{ stat.description }}
42
+ </div>
43
+ {% endif %}
44
+ </div>
45
+
46
+ <!-- Icon -->
47
+ {% if stat.icon %}
48
+ <div class="flex-shrink-0 ml-3">
49
+ <div class="w-10 h-10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
50
+ <span class="material-icons text-2xl
51
+ {% if stat.change_type == 'positive' %}text-green-600 dark:text-green-400
52
+ {% elif stat.change_type == 'negative' %}text-red-600 dark:text-red-400
53
+ {% else %}text-primary-600 dark:text-primary-400{% endif %}">
54
+ {{ stat.icon }}
55
+ </span>
56
+ </div>
57
+ </div>
58
+ {% endif %}
59
+ </div>
60
+ </div>
61
+
62
+ <!-- Accent bar -->
63
+ {% if stat.show_accent %}
64
+ <div class="h-1 w-full
65
+ {% if stat.change_type == 'positive' %}bg-gradient-to-r from-green-400 to-green-600
66
+ {% elif stat.change_type == 'negative' %}bg-gradient-to-r from-red-400 to-red-600
67
+ {% else %}bg-gradient-to-r from-primary-400 to-primary-600{% endif %}">
68
+ </div>
69
+ {% endif %}
70
+ </div>
71
+ {% endfor %}
72
+ </div>
@@ -0,0 +1,28 @@
1
+ {% load unfold %}
2
+
3
+ <!-- Status Badge Component -->
4
+ <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
5
+ {% if status == 'active' or status == 'healthy' or status == 'success' %}bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400
6
+ {% elif status == 'warning' or status == 'pending' %}bg-amber-100 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400
7
+ {% elif status == 'error' or status == 'failed' or status == 'inactive' %}bg-red-100 dark:bg-red-900/20 text-red-600 dark:text-red-400
8
+ {% elif status == 'info' or status == 'processing' %}bg-blue-100 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400
9
+ {% else %}bg-base-100 dark:bg-base-800 text-font-subtle-light dark:text-font-subtle-dark{% endif %}">
10
+
11
+ {% if icon %}
12
+ <span class="material-icons text-xs mr-1">{{ icon }}</span>
13
+ {% else %}
14
+ {% if status == 'active' or status == 'healthy' or status == 'success' %}
15
+ <span class="material-icons text-xs mr-1">check_circle</span>
16
+ {% elif status == 'warning' or status == 'pending' %}
17
+ <span class="material-icons text-xs mr-1">warning</span>
18
+ {% elif status == 'error' or status == 'failed' %}
19
+ <span class="material-icons text-xs mr-1">error</span>
20
+ {% elif status == 'inactive' %}
21
+ <span class="material-icons text-xs mr-1">cancel</span>
22
+ {% elif status == 'info' or status == 'processing' %}
23
+ <span class="material-icons text-xs mr-1">info</span>
24
+ {% endif %}
25
+ {% endif %}
26
+
27
+ {{ text|default:status|title }}
28
+ </span>
@@ -0,0 +1,27 @@
1
+ {% load unfold %}
2
+
3
+ <!-- User Avatar Component -->
4
+ <div class="flex items-center">
5
+ <div class="h-{{ size|default:'8' }} w-{{ size|default:'8' }} bg-{{ color|default:'primary' }}-100 dark:bg-{{ color|default:'primary' }}-900/20 rounded-full flex items-center justify-center mr-3">
6
+ {% if image %}
7
+ <img src="{{ image }}" alt="{{ name }}" class="h-{{ size|default:'8' }} w-{{ size|default:'8' }} rounded-full object-cover">
8
+ {% else %}
9
+ <span class="text-sm font-medium text-{{ color|default:'primary' }}-600 dark:text-{{ color|default:'primary' }}-400">
10
+ {% if name %}{{ name|slice:":1"|upper }}{% elif email %}{{ email|slice:":1"|upper }}{% else %}?{% endif %}
11
+ </span>
12
+ {% endif %}
13
+ </div>
14
+
15
+ {% if show_details %}
16
+ <div>
17
+ <div class="text-sm font-medium text-font-default-light dark:text-font-default-dark">
18
+ {% if name %}{{ name|truncatechars:20 }}{% elif email %}{{ email|truncatechars:20 }}{% else %}Unknown User{% endif %}
19
+ </div>
20
+ {% if email and name %}
21
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
22
+ {{ email|truncatechars:30 }}
23
+ </div>
24
+ {% endif %}
25
+ </div>
26
+ {% endif %}
27
+ </div>
@@ -246,13 +246,13 @@
246
246
  }
247
247
 
248
248
  function scrollToBottom(element) {
249
- // Now the element itself has overflow-y-auto
250
- if (element) {
251
- // Small delay for DOM update
252
- setTimeout(() => {
253
- element.scrollTop = element.scrollHeight;
254
- }, 10);
255
- }
249
+ if (!element) return;
250
+
251
+ // Принудительно скроллим элемент
252
+ setTimeout(() => {
253
+ element.scrollTop = element.scrollHeight;
254
+ console.log('Scrolled to bottom:', element.scrollTop, element.scrollHeight);
255
+ }, 50);
256
256
  }
257
257
 
258
258
  window.closeCommandModal = function() {
@@ -1,17 +1,54 @@
1
1
  {% load unfold %}
2
2
 
3
- <!-- Activity Tracker -->
3
+ <!-- Activity Tracker using reusable components -->
4
4
  <div class="mt-8 w-full">
5
- <div class="flex items-center mb-6">
6
- <div class="flex items-center justify-center w-8 h-8 bg-primary-100 dark:bg-primary-900/20 rounded-lg mr-3">
7
- <span class="material-icons text-primary-600 dark:text-primary-500 text-lg">timeline</span>
5
+ {% include 'admin/components/section_header.html' with title='Activity Tracker' icon='timeline' icon_color='primary' %}
6
+
7
+ {% component "unfold/components/card.html" with title="User Activity Heatmap" subtitle="Last 52 weeks of user activity" %}
8
+ <div class="space-y-4">
9
+ <!-- Legend -->
10
+ <div class="flex items-center justify-between text-sm text-font-subtle-light dark:text-font-subtle-dark">
11
+ <span>Less</span>
12
+ <div class="flex items-center space-x-1">
13
+ <div class="w-3 h-3 bg-base-200 dark:bg-base-700 rounded-sm"></div>
14
+ <div class="w-3 h-3 bg-green-200 dark:bg-green-800 rounded-sm"></div>
15
+ <div class="w-3 h-3 bg-green-400 dark:bg-green-600 rounded-sm"></div>
16
+ <div class="w-3 h-3 bg-green-600 dark:bg-green-500 rounded-sm"></div>
17
+ <div class="w-3 h-3 bg-green-800 dark:bg-green-400 rounded-sm"></div>
18
+ </div>
19
+ <span>More</span>
20
+ </div>
21
+
22
+ <!-- Activity Tracker Grid -->
23
+ <div class="overflow-x-auto">
24
+ {% if activity_tracker %}
25
+ {% component "unfold/components/tracker.html" with data=activity_tracker %}
26
+ {% endcomponent %}
27
+ {% else %}
28
+ <div class="flex items-center justify-center p-8 bg-base-50 dark:bg-base-800 rounded-lg border border-base-200 dark:border-base-700">
29
+ <div class="text-center">
30
+ <span class="material-icons text-4xl text-base-400 dark:text-base-500 mb-2">timeline</span>
31
+ <p class="text-font-subtle-light dark:text-font-subtle-dark">No activity data available</p>
32
+ </div>
33
+ </div>
34
+ {% endif %}
35
+ </div>
36
+
37
+ <!-- Summary Stats -->
38
+ <div class="grid grid-cols-3 gap-4 pt-4 border-t border-base-200 dark:border-base-700">
39
+ <div class="text-center">
40
+ <div class="text-lg font-semibold text-font-default-light dark:text-font-default-dark">365</div>
41
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">Days tracked</div>
42
+ </div>
43
+ <div class="text-center">
44
+ <div class="text-lg font-semibold text-green-600 dark:text-green-400">52</div>
45
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">Weeks</div>
46
+ </div>
47
+ <div class="text-center">
48
+ <div class="text-lg font-semibold text-primary-600 dark:text-primary-400">{{ activity_tracker|length|default:"0" }}</div>
49
+ <div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">Data points</div>
50
+ </div>
51
+ </div>
8
52
  </div>
9
- <h2 class="text-xl font-semibold text-font-important-light dark:text-font-important-dark">
10
- Activity Tracker
11
- </h2>
12
- </div>
13
- {% component "unfold/components/card.html" %}
14
- {% component "unfold/components/tracker.html" with data=activity_tracker %}
15
- {% endcomponent %}
16
53
  {% endcomponent %}
17
54
  </div>