adminita 0.1.5__py3-none-any.whl → 0.1.7__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.
@@ -155,13 +155,28 @@
155
155
 
156
156
  #result_list tbody td {
157
157
  @apply px-4 py-3 text-sm text-gray-900 dark:text-gray-100;
158
- text-align: left !important;
158
+
159
159
  }
160
160
 
161
161
  #result_list tbody td a {
162
162
  @apply text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-300 no-underline;
163
163
  }
164
164
 
165
+ /* Force admin title column to be left-aligned */
166
+ #result_list tbody th,
167
+ #result_list tbody th.field-title,
168
+ #result_list tbody th a {
169
+ text-align: left !important;
170
+ justify-content: flex-start !important;
171
+ }
172
+
173
+ #result_list tbody th a {
174
+ display: inline-flex;
175
+ align-items: center;
176
+ justify-content: flex-start !important;
177
+ }
178
+
179
+
165
180
  /* Action checkboxes column */
166
181
  #result_list .action-checkbox {
167
182
  @apply w-10;
@@ -370,4 +385,37 @@
370
385
  content: " ▼";
371
386
  @apply text-xs;
372
387
  }
388
+
389
+ /* INLINE FORMS */
390
+ .inline-related input[type="text"],
391
+ .inline-related input[type="number"],
392
+ .inline-related input[type="url"],
393
+ .inline-related input[type="email"],
394
+ .inline-related select,
395
+ .inline-related textarea {
396
+ @apply w-full px-3 py-2 text-sm bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500 dark:text-white;
397
+ }
398
+
399
+ .inline-related input[type="checkbox"] {
400
+ @apply w-4 h-4 text-primary-600 bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 rounded focus:ring-primary-500;
401
+ }
402
+
403
+ .collapse-icon.rotate-180 {
404
+ transform: rotate(180deg);
405
+ }
406
+
407
+ .empty-form {
408
+ display: none;
409
+ }
410
+
411
+ .inline-related.marked-for-deletion {
412
+ opacity: 0.5;
413
+ background-color: rgb(254 242 242) !important;
414
+ }
415
+
416
+ .dark .inline-related.marked-for-deletion {
417
+ background-color: rgb(127 29 29 / 0.3) !important;
418
+ }
419
+
420
+
373
421
  }
@@ -5,14 +5,9 @@
5
5
  <meta charset="utf-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>{% block title %}{% endblock %}</title>
8
-
9
8
  <link rel="stylesheet" href="{% static 'adminita/adminita-tailwind.css' %}">
10
9
  <script src="{% static 'adminita/adminita-tailwind.js' %}" defer></script>
11
10
  <link rel="stylesheet" href="{% static 'admin/css/widgets.css' %}">
12
- <script src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script>
13
- <script src="{% static 'admin/js/jquery.init.js' %}"></script>
14
- <script src="{% static 'admin/js/admin/RelatedObjectLookups.js' %}"></script>
15
-
16
11
  </head>
17
12
  {% block dark_mode_vars %}
18
13
  <script>
@@ -37,7 +32,7 @@
37
32
 
38
33
  {% block branding %}
39
34
  <div class="h-16 px-6 flex items-center justify-between border-b border-gray-200 dark:border-gray-700">
40
- <a href="{% url 'admin:index' %}" class="text-sm font-normal text-gray-900 dark:text-white hover:text-primary-600 dark:hover:text-primary-400 transition-colors">
35
+ <a href="{% url 'admin:index' %}" class="text-md font-semibold text-gray-900 dark:text-white hover:text-primary-600 dark:hover:text-primary-400 transition-colors">
41
36
  {{ site_header|default:_('Django administration') }}
42
37
  </a>
43
38
  </div>
@@ -47,9 +42,9 @@
47
42
 
48
43
  <nav class="px-4 py-6 overflow-y-auto h-[calc(100vh-4rem)]">
49
44
  {% block sidebar_content %}
50
- {% if app_list %}
45
+ {% if available_apps %}
51
46
  <div class="space-y-6">
52
- {% for app in app_list %}
47
+ {% for app in available_apps %}
53
48
  <div>
54
49
  <h3 class="px-3 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2">
55
50
  {{ app.name }}
@@ -78,66 +73,67 @@
78
73
  <div class="flex-1 flex flex-col md:ml-64 transition-all duration-300" id="main-content">
79
74
 
80
75
  <!-- Top Bar -->
81
- {% block header %}
82
- <header class="h-16 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between px-6 sticky top-0 z-20">
83
-
84
- <div class="flex items-center space-x-4">
85
- <!-- Mobile menu button - hidden on desktop -->
86
- <button id="sidebar-toggle" type="button" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors md:hidden">
87
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
89
- </svg>
90
- </button>
76
+ <div class="min-h-[8rem]">
77
+ {% block header %}
78
+ <header class="h-16 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between px-6 sticky top-0 z-20">
91
79
 
92
- {% block page_title %}
93
- <h1 class="text-xl font-semibold text-gray-900 dark:text-white">
94
- {% block content_title %}{% endblock %}
95
- </h1>
96
- {% endblock %}
97
- </div>
98
-
99
- <div class="flex items-center space-x-4">
100
- <!-- Dark mode toggle -->
101
- <button id="dark-mode-toggle" type="button" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Toggle dark mode">
102
- <svg class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
103
- <path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"/>
104
- </svg>
105
- <svg class="w-5 h-5 block dark:hidden" fill="currentColor" viewBox="0 0 20 20">
106
- <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/>
107
- </svg>
108
- </button>
80
+ <div class="flex items-center space-x-4">
81
+ <!-- Mobile menu button - hidden on desktop -->
82
+ <button id="sidebar-toggle" type="button" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors md:hidden">
83
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
84
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
85
+ </svg>
86
+ </button>
87
+
88
+ {% block page_title %}
89
+ <h1 class="text-xl font-semibold text-gray-900 dark:text-white">
90
+ {% block content_title %}{% endblock %}
91
+ </h1>
92
+ {% endblock %}
93
+ </div>
109
94
 
110
- {% block usertools %}
111
- {% if user.is_active and user.is_staff %}
112
- <div class="flex items-center space-x-3 text-sm">
113
- <span class="text-gray-700 dark:text-gray-300 hidden sm:inline">
114
- {% trans 'Welcome,' %} <strong>{% firstof user.get_short_name user.first_name "back" %}</strong>
115
- </span>
116
- {% if site_url %}
117
- <a href="{{ site_url }}" class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors hidden sm:inline">
118
- {% trans 'View site' %}
119
- </a>
120
- <span class="text-gray-400 hidden sm:inline">/</span>
95
+ <div class="flex items-center space-x-4">
96
+ <!-- Dark mode toggle -->
97
+ <button id="dark-mode-toggle" type="button" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Toggle dark mode">
98
+ <svg class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
99
+ <path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"/>
100
+ </svg>
101
+ <svg class="w-5 h-5 block dark:hidden" fill="currentColor" viewBox="0 0 20 20">
102
+ <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/>
103
+ </svg>
104
+ </button>
105
+
106
+ {% block usertools %}
107
+ {% if user.is_active and user.is_staff %}
108
+ <div class="flex items-center space-x-3 text-sm">
109
+ <span class="text-gray-700 dark:text-gray-300 hidden sm:inline">
110
+ {% trans 'Welcome,' %} <strong>{% firstof user.get_short_name user.first_name "back" %}</strong>
111
+ </span>
112
+ {% if site_url %}
113
+ <a href="{{ site_url }}" class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors hidden sm:inline">
114
+ {% trans 'View site' %}
115
+ </a>
116
+ <span class="text-gray-400 hidden sm:inline">/</span>
117
+ {% endif %}
118
+ <a href="{% url 'admin:password_change' %}" class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors hidden sm:inline">
119
+ {% trans 'Change password' %}
120
+ </a>
121
+ <span class="text-gray-400 hidden sm:inline">/</span>
122
+ <form method="post" action="{% url 'admin:logout' %}" style="display: inline;">
123
+ {% csrf_token %}
124
+ <button type="submit" class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors bg-transparent border-0 cursor-pointer">
125
+ {% trans 'Log out' %}
126
+ </button>
127
+ </form>
128
+ </div>
121
129
  {% endif %}
122
- <a href="{% url 'admin:password_change' %}" class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors hidden sm:inline">
123
- {% trans 'Change password' %}
124
- </a>
125
- <span class="text-gray-400 hidden sm:inline">/</span>
126
- <form method="post" action="{% url 'admin:logout' %}" style="display: inline;">
127
- {% csrf_token %}
128
- <button type="submit" class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors bg-transparent border-0 cursor-pointer">
129
- {% trans 'Log out' %}
130
- </button>
131
- </form>
132
- </div>
133
- {% endif %}
134
- {% endblock %}
135
- </div>
136
- </header>
137
- {% endblock %}
138
-
130
+ {% endblock %}
131
+ </div>
132
+ </header>
133
+ {% endblock %}
134
+ </div>
139
135
  <!-- Main Content Area with Max Width Container -->
140
- <main class="flex-1 p-6">
136
+ <main class="flex-1 p-6 pt-16">
141
137
  <!-- Container with max-width to prevent super-wide content -->
142
138
  <div class="max-w-7xl mx-auto">
143
139
  {% block messages %}
@@ -176,7 +172,14 @@
176
172
  </div>
177
173
 
178
174
 
179
-
180
- {% block extrajs %}{% endblock %}
175
+ {% block extrajs %}
176
+ {{ block.super }}
177
+ <script src="{% url 'admin:jsi18n' %}"></script>
178
+ <script src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script>
179
+ <script src="{% static 'admin/js/jquery.init.js' %}"></script>
180
+ <script src="{% static 'admin/js/core.js' %}"></script>
181
+ <script src="{% static 'admin/js/admin/RelatedObjectLookups.js' %}"></script>
182
+ {% endblock %}
183
+
181
184
  </body>
182
185
  </html>
@@ -1,6 +1,9 @@
1
1
  {% extends "admin/base.html" %}
2
2
  {% load i18n admin_urls static admin_modify %}
3
3
 
4
+ {% block breadcrumbs %}
5
+ {% endblock %}
6
+
4
7
  {% block content_title %}
5
8
  {% if add %}
6
9
  {% blocktrans with name=opts.verbose_name %}Add {{ name }}{% endblocktrans %}
@@ -9,9 +12,8 @@
9
12
  {% endif %}
10
13
  {% endblock %}
11
14
 
12
-
13
15
  {% block content %}
14
- <!-- Object Tools (History, View on Site) -->
16
+ {# Object Tools (History, View on Site) #}
15
17
  {% if change and not is_popup %}
16
18
  <div class="mb-6 flex items-center justify-end space-x-3">
17
19
  <a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg font-medium text-sm transition-colors">
@@ -21,7 +23,7 @@
21
23
  {% trans 'History' %}
22
24
  </a>
23
25
  {% if has_absolute_url %}
24
- <a href="{{ absolute_url }}" class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium text-sm transition-colors" target="_blank">
26
+ <a href="{{ absolute_url }}" class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium text-sm transition-colors" target="_blank">
25
27
  {% trans 'View on site' %}
26
28
  <svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
27
29
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
@@ -34,6 +36,7 @@
34
36
  <form method="post" id="{{ opts.model_name }}_form" enctype="multipart/form-data" class="space-y-6">
35
37
  {% csrf_token %}
36
38
 
39
+ {# Form Errors #}
37
40
  {% if errors %}
38
41
  <div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
39
42
  <div class="flex items-start">
@@ -50,106 +53,57 @@
50
53
  </div>
51
54
  {% endif %}
52
55
 
53
-
54
- <!-- Main Form -->
56
+ {# Main Form Fieldsets #}
55
57
  <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
56
-
57
- {% block field_sets %}
58
- {% for fieldset in adminform %}
59
-
60
- <div class="{% if not forloop.first %}border-t border-gray-200 dark:border-gray-700{% endif %}">
61
-
62
- {% if fieldset.name %}
63
- <div class="px-6 py-4 bg-gray-50 dark:bg-gray-750">
64
- <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
65
- {{ fieldset.name }}
66
- </h2>
67
-
68
- {% if fieldset.description %}
69
- <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
70
- {{ fieldset.description|safe }}
71
- </p>
72
- {% endif %}
73
- </div>
74
- {% endif %}
75
-
76
- <div class="px-6 py-6 space-y-6">
77
-
78
- {% for line in fieldset %}
79
- <div class="{% if line.fields|length > 1 %}grid grid-cols-1 md:grid-cols-{{ line.fields|length }} gap-6{% endif %}">
80
-
81
- {% for field in line %}
82
- <div class="{% if field.is_readonly %}opacity-75{% endif %}">
83
-
84
- <label for="{{ field.field.id_for_label }}"
85
- class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
86
- {{ field.field.label }}
87
- {% if field.field.field.required %}
88
- <span class="text-red-600 dark:text-red-400">*</span>
89
- {% endif %}
90
- </label>
91
-
92
- {% if field.is_readonly %}
93
- <div class="px-3 py-2 bg-gray-50 dark:bg-gray-750 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-gray-100">
94
- {{ field.field }}
95
- </div>
96
- {% else %}
97
- {{ field.field }}
98
- {% endif %}
99
-
100
- {% if field.field.help_text %}
101
- <p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
102
- {{ field.field.help_text|safe }}
58
+ {% block field_sets %}
59
+ {% for fieldset in adminform %}
60
+ <fieldset class="module {% if fieldset.classes %}{{ fieldset.classes }}{% endif %}{% if not forloop.first %} border-t border-gray-200 dark:border-gray-700{% endif %}">
61
+
62
+ {% if fieldset.name %}
63
+ {# Collapsible Header #}
64
+ <h2 class="fieldset-header flex items-center gap-2 px-6 py-4 bg-gray-50 dark:bg-gray-750 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
65
+ onclick="toggleFieldset(this)">
66
+ <svg class="collapse-icon w-4 h-4 text-gray-500 transform transition-transform duration-200 -rotate-90"
67
+ fill="none" stroke="currentColor" viewBox="0 0 24 24">
68
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
69
+ </svg>
70
+ <span class="text-lg font-semibold text-gray-900 dark:text-white">
71
+ {{ fieldset.name }}
72
+ </span>
73
+ </h2>
74
+ {% if fieldset.description %}
75
+ <p class="fieldset-description px-6 pb-2 text-sm text-gray-600 dark:text-gray-400 hidden">
76
+ {{ fieldset.description|safe }}
103
77
  </p>
104
78
  {% endif %}
105
-
106
- {% if field.field.errors %}
107
- <div class="mt-2 text-sm text-red-600 dark:text-red-400">
108
- {{ field.field.errors }}
109
- </div>
110
79
  {% endif %}
111
-
112
- </div>
113
- {% endfor %}
114
-
115
- </div>
116
- {% endfor %}
117
-
118
- </div>
119
- </div>
120
-
121
- {% endfor %}
122
- {% endblock %}
123
-
124
- </div>
125
-
126
-
127
- <!-- Inline Forms -->
128
- {% block inline_field_sets %}
129
- {% for inline_admin_formset in inline_admin_formsets %}
130
- <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
131
- <div class="px-6 py-4 bg-gray-50 dark:bg-gray-750 border-b border-gray-200 dark:border-gray-700">
132
- <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
133
- {{ inline_admin_formset.opts.verbose_name_plural|capfirst }}
134
- </h2>
135
- </div>
136
-
137
- <div class="p-6">
138
- {{ inline_admin_formset.formset.management_form }}
139
80
 
140
- {% for inline_admin_form in inline_admin_formset %}
141
- <div class="mb-6 pb-6 border-b border-gray-200 dark:border-gray-700 last:border-b-0 last:mb-0 last:pb-0">
142
- {{ inline_admin_form.form.non_field_errors }}
143
-
144
- <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
145
- {% for fieldset in inline_admin_form %}
146
- {% for line in fieldset %}
81
+ {# Fieldset Content #}
82
+ <div class="fieldset-content px-6 py-6 space-y-6{% if fieldset.name %} hidden{% endif %}">
83
+ {% for line in fieldset %}
84
+ <div class="{% if line.fields|length > 1 %}grid grid-cols-1 md:grid-cols-{{ line.fields|length }} gap-6{% endif %}">
147
85
  {% for field in line %}
148
- <div>
86
+ <div class="{% if field.is_readonly %}opacity-75{% endif %}">
149
87
  <label for="{{ field.field.id_for_label }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
150
88
  {{ field.field.label }}
89
+ {% if field.field.field.required %}
90
+ <span class="text-red-600 dark:text-red-400">*</span>
91
+ {% endif %}
151
92
  </label>
93
+
94
+ {% if field.is_readonly %}
95
+ <div class="px-3 py-2 bg-gray-50 dark:bg-gray-750 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-gray-100">
96
+ {{ field.contents }}
97
+ </div>
98
+ {% else %}
152
99
  {{ field.field }}
100
+ {% endif %}
101
+
102
+ {% if field.field.help_text %}
103
+ <p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
104
+ {{ field.field.help_text|safe }}
105
+ </p>
106
+ {% endif %}
153
107
 
154
108
  {% if field.field.errors %}
155
109
  <div class="mt-2 text-sm text-red-600 dark:text-red-400">
@@ -158,49 +112,27 @@
158
112
  {% endif %}
159
113
  </div>
160
114
  {% endfor %}
161
- {% endfor %}
162
- {% endfor %}
163
115
  </div>
116
+ {% endfor %}
164
117
  </div>
165
- {% endfor %}
166
- </div>
118
+ </fieldset>
119
+ {% endfor %}
120
+ {% endblock %}
167
121
  </div>
122
+
123
+ {# Inline Forms - Uses stacked.html or tabular.html templates #}
124
+ {% block inline_field_sets %}
125
+ {% for inline_admin_formset in inline_admin_formsets %}
126
+ {% include inline_admin_formset.opts.template %}
168
127
  {% endfor %}
169
128
  {% endblock %}
170
129
 
171
- <!-- Submit Buttons -->
172
- <div class="flex items-center justify-between bg-gray-50 dark:bg-gray-800 px-6 py-4 rounded-lg border border-gray-200 dark:border-gray-700">
173
- <div class="flex items-center space-x-3">
174
- <button type="submit" name="_save" class="inline-flex items-center px-6 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors">
175
- {% trans 'Save' %}
176
- </button>
177
-
178
- {% if show_save_and_add_another %}
179
- <button type="submit" name="_addanother" class="inline-flex items-center px-4 py-2.5 bg-gray-600 hover:bg-gray-700 text-white rounded-lg font-medium transition-colors">
180
- {% trans 'Save and add another' %}
181
- </button>
182
- {% endif %}
183
-
184
- {% if show_save_and_continue %}
185
- <button type="submit" name="_continue" class="inline-flex items-center px-4 py-2.5 bg-gray-600 hover:bg-gray-700 text-white rounded-lg font-medium transition-colors">
186
- {% trans 'Save and continue editing' %}
187
- </button>
188
- {% endif %}
189
- </div>
190
-
191
- <div class="flex items-center space-x-3">
192
- {% if show_delete_link %}
193
- <a href="{% url opts|admin_urlname:'delete' original.pk|admin_urlquote %}" class="inline-flex items-center px-4 py-2.5 bg-red-600 hover:bg-red-700 text-white rounded-lg font-medium transition-colors">
194
- {% trans 'Delete' %}
195
- </a>
196
- {% endif %}
197
-
198
- <a href="{% url opts|admin_urlname:'changelist' %}" class="inline-flex items-center px-4 py-2.5 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg font-medium transition-colors">
199
- {% trans 'Cancel' %}
200
- </a>
201
- </div>
202
- </div>
203
- </form>
130
+ {# Submit Buttons #}
131
+ {% block submit_buttons_bottom %}
132
+ {% include "admin/submit_line.html" %}
133
+ {% endblock %}
134
+
135
+ </form>
204
136
  {% endblock %}
205
137
 
206
138
  {% block extrastyle %}
@@ -215,6 +147,48 @@
215
147
  <script src="{% static 'admin/js/jquery.init.js' %}"></script>
216
148
  <script src="{% static 'admin/js/urlify.js' %}"></script>
217
149
  <script src="{% static 'admin/js/prepopulate.js' %}"></script>
150
+ <script src="{% static 'admin/js/vendor/xregexp/xregexp.min.js' %}"></script>
151
+ <script src="{% static 'admin/js/inlines.js' %}"></script>
152
+ <script src="{% static 'admin/js/admin/RelatedObjectLookups.js' %}"></script>
218
153
  {{ media.js }}
219
154
  {% if add %}{% prepopulated_fields_js %}{% endif %}
220
- {% endblock %}
155
+
156
+ <script>
157
+ function toggleFieldset(header) {
158
+ const fieldset = header.closest('fieldset');
159
+ const content = fieldset.querySelector('.fieldset-content');
160
+ const description = fieldset.querySelector('.fieldset-description');
161
+ const icon = header.querySelector('.collapse-icon');
162
+
163
+ if (content.classList.contains('hidden')) {
164
+ content.classList.remove('hidden');
165
+ if (description) description.classList.remove('hidden');
166
+ icon.classList.remove('-rotate-90');
167
+ } else {
168
+ content.classList.add('hidden');
169
+ if (description) description.classList.add('hidden');
170
+ icon.classList.add('-rotate-90');
171
+ }
172
+ }
173
+
174
+ // Auto-expand fieldsets that contain validation errors
175
+ document.addEventListener('DOMContentLoaded', function() {
176
+ document.querySelectorAll('fieldset.module').forEach(function(fieldset) {
177
+ // Check if this fieldset contains any error messages
178
+ const hasErrors = fieldset.querySelector('.text-red-600, .text-red-400, .errorlist');
179
+ if (hasErrors) {
180
+ const content = fieldset.querySelector('.fieldset-content');
181
+ const description = fieldset.querySelector('.fieldset-description');
182
+ const icon = fieldset.querySelector('.collapse-icon');
183
+
184
+ if (content && content.classList.contains('hidden')) {
185
+ content.classList.remove('hidden');
186
+ if (description) description.classList.remove('hidden');
187
+ if (icon) icon.classList.remove('-rotate-90');
188
+ }
189
+ }
190
+ });
191
+ });
192
+ </script>
193
+
194
+ {% endblock %}
@@ -64,9 +64,9 @@
64
64
  </div>
65
65
 
66
66
  <!-- Results -->
67
- <form id="changelist-form" method="post">
67
+ <form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>
68
68
  {% csrf_token %}
69
-
69
+ {% if cl.formset %}{{ cl.formset.management_form }}{% endif %}
70
70
  <!-- Actions Bar -->
71
71
  {% if action_form and cl.show_admin_actions %}
72
72
  <div class="actions px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center gap-3">
@@ -103,6 +103,15 @@
103
103
  {% endif %}
104
104
  {% endblock %}
105
105
  </div>
106
+ <!-- Save Button for list_editable -->
107
+ {% if cl.formset and cl.result_count %}
108
+ <div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex items-center justify-end">
109
+ <input type="submit"
110
+ name="_save"
111
+ value="{% translate 'Save' %}"
112
+ class="px-6 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors cursor-pointer">
113
+ </div>
114
+ {% endif %}
106
115
  </form>
107
116
 
108
117
  <!-- Pagination -->
@@ -23,20 +23,21 @@
23
23
 
24
24
  <div class="p-6">
25
25
  <p class="text-gray-700 dark:text-gray-300 mb-4">
26
- {% blocktrans %}All of the following related items will be deleted:{% endblocktrans %}
26
+ {% blocktrans %}All of the following related items will be deleted: {% endblocktrans %}
27
27
  </p>
28
28
 
29
29
  <!-- Deleted Objects Summary -->
30
30
  {% if deleted_objects %}
31
31
  <div class="bg-gray-50 dark:bg-gray-750 rounded-lg p-4 mb-6">
32
- <h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-2">{% trans "Summary" %}</h3>
32
+ <h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-2">{% trans "Summary " %}</h3>
33
33
  <ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
34
34
  {% for deleted_object in deleted_objects %}
35
- <li class="flex items-center">
35
+ <li class="flex items-center" style="word-spacing: 0.25em; [&_a]:ml-2">
36
+
36
37
  <svg class="w-4 h-4 text-red-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
37
38
  <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
38
39
  </svg>
39
- {{ deleted_object }}
40
+ {{ deleted_object|safe }}
40
41
  </li>
41
42
  {% endfor %}
42
43
  </ul>
@@ -23,14 +23,14 @@
23
23
 
24
24
  <div class="p-6">
25
25
  <p class="text-gray-700 dark:text-gray-300 mb-4">
26
- {% blocktrans %}All of the following objects and their related items will be deleted:{% endblocktrans %}
26
+ {% blocktrans %}All of the following objects and their related items will be deleted: {% endblocktrans %}
27
27
  </p>
28
28
 
29
29
  <!-- Deleted Objects Summary -->
30
30
  {% if deletable_objects %}
31
31
  <div class="bg-gray-50 dark:bg-gray-750 rounded-lg p-4 mb-6 max-h-96 overflow-y-auto">
32
32
  <h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3">
33
- {% trans "Objects to be deleted:" %}
33
+ {% trans "Objects to be deleted: " %}
34
34
  </h3>
35
35
  <ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
36
36
  {% for deletable_object in deletable_objects %}
@@ -38,7 +38,7 @@
38
38
  <svg class="w-4 h-4 text-red-500 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
39
39
  <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
40
40
  </svg>
41
- {{ deletable_object }}
41
+ {{ deletable_object|safe }}
42
42
  </li>
43
43
  {% endfor %}
44
44
  </ul>