django-unfold 0.30.0__py3-none-any.whl → 0.32.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {django_unfold-0.30.0.dist-info → django_unfold-0.32.0.dist-info}/METADATA +101 -31
- {django_unfold-0.30.0.dist-info → django_unfold-0.32.0.dist-info}/RECORD +76 -73
- unfold/admin.py +30 -11
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +3 -3
- unfold/contrib/forms/templates/unfold/forms/array.html +3 -1
- unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +6 -6
- unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +1 -1
- unfold/contrib/forms/widgets.py +22 -11
- unfold/contrib/guardian/templates/unfold/guardian/group_form.html +4 -4
- unfold/contrib/guardian/templates/unfold/guardian/user_form.html +4 -4
- unfold/contrib/import_export/templates/admin/import_export/change_form.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_errors.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_preview.html +3 -3
- unfold/contrib/import_export/templates/admin/import_export/import_validation.html +4 -4
- unfold/contrib/inlines/forms.py +1 -2
- unfold/contrib/simple_history/templates/simple_history/object_history_list.html +9 -9
- unfold/contrib/simple_history/templates/simple_history/submit_line.html +1 -1
- unfold/dataclasses.py +10 -1
- unfold/fields.py +1 -1
- unfold/settings.py +1 -0
- unfold/sites.py +39 -15
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/js/alpine.anchor.js +1 -0
- unfold/static/unfold/js/alpine.js +2 -2
- unfold/static/unfold/js/alpine.persist.js +1 -1
- unfold/static/unfold/js/app.js +26 -3
- unfold/styles.css +10 -10
- unfold/templates/admin/actions.html +1 -1
- unfold/templates/admin/app_list.html +1 -1
- unfold/templates/admin/base.html +4 -4
- unfold/templates/admin/change_list.html +2 -2
- unfold/templates/admin/change_list_results.html +2 -2
- unfold/templates/admin/delete_confirmation.html +4 -4
- unfold/templates/admin/delete_selected_confirmation.html +4 -4
- unfold/templates/admin/edit_inline/stacked.html +2 -2
- unfold/templates/admin/edit_inline/tabular.html +3 -3
- unfold/templates/admin/filter.html +2 -2
- unfold/templates/admin/includes/fieldset.html +1 -1
- unfold/templates/admin/includes/object_delete_summary.html +1 -1
- unfold/templates/admin/login.html +8 -8
- unfold/templates/admin/object_history.html +4 -4
- unfold/templates/admin/search_form.html +1 -1
- unfold/templates/admin/submit_line.html +7 -5
- unfold/templates/auth/widgets/read_only_password_hash.html +1 -1
- unfold/templates/registration/logged_out.html +1 -1
- unfold/templates/unfold/change_list_filter.html +9 -1
- unfold/templates/unfold/components/card.html +7 -3
- unfold/templates/unfold/components/icon.html +1 -0
- unfold/templates/unfold/components/separator.html +1 -1
- unfold/templates/unfold/components/table.html +31 -0
- unfold/templates/unfold/helpers/account_links.html +2 -2
- unfold/templates/unfold/helpers/actions_row.html +4 -4
- unfold/templates/unfold/helpers/app_list.html +48 -38
- unfold/templates/unfold/helpers/app_list_default.html +4 -4
- unfold/templates/unfold/helpers/breadcrumb_item.html +1 -1
- unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
- unfold/templates/unfold/helpers/fieldset_row.html +6 -6
- unfold/templates/unfold/helpers/fieldsets_tabs.html +2 -2
- unfold/templates/unfold/helpers/header.html +1 -1
- unfold/templates/unfold/helpers/help_text.html +1 -1
- unfold/templates/unfold/helpers/history.html +1 -1
- unfold/templates/unfold/helpers/label.html +2 -3
- unfold/templates/unfold/helpers/search.html +7 -4
- unfold/templates/unfold/helpers/search_results.html +2 -2
- unfold/templates/unfold/helpers/tab_action.html +1 -1
- unfold/templates/unfold/helpers/tab_list.html +27 -5
- unfold/templates/unfold/helpers/theme_switch.html +2 -2
- unfold/templates/unfold/layouts/skeleton.html +6 -1
- unfold/templates/unfold/widgets/clearable_file_input.html +14 -6
- unfold/templates/unfold/widgets/clearable_file_input_small.html +4 -4
- unfold/templates/unfold/widgets/split_datetime.html +2 -2
- unfold/templatetags/unfold.py +33 -12
- unfold/templatetags/unfold_list.py +16 -6
- unfold/widgets.py +2 -2
- {django_unfold-0.30.0.dist-info → django_unfold-0.32.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.30.0.dist-info → django_unfold-0.32.0.dist-info}/WHEEL +0 -0
unfold/admin.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import copy
|
2
2
|
from functools import update_wrapper
|
3
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple
|
3
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
4
4
|
|
5
5
|
from django import forms
|
6
6
|
from django.contrib.admin import ModelAdmin as BaseModelAdmin
|
@@ -148,6 +148,7 @@ class ModelAdminMixin:
|
|
148
148
|
else:
|
149
149
|
kwargs["widget"] = UnfoldAdminSelectWidget()
|
150
150
|
|
151
|
+
if "choices" not in kwargs:
|
151
152
|
kwargs["choices"] = db_field.get_choices(
|
152
153
|
include_blank=db_field.blank, blank_choice=[("", _("Select value"))]
|
153
154
|
)
|
@@ -229,6 +230,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
229
230
|
add_fieldsets = ()
|
230
231
|
list_horizontal_scrollbar_top = False
|
231
232
|
list_filter_submit = False
|
233
|
+
list_fullwidth = False
|
232
234
|
compressed_fields = False
|
233
235
|
readonly_preprocess_fields = {}
|
234
236
|
checks_class = UnfoldModelAdminChecks
|
@@ -255,7 +257,10 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
255
257
|
return super().get_fieldsets(request, obj)
|
256
258
|
|
257
259
|
def _filter_unfold_actions_by_permissions(
|
258
|
-
self,
|
260
|
+
self,
|
261
|
+
request: HttpRequest,
|
262
|
+
actions: List[UnfoldAction],
|
263
|
+
object_id: Optional[Union[int, str]] = None,
|
259
264
|
) -> List[UnfoldAction]:
|
260
265
|
"""Filter out any Unfold actions that the user doesn't have access to."""
|
261
266
|
filtered_actions = []
|
@@ -263,12 +268,22 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
263
268
|
if not hasattr(action.method, "allowed_permissions"):
|
264
269
|
filtered_actions.append(action)
|
265
270
|
continue
|
271
|
+
|
266
272
|
permission_checks = (
|
267
273
|
getattr(self, f"has_{permission}_permission")
|
268
274
|
for permission in action.method.allowed_permissions
|
269
275
|
)
|
270
|
-
|
271
|
-
|
276
|
+
|
277
|
+
if object_id:
|
278
|
+
if any(
|
279
|
+
has_permission(request, object_id)
|
280
|
+
for has_permission in permission_checks
|
281
|
+
):
|
282
|
+
filtered_actions.append(action)
|
283
|
+
else:
|
284
|
+
if any(has_permission(request) for has_permission in permission_checks):
|
285
|
+
filtered_actions.append(action)
|
286
|
+
|
272
287
|
return filtered_actions
|
273
288
|
|
274
289
|
def get_actions_list(self, request: HttpRequest) -> List[UnfoldAction]:
|
@@ -282,9 +297,11 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
282
297
|
"""
|
283
298
|
return [self.get_unfold_action(action) for action in self.actions_list or []]
|
284
299
|
|
285
|
-
def get_actions_detail(
|
300
|
+
def get_actions_detail(
|
301
|
+
self, request: HttpRequest, object_id: int
|
302
|
+
) -> List[UnfoldAction]:
|
286
303
|
return self._filter_unfold_actions_by_permissions(
|
287
|
-
request, self._get_base_actions_detail()
|
304
|
+
request, self._get_base_actions_detail(), object_id
|
288
305
|
)
|
289
306
|
|
290
307
|
def _get_base_actions_detail(self) -> List[UnfoldAction]:
|
@@ -304,9 +321,11 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
304
321
|
"""
|
305
322
|
return [self.get_unfold_action(action) for action in self.actions_row or []]
|
306
323
|
|
307
|
-
def get_actions_submit_line(
|
324
|
+
def get_actions_submit_line(
|
325
|
+
self, request: HttpRequest, object_id: int
|
326
|
+
) -> List[UnfoldAction]:
|
308
327
|
return self._filter_unfold_actions_by_permissions(
|
309
|
-
request, self._get_base_actions_submit_line()
|
328
|
+
request, self._get_base_actions_submit_line(), object_id
|
310
329
|
)
|
311
330
|
|
312
331
|
def _get_base_actions_submit_line(self) -> List[UnfoldAction]:
|
@@ -404,7 +423,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
404
423
|
|
405
424
|
actions = []
|
406
425
|
if object_id:
|
407
|
-
for action in self.get_actions_detail(request):
|
426
|
+
for action in self.get_actions_detail(request, object_id):
|
408
427
|
actions.append(
|
409
428
|
{
|
410
429
|
"title": action.description,
|
@@ -418,7 +437,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
418
437
|
|
419
438
|
extra_context.update(
|
420
439
|
{
|
421
|
-
"actions_submit_line": self.get_actions_submit_line(request),
|
440
|
+
"actions_submit_line": self.get_actions_submit_line(request, object_id),
|
422
441
|
"actions_detail": actions,
|
423
442
|
}
|
424
443
|
)
|
@@ -485,7 +504,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
485
504
|
) -> None:
|
486
505
|
super().save_model(request, obj, form, change)
|
487
506
|
|
488
|
-
for action in self.get_actions_submit_line(request):
|
507
|
+
for action in self.get_actions_submit_line(request, obj.pk):
|
489
508
|
if action.action_name not in request.POST:
|
490
509
|
continue
|
491
510
|
|
@@ -9,11 +9,11 @@
|
|
9
9
|
|
10
10
|
{% if choice.min is not None and choice.max is not None and choice.step %}
|
11
11
|
<div class="admin-numeric-filter-slider-tooltips">
|
12
|
-
<span class="admin-numeric-filter-slider-tooltip-from border cursor-not-allowed flex flex-grow flex-row items-center mr-auto rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
12
|
+
<span class="admin-numeric-filter-slider-tooltip-from border cursor-not-allowed flex flex-grow flex-row items-center mr-auto rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
13
13
|
{{ choice.value_from }}
|
14
14
|
</span>
|
15
15
|
|
16
|
-
<span class="admin-numeric-filter-slider-tooltip-to border cursor-not-allowed flex flex-grow flex-row items-center rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
16
|
+
<span class="admin-numeric-filter-slider-tooltip-to border cursor-not-allowed flex flex-grow flex-row items-center rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
17
17
|
{{ choice.value_to }}
|
18
18
|
</span>
|
19
19
|
</div>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
{{ choice.form.as_p }}
|
26
26
|
</div>
|
27
27
|
{% else %}
|
28
|
-
<div class="admin-numeric-filter-slider-error dark:text-gray-
|
28
|
+
<div class="admin-numeric-filter-slider-error dark:text-gray-300">
|
29
29
|
<p>
|
30
30
|
{% trans 'Not enough data.' %}
|
31
31
|
</p>
|
@@ -15,7 +15,9 @@
|
|
15
15
|
|
16
16
|
<template x-for="(item, index) in items" :key="item.key">
|
17
17
|
<div class="flex flex-row">
|
18
|
-
{%
|
18
|
+
{% with widget=template %}
|
19
|
+
{% include template.template_name %}
|
20
|
+
{% endwith %}
|
19
21
|
|
20
22
|
<a x-on:click="items.splice(index, 1)" class="bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500">
|
21
23
|
<span class="material-symbols-outlined text-sm">delete</span>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<template id="trix-toolbar">
|
4
4
|
<div class="bg-gray-50 flex flex-row flex-wrap border group-[.errors]:border-t-red-600 gap-4 group-[.errors]:border-x-red-600 px-3 py-2 relative rounded-t-md shadow-sm w-full dark:bg-white/[.02] dark:border-gray-700 dark:group-[.errors]:border-t-red-500 dark:group-[.errors]:border-x-red-500">
|
5
|
-
<div data-trix-button-group="text-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm shrink-0 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
5
|
+
<div data-trix-button-group="text-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm shrink-0 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
6
6
|
{% spaceless %}
|
7
7
|
<button type="button" data-trix-attribute="p" data-trix-key="p" title="{% trans "Paragraph" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
8
8
|
<span class="material-symbols-outlined">format_paragraph</span>
|
@@ -30,7 +30,7 @@
|
|
30
30
|
{% endspaceless %}
|
31
31
|
</div>
|
32
32
|
|
33
|
-
<div data-trix-button-group="block-headings" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
33
|
+
<div data-trix-button-group="block-headings" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
34
34
|
{% spaceless %}
|
35
35
|
<button type="button" data-trix-attribute="heading1" title="{% trans "Heading" %} 1" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
36
36
|
<span class="material-symbols-outlined">format_h1</span>
|
@@ -50,7 +50,7 @@
|
|
50
50
|
{% endspaceless %}
|
51
51
|
</div>
|
52
52
|
|
53
|
-
<div data-trix-button-group="block-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
53
|
+
<div data-trix-button-group="block-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
54
54
|
{% spaceless %}
|
55
55
|
<button type="button" data-trix-attribute="quote" title="{% trans "Quote" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
56
56
|
<span class="material-symbols-outlined">format_quote</span>
|
@@ -79,7 +79,7 @@
|
|
79
79
|
</div>
|
80
80
|
|
81
81
|
<div class="lg:ml-auto">
|
82
|
-
<div data-trix-button-group="history-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
82
|
+
<div data-trix-button-group="history-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
83
83
|
<button type="button" data-trix-action="undo" data-trix-key="z" title="{% trans "Undo" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
84
84
|
<span class="material-symbols-outlined">undo</span>
|
85
85
|
</button>
|
@@ -92,9 +92,9 @@
|
|
92
92
|
<div data-trix-dialogs>
|
93
93
|
<div class="absolute bg-white border-b -bottom-px left-0 px-4 py-2 right-0 shadow-sm translate-y-full dark:bg-gray-900 dark:border-gray-700" data-trix-dialog="href" data-trix-dialog-attribute="href">
|
94
94
|
<div class="flex flex-row">
|
95
|
-
<input type="url" name="href" class="border bg-white font-medium px-3 rounded-md shadow-sm text-gray-500 text-sm focus:ring focus:ring-primary-300 focus:border-primary-600 focus:outline-none group-[.errors]:border-red-600 group-[.errors]:focus:ring-red-200 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
95
|
+
<input type="url" name="href" class="border bg-white font-medium px-3 rounded-md shadow-sm text-gray-500 text-sm focus:ring focus:ring-primary-300 focus:border-primary-600 focus:outline-none group-[.errors]:border-red-600 group-[.errors]:focus:ring-red-200 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300 dark:focus:ring-primary-600/30 dark:group-[.errors]:border-red-500 dark:group-[.errors]:focus:ring-red-600/40" placeholder="{% trans "Enter an URL" %}" required data-trix-input>
|
96
96
|
|
97
|
-
<div class="bg-white border border-md flex flex-row ml-4 rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
97
|
+
<div class="bg-white border border-md flex flex-row ml-4 rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
98
98
|
<button type="button" data-trix-method="setAttribute" title="{% trans "Link" %}" class="border-r cursor-pointer flex items-center h-8 justify-center text-gray-400 transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
99
99
|
<span class="material-symbols-outlined">link</span>
|
100
100
|
</button>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% include "unfold/forms/helpers/toolbar.html" %}
|
2
2
|
|
3
|
-
<input type="hidden" name="{{ widget.name }}" id="wysiwyg-{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}>
|
3
|
+
<input type="hidden" name="{{ widget.name }}" id="wysiwyg-{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %} {% include "django/forms/widgets/attrs.html" %}>
|
4
4
|
|
5
5
|
<div class="max-w-4xl relative">
|
6
6
|
<trix-editor input="wysiwyg-{{ widget.name }}" {% include "django/forms/widgets/attrs.html" %}></trix-editor>
|
unfold/contrib/forms/widgets.py
CHANGED
@@ -4,7 +4,11 @@ from django.core.validators import EMPTY_VALUES
|
|
4
4
|
from django.forms import MultiWidget, Widget
|
5
5
|
from django.http import QueryDict
|
6
6
|
from django.utils.datastructures import MultiValueDict
|
7
|
-
from unfold.widgets import
|
7
|
+
from unfold.widgets import (
|
8
|
+
PROSE_CLASSES,
|
9
|
+
UnfoldAdminSelectWidget,
|
10
|
+
UnfoldAdminTextInputWidget,
|
11
|
+
)
|
8
12
|
|
9
13
|
WYSIWYG_CLASSES = [
|
10
14
|
*PROSE_CLASSES,
|
@@ -20,26 +24,33 @@ WYSIWYG_CLASSES = [
|
|
20
24
|
"w-full",
|
21
25
|
"focus:outline-none",
|
22
26
|
"dark:border-gray-700",
|
23
|
-
"dark:text-gray-
|
27
|
+
"dark:text-gray-300",
|
24
28
|
"dark:group-[.errors]:border-red-500",
|
25
29
|
]
|
26
30
|
|
27
31
|
|
28
32
|
class ArrayWidget(MultiWidget):
|
29
33
|
template_name = "unfold/forms/array.html"
|
30
|
-
widget_class = UnfoldAdminTextInputWidget
|
31
34
|
|
32
35
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
33
|
-
|
36
|
+
if "choices" in kwargs:
|
37
|
+
self.choices = kwargs["choices"]
|
38
|
+
|
39
|
+
widgets = [self.get_widget_instance()]
|
34
40
|
super().__init__(widgets)
|
35
41
|
|
42
|
+
def get_widget_instance(self) -> Any:
|
43
|
+
if hasattr(self, "choices"):
|
44
|
+
return UnfoldAdminSelectWidget(choices=self.choices)
|
45
|
+
|
46
|
+
return UnfoldAdminTextInputWidget()
|
47
|
+
|
36
48
|
def get_context(self, name: str, value: str, attrs: Dict) -> Dict:
|
37
49
|
self._resolve_widgets(value)
|
38
50
|
context = super().get_context(name, value, attrs)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
context.update({"template": template_widget})
|
51
|
+
context.update(
|
52
|
+
{"template": self.get_widget_instance().get_context(name, "", {})["widget"]}
|
53
|
+
)
|
43
54
|
return context
|
44
55
|
|
45
56
|
def value_from_datadict(
|
@@ -71,12 +82,12 @@ class ArrayWidget(MultiWidget):
|
|
71
82
|
value = []
|
72
83
|
|
73
84
|
elif isinstance(value, List):
|
74
|
-
self.widgets = [self.
|
85
|
+
self.widgets = [self.get_widget_instance() for item in value]
|
75
86
|
else:
|
76
|
-
self.widgets = [self.
|
87
|
+
self.widgets = [self.get_widget_instance() for item in value.split(",")]
|
77
88
|
|
78
89
|
self.widgets_names = ["" for i in range(len(self.widgets))]
|
79
|
-
self.widgets = [w
|
90
|
+
self.widgets = [w if isinstance(w, type) else w for w in self.widgets]
|
80
91
|
|
81
92
|
|
82
93
|
class WysiwygWidget(Widget):
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</h2>
|
9
9
|
|
10
10
|
{% if groups_perms.items %}
|
11
|
-
<table id="group-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
11
|
+
<table id="group-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
12
12
|
<thead class="hidden lg:table-header-group">
|
13
13
|
<tr>
|
14
14
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<tbody>
|
31
31
|
{% for group, group_perms in groups_perms.items %}
|
32
32
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
33
|
-
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
33
|
+
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "User" %}">
|
34
34
|
<span class="text-gray-700 dark:text-gray-200">
|
35
35
|
{{ group }}
|
36
36
|
</span>
|
37
37
|
</th>
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
40
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
42
|
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
43
|
{% else %}
|
@@ -46,7 +46,7 @@
|
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
48
48
|
|
49
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
49
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "Action" %}">
|
50
50
|
<a href="group-manage/{{ group.id|safe }}/" class="hover:text-gray-700 dark:hover:text-white">
|
51
51
|
{% trans "Edit" %}
|
52
52
|
</a>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</h2>
|
9
9
|
|
10
10
|
{% if users_perms.items %}
|
11
|
-
<table id="user-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
11
|
+
<table id="user-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
12
12
|
<thead class="hidden lg:table-header-group">
|
13
13
|
<tr>
|
14
14
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<tbody>
|
31
31
|
{% for user, user_perms in users_perms.items %}
|
32
32
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
33
|
-
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
33
|
+
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "User" %}">
|
34
34
|
<span class="text-gray-700 dark:text-gray-200">
|
35
35
|
{{ user }}
|
36
36
|
</span>
|
37
37
|
</th>
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
40
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
42
|
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
43
|
{% else %}
|
@@ -46,7 +46,7 @@
|
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
48
48
|
|
49
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
49
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "Action" %}">
|
50
50
|
<a href="user-manage/{{ user.id|safe }}/" class="hover:text-gray-700 dark:hover:text-white">
|
51
51
|
{% trans "Edit" %}
|
52
52
|
</a>
|
@@ -5,6 +5,6 @@
|
|
5
5
|
{{ block.super }}
|
6
6
|
|
7
7
|
{% if show_change_form_export %}
|
8
|
-
<input type="submit" value="{% translate 'Export' %}" name="_export-item" class="bg-white text-gray-500 border cursor-pointer flex font-medium items-center px-3 py-2 mr-3 rounded-md shadow-sm text-sm dark:bg-gray-900 dark:border dark:border-gray-700 dark:text-gray-
|
8
|
+
<input type="submit" value="{% translate 'Export' %}" name="_export-item" class="bg-white text-gray-500 border cursor-pointer flex font-medium items-center px-3 py-2 mr-3 rounded-md shadow-sm text-sm dark:bg-gray-900 dark:border dark:border-gray-700 dark:text-gray-300">
|
9
9
|
{% endif %}
|
10
10
|
{% endblock %}
|
@@ -26,7 +26,7 @@
|
|
26
26
|
</div>
|
27
27
|
</div>
|
28
28
|
|
29
|
-
<div class="block border leading-relaxed rounded p-4 text-sm traceback dark:border-gray-800 dark:text-gray-
|
29
|
+
<div class="block border leading-relaxed rounded p-4 text-sm traceback dark:border-gray-800 dark:text-gray-300 " x-show="open">
|
30
30
|
{{ error.traceback|linebreaks }}
|
31
31
|
</div>
|
32
32
|
</li>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% load admin_urls i18n import_export_tags unfold %}
|
2
2
|
|
3
3
|
{% block preview %}
|
4
|
-
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-
|
4
|
+
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
5
5
|
<thead class="hidden lg:table-header-group">
|
6
6
|
<tr>
|
7
7
|
<th class="align-middle capitalize font-medium px-3 py-2 text-left text-gray-400 text-sm"></th>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<tbody>
|
18
18
|
{% for row in result.valid_rows %}
|
19
19
|
<tr class="{{ row.import_type }} {% cycle '' 'bg-gray-50 dark:bg-white/[.02]' %} block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
20
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
20
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
21
21
|
{% if row.import_type == 'new' %}
|
22
22
|
{% trans "New" %}
|
23
23
|
{% elif row.import_type == 'skip' %}
|
@@ -30,7 +30,7 @@
|
|
30
30
|
</td>
|
31
31
|
|
32
32
|
{% for field in row.diff %}
|
33
|
-
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
33
|
+
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
34
34
|
{{ field }}
|
35
35
|
</td>
|
36
36
|
{% endfor %}
|
@@ -15,7 +15,7 @@
|
|
15
15
|
</div>
|
16
16
|
</div>
|
17
17
|
|
18
|
-
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-
|
18
|
+
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
19
19
|
<thead class="hidden lg:table-header-group">
|
20
20
|
<tr>
|
21
21
|
<th class="align-middle capitalize font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -37,11 +37,11 @@
|
|
37
37
|
<tbody>
|
38
38
|
{% for row in result.invalid_rows %}
|
39
39
|
<tr class="{% cycle '' 'bg-gray-50 dark:bg-white/[.02]' %} block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
40
|
-
<td data-label="{% trans "Row" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
40
|
+
<td data-label="{% trans "Row" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
41
41
|
{{ row.number }}
|
42
42
|
</td>
|
43
43
|
|
44
|
-
<td data-label="{% trans "Errors" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
44
|
+
<td data-label="{% trans "Errors" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
45
45
|
<div>
|
46
46
|
<span class="bg-red-600 font-semibold ml-2 px-1 rounded-sm text-xs text-white">{{ row.error_count }}</span>
|
47
47
|
</div>
|
@@ -77,7 +77,7 @@
|
|
77
77
|
</td>
|
78
78
|
|
79
79
|
{% for field in row.values %}
|
80
|
-
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
80
|
+
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
81
81
|
{{ field }}
|
82
82
|
</td>
|
83
83
|
{% endfor %}
|
unfold/contrib/inlines/forms.py
CHANGED
@@ -12,9 +12,8 @@ class NonrelatedInlineModelFormSet(BaseModelFormSet):
|
|
12
12
|
**kwargs: Any,
|
13
13
|
) -> None:
|
14
14
|
self.instance = instance
|
15
|
-
self.queryset = self.provided_queryset
|
16
|
-
|
17
15
|
super().__init__(**kwargs)
|
16
|
+
self.queryset = self.provided_queryset
|
18
17
|
|
19
18
|
@classmethod
|
20
19
|
def get_default_prefix(cls: BaseModelFormSet) -> str:
|
@@ -3,7 +3,7 @@
|
|
3
3
|
{% load admin_urls %}
|
4
4
|
{% load getattribute from getattributes %}
|
5
5
|
|
6
|
-
<table id="change-history" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
6
|
+
<table id="change-history" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
7
7
|
<thead class="hidden lg:table-header-group">
|
8
8
|
<tr>
|
9
9
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -41,27 +41,27 @@
|
|
41
41
|
<tbody>
|
42
42
|
{% for record in historical_records %}
|
43
43
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
44
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
44
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Object' %}">
|
45
45
|
<a href="{% url opts|admin_urlname:'simple_history' object.pk record.pk %}">
|
46
46
|
{{ record.history_object }}
|
47
47
|
</a>
|
48
48
|
</td>
|
49
49
|
|
50
50
|
{% for column in history_list_display %}
|
51
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
51
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans column %}">
|
52
52
|
{{ record|getattribute:column }}
|
53
53
|
</th>
|
54
54
|
{% endfor %}
|
55
55
|
|
56
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
56
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Date/time' %}">
|
57
57
|
{{ record.history_date }}
|
58
58
|
</td>
|
59
59
|
|
60
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
60
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Comment' %}">
|
61
61
|
{{ record.get_history_type_display }}
|
62
62
|
</td>
|
63
63
|
|
64
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
64
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changed by' %}">
|
65
65
|
{% if record.history_user %}
|
66
66
|
{% url admin_user_view record.history_user_id as admin_user_url %}
|
67
67
|
|
@@ -75,11 +75,11 @@
|
|
75
75
|
{% endif %}
|
76
76
|
</td>
|
77
77
|
|
78
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
78
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Change reason' %}">
|
79
79
|
{{ record.history_change_reason }}
|
80
80
|
</td>
|
81
81
|
|
82
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
82
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changes' %}">
|
83
83
|
{% block history_delta_changes %}
|
84
84
|
{% if record.history_delta_changes %}
|
85
85
|
<ul>
|
@@ -97,7 +97,7 @@
|
|
97
97
|
{% endif %}
|
98
98
|
|
99
99
|
{% if change.old and change.new %}
|
100
|
-
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-
|
100
|
+
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-300 dark:text-gray-600">arrow_right_alt</span>
|
101
101
|
{% endif %}
|
102
102
|
|
103
103
|
{% if change.new %}
|
@@ -16,7 +16,7 @@
|
|
16
16
|
</button>
|
17
17
|
{% endif %}
|
18
18
|
|
19
|
-
<a href="{{ history_url }}" class="border font-medium mr-auto px-3 py-2 rounded-md text-sm text-gray-500 text-center transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:text-gray-
|
19
|
+
<a href="{{ history_url }}" class="border font-medium mr-auto px-3 py-2 rounded-md text-sm text-gray-500 text-center transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:text-gray-300 dark:hover:text-gray-200 dark:hover:bg-gray-900">
|
20
20
|
{% trans 'Close' %}
|
21
21
|
</a>
|
22
22
|
</div>
|
unfold/dataclasses.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
-
from typing import Dict, Optional
|
2
|
+
from typing import Callable, Dict, Optional, Union
|
3
3
|
|
4
4
|
from .typing import ActionFunction
|
5
5
|
|
@@ -11,3 +11,12 @@ class UnfoldAction:
|
|
11
11
|
description: str
|
12
12
|
path: str
|
13
13
|
attrs: Optional[Dict] = None
|
14
|
+
object_id: Optional[Union[int, str]] = None
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass
|
18
|
+
class Favicon:
|
19
|
+
href: Union[str, Callable]
|
20
|
+
rel: Optional[str] = None
|
21
|
+
type: Optional[str] = None
|
22
|
+
sizes: Optional[str] = None
|
unfold/fields.py
CHANGED
@@ -95,7 +95,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
95
95
|
current_app=self.model_admin.admin_site.name,
|
96
96
|
)
|
97
97
|
return format_html(
|
98
|
-
'<a href="{}" class="text-primary-600 underline
|
98
|
+
'<a href="{}" class="text-primary-600 underline dark:text-primary-500">{}</a>',
|
99
99
|
url,
|
100
100
|
remote_obj,
|
101
101
|
)
|