django-smartbase-admin 0.2.54__py3-none-any.whl → 1.0.38__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_smartbase_admin/actions/admin_action_list.py +74 -38
- django_smartbase_admin/actions/advanced_filters.py +24 -1
- django_smartbase_admin/admin/admin_base.py +401 -96
- django_smartbase_admin/admin/site.py +93 -35
- django_smartbase_admin/admin/widgets.py +589 -26
- django_smartbase_admin/apps.py +2 -0
- django_smartbase_admin/engine/actions.py +34 -16
- django_smartbase_admin/engine/admin_base_view.py +252 -115
- django_smartbase_admin/engine/configuration.py +186 -4
- django_smartbase_admin/engine/const.py +6 -0
- django_smartbase_admin/engine/dashboard.py +44 -23
- django_smartbase_admin/engine/fake_inline.py +15 -11
- django_smartbase_admin/engine/field.py +42 -12
- django_smartbase_admin/engine/field_formatter.py +22 -8
- django_smartbase_admin/engine/filter_widgets.py +309 -20
- django_smartbase_admin/engine/menu_item.py +8 -5
- django_smartbase_admin/engine/modal_view.py +12 -7
- django_smartbase_admin/engine/request.py +2 -0
- django_smartbase_admin/integration/__init__.py +0 -0
- django_smartbase_admin/integration/django_cms.py +43 -0
- django_smartbase_admin/locale/sk/LC_MESSAGES/django.mo +0 -0
- django_smartbase_admin/locale/sk/LC_MESSAGES/django.po +268 -37
- django_smartbase_admin/migrations/0005_sbadminuserconfiguration.py +26 -0
- django_smartbase_admin/migrations/0006_alter_sbadminuserconfiguration_color_scheme.py +18 -0
- django_smartbase_admin/models.py +22 -0
- django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py +96 -0
- django_smartbase_admin/monkeypatch/fake_inline_monkeypatch.py +1 -1
- django_smartbase_admin/querysets.py +3 -0
- django_smartbase_admin/services/configuration.py +30 -0
- django_smartbase_admin/services/thread_local.py +6 -19
- django_smartbase_admin/services/views.py +80 -13
- django_smartbase_admin/services/xlsx_export.py +6 -0
- django_smartbase_admin/static/sb_admin/build/tailwind.config.js +1 -0
- django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/colors.js +4 -0
- django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/spacing.js +1 -0
- django_smartbase_admin/static/sb_admin/build/webpack.common.js +11 -8
- django_smartbase_admin/static/sb_admin/css/ckeditor/ckeditor_content_dark.css +208 -0
- django_smartbase_admin/static/sb_admin/css/coloris/coloris.min.css +1 -0
- django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -0
- django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -0
- django_smartbase_admin/static/sb_admin/dist/calendar_style.js +0 -0
- django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
- django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/tree_widget.js +1 -0
- django_smartbase_admin/static/sb_admin/dist/tree_widget_style.css +1 -0
- django_smartbase_admin/static/sb_admin/dist/tree_widget_style.js +0 -0
- django_smartbase_admin/static/sb_admin/fancytree/jquery.fancytree-all-deps.min.js +1 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-csv.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-doc.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-docx.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-other.svg +13 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-pdf.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-ppt.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-xls.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-xlsx.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-zip.svg +18 -0
- django_smartbase_admin/static/sb_admin/images/flags/de-at.png +0 -0
- django_smartbase_admin/static/sb_admin/images/flags/de-ch.png +0 -0
- django_smartbase_admin/static/sb_admin/images/logo_light.svg +21 -0
- django_smartbase_admin/static/sb_admin/js/coloris/coloris.min.js +6 -0
- django_smartbase_admin/static/sb_admin/js/fullcalendar.min.js +14804 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Bolt-one.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Calendar.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Caution.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Electric-drill.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Fire-extinguisher.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Gas.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Lightning-fill.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Moon.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Phone-telephone.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Printer.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Pull.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Sun-one.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Time.svg +3 -0
- django_smartbase_admin/static/sb_admin/src/css/_base.css +5 -1
- django_smartbase_admin/static/sb_admin/src/css/_colors.css +257 -82
- django_smartbase_admin/static/sb_admin/src/css/_components.css +61 -13
- django_smartbase_admin/static/sb_admin/src/css/_datepicker.css +8 -1
- django_smartbase_admin/static/sb_admin/src/css/_filer.css +60 -0
- django_smartbase_admin/static/sb_admin/src/css/_inlines.css +51 -10
- django_smartbase_admin/static/sb_admin/src/css/_tabulator.css +8 -2
- django_smartbase_admin/static/sb_admin/src/css/calendar.css +162 -0
- django_smartbase_admin/static/sb_admin/src/css/components/_button.css +41 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +26 -8
- django_smartbase_admin/static/sb_admin/src/css/components/_input.css +62 -20
- django_smartbase_admin/static/sb_admin/src/css/components/_modal.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +21 -2
- django_smartbase_admin/static/sb_admin/src/css/components/_toggle.css +12 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_tooltip.css +8 -22
- django_smartbase_admin/static/sb_admin/src/css/style.css +17 -0
- django_smartbase_admin/static/sb_admin/src/css/tree_widget.css +411 -0
- django_smartbase_admin/static/sb_admin/src/js/autocomplete.js +63 -5
- django_smartbase_admin/static/sb_admin/src/js/calendar.js +56 -0
- django_smartbase_admin/static/sb_admin/src/js/chart.js +8 -22
- django_smartbase_admin/static/sb_admin/src/js/choices.js +18 -8
- django_smartbase_admin/static/sb_admin/src/js/datepicker.js +97 -336
- django_smartbase_admin/static/sb_admin/src/js/datepicker_plugins.js +357 -0
- django_smartbase_admin/static/sb_admin/src/js/main.js +304 -31
- django_smartbase_admin/static/sb_admin/src/js/multiselect.js +50 -41
- django_smartbase_admin/static/sb_admin/src/js/range.js +3 -2
- django_smartbase_admin/static/sb_admin/src/js/table.js +34 -5
- django_smartbase_admin/static/sb_admin/src/js/table_modules/advanced_filter_module.js +43 -20
- django_smartbase_admin/static/sb_admin/src/js/table_modules/data_edit_module.js +8 -10
- django_smartbase_admin/static/sb_admin/src/js/table_modules/filter_module.js +3 -3
- django_smartbase_admin/static/sb_admin/src/js/table_modules/header_tabs_module.js +11 -11
- django_smartbase_admin/static/sb_admin/src/js/table_modules/selection_module.js +28 -8
- django_smartbase_admin/static/sb_admin/src/js/table_modules/table_params_module.js +6 -0
- django_smartbase_admin/static/sb_admin/src/js/table_modules/views_module.js +6 -0
- django_smartbase_admin/static/sb_admin/src/js/tree_widget.js +406 -0
- django_smartbase_admin/static/sb_admin/src/js/utils.js +56 -21
- django_smartbase_admin/templates/sb_admin/actions/change_form.html +169 -114
- django_smartbase_admin/templates/sb_admin/actions/dashboard.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/list.html +79 -39
- django_smartbase_admin/templates/sb_admin/actions/partials/action_link.html +14 -0
- django_smartbase_admin/templates/sb_admin/actions/partials/tabulator_header_v2.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/tree_list.html +63 -0
- django_smartbase_admin/templates/sb_admin/authentification/login_base.html +5 -1
- django_smartbase_admin/templates/sb_admin/components/columns.html +1 -1
- django_smartbase_admin/templates/sb_admin/components/filters_v2.html +99 -85
- django_smartbase_admin/templates/sb_admin/dashboard/calendar_widget.html +69 -0
- django_smartbase_admin/templates/sb_admin/dashboard/chart_widget.html +21 -2
- django_smartbase_admin/templates/sb_admin/dashboard/list_widget.html +6 -0
- django_smartbase_admin/templates/sb_admin/dashboard/widget_base.html +1 -1
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/date_field.html +18 -8
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html +1 -1
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/tree_select_filter.html +2 -0
- django_smartbase_admin/templates/sb_admin/filter_widgets/date_field.html +18 -4
- django_smartbase_admin/templates/sb_admin/filter_widgets/multiple_choice_field.html +14 -0
- django_smartbase_admin/templates/sb_admin/filter_widgets/partials/clear.html +10 -5
- django_smartbase_admin/templates/sb_admin/filter_widgets/radio_choice_field.html +2 -2
- django_smartbase_admin/templates/sb_admin/filter_widgets/tree_select_filter.html +16 -0
- django_smartbase_admin/templates/sb_admin/includes/change_form_title.html +3 -1
- django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html +48 -39
- django_smartbase_admin/templates/sb_admin/includes/notifications.html +2 -1
- django_smartbase_admin/templates/sb_admin/includes/readonly_boolean_field.html +9 -0
- django_smartbase_admin/templates/sb_admin/includes/readonly_field.html +12 -0
- django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html +4 -5
- django_smartbase_admin/templates/sb_admin/inlines/stacked_inline.html +68 -40
- django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +76 -34
- django_smartbase_admin/templates/sb_admin/integrations/filer/folder_list.html +18 -0
- django_smartbase_admin/templates/sb_admin/navigation.html +166 -158
- django_smartbase_admin/templates/sb_admin/partials/modal/modal_content.html +2 -6
- django_smartbase_admin/templates/sb_admin/sb_admin_base.html +49 -4
- django_smartbase_admin/templates/sb_admin/sb_admin_base_no_sidebar.html +27 -11
- django_smartbase_admin/templates/sb_admin/sb_admin_js_trans.html +3 -0
- django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
- django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +6 -3
- django_smartbase_admin/templates/sb_admin/widgets/array.html +0 -1
- django_smartbase_admin/templates/sb_admin/widgets/attributes.html +68 -0
- django_smartbase_admin/templates/sb_admin/widgets/autocomplete.html +13 -2
- django_smartbase_admin/templates/sb_admin/widgets/{checkbox_select.html → checkbox_dropdown.html} +2 -2
- django_smartbase_admin/templates/sb_admin/widgets/clearable_file_input.html +2 -2
- django_smartbase_admin/templates/sb_admin/widgets/color_field.html +30 -0
- django_smartbase_admin/templates/sb_admin/widgets/date.html +8 -1
- django_smartbase_admin/templates/sb_admin/widgets/filer_file.html +84 -0
- django_smartbase_admin/templates/sb_admin/widgets/includes/related_item_buttons.html +38 -0
- django_smartbase_admin/templates/sb_admin/widgets/multiwidget.html +1 -1
- django_smartbase_admin/templates/sb_admin/widgets/radio.html +3 -2
- django_smartbase_admin/templates/sb_admin/widgets/radio_dropdown.html +30 -0
- django_smartbase_admin/templates/sb_admin/widgets/read_only_password_hash.html +3 -0
- django_smartbase_admin/templates/sb_admin/widgets/time.html +8 -1
- django_smartbase_admin/templates/sb_admin/widgets/toggle.html +1 -1
- django_smartbase_admin/templates/sb_admin/widgets/tree_base.html +59 -0
- django_smartbase_admin/templates/sb_admin/widgets/tree_select.html +24 -0
- django_smartbase_admin/templates/sb_admin/widgets/tree_select_inline.html +12 -0
- django_smartbase_admin/templatetags/sb_admin_tags.py +85 -4
- django_smartbase_admin/utils.py +22 -3
- django_smartbase_admin/views/dashboard_view.py +6 -0
- django_smartbase_admin/views/global_filter_view.py +8 -2
- django_smartbase_admin/views/translations_view.py +12 -5
- django_smartbase_admin/views/user_config_view.py +52 -0
- django_smartbase_admin-1.0.38.dist-info/METADATA +166 -0
- {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/RECORD +177 -115
- {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/WHEEL +1 -1
- django_smartbase_admin/templates/sb_admin/integrations/sorting/change_list.html +0 -401
- django_smartbase_admin-0.2.54.dist-info/METADATA +0 -25
- {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/LICENSE.md +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{% load i18n filer_admin_tags static %}
|
|
2
|
+
|
|
3
|
+
{% spaceless %}
|
|
4
|
+
<div class="clearfix"></div>
|
|
5
|
+
|
|
6
|
+
<div class="dz-preview dz-file-preview hidden js-filer-dropzone-template">
|
|
7
|
+
<span class="filerFile">
|
|
8
|
+
<div class="dz-thumbnail"><img class="quiet" data-dz-thumbnail></div>
|
|
9
|
+
<div>
|
|
10
|
+
<span data-dz-name class="dz-name"></span>
|
|
11
|
+
<span class="filerClearer filer-icon filer-icon-remove-selection" title="{% trans 'Clear' %}"
|
|
12
|
+
data-dz-remove data-no-icon-file="{% static 'filer/icons/file-unknown.svg' %}">
|
|
13
|
+
<button type="button" class="btn btn-tiny btn-destructive">{% trans 'Delete' %}</button>
|
|
14
|
+
</span>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="dz-progress js-filer-dropzone-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
|
|
17
|
+
</span>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="js-filer-dropzone filer-dropzone{% if object %} js-object-attached{% endif %}"
|
|
21
|
+
data-url="{% url 'admin:filer-ajax_upload' %}"
|
|
22
|
+
data-max-files="1"
|
|
23
|
+
{% if max_filesize %}}data-max-filesize="{{ max_filesize|safe }}"{% endif %}>
|
|
24
|
+
<div class="z-index-fix"></div>
|
|
25
|
+
<div class="dz-default dz-message js-filer-dropzone-message{% if object %} hidden{% endif %}">
|
|
26
|
+
<span class="w-64 h-64 flex items-center justify-center rounded-full mr-16 js-input-file-empty input-file-upload-icon">
|
|
27
|
+
<svg class="w-24 h-24">
|
|
28
|
+
<use xlink:href="#Upload"></use>
|
|
29
|
+
</svg>
|
|
30
|
+
</span>
|
|
31
|
+
<span>{% trans "drop your file here or" %}</span>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<span class="filerFile js-file-selector">
|
|
35
|
+
{% if object %}
|
|
36
|
+
{% if object.file.exists %}
|
|
37
|
+
<a href="{{ object.url }}" target="_blank">{% file_icon object size='64x64' %}</a>
|
|
38
|
+
{% else %}
|
|
39
|
+
{% file_icon object %}
|
|
40
|
+
{% endif %}
|
|
41
|
+
{% else %}
|
|
42
|
+
<img class="thumbnail_img hidden quiet" alt="{% trans 'No file selected' %}">
|
|
43
|
+
{% endif %}
|
|
44
|
+
|
|
45
|
+
<div>
|
|
46
|
+
{% if object %}
|
|
47
|
+
{% if object.file.exists %}
|
|
48
|
+
<span class="description_text">{{ object.label }}</span>
|
|
49
|
+
{% else %}
|
|
50
|
+
{% file_icon object %}
|
|
51
|
+
<span class="description_text">{% trans 'File is missing' %}</span>
|
|
52
|
+
{% endif %}
|
|
53
|
+
{% else %}
|
|
54
|
+
<span class="description_text"></span>
|
|
55
|
+
{% endif %}
|
|
56
|
+
|
|
57
|
+
<div class="flex items-center gap-4">
|
|
58
|
+
<a class="filerClearer {% if not object %}hidden{% endif %}" title="{% trans 'Clear' %}"
|
|
59
|
+
data-no-icon-file="{% static 'filer/icons/file-unknown.svg' %}">
|
|
60
|
+
<button type="button" class="btn btn-tiny btn-destructive">{% trans 'Delete' %}</button>
|
|
61
|
+
</a>
|
|
62
|
+
|
|
63
|
+
<a href="{{ lookup_url }}" class="js-related-lookup related-lookup {% if object %}related-lookup-change{% endif %} lookup" id="{{ id }}_lookup"
|
|
64
|
+
title="{% trans 'Lookup' %}">
|
|
65
|
+
<button type="button" class="btn btn-primary-light btn-tiny add-file-button">{% trans 'Choose File' %}</button>
|
|
66
|
+
</a>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
<div class="hidden">{{ hidden_input }}</div>
|
|
72
|
+
<script type="text/javascript" id="{{id}}_javascript">
|
|
73
|
+
django.jQuery(document).ready(function(){
|
|
74
|
+
var plus = django.jQuery('#add_{{ id }}');
|
|
75
|
+
if (plus.length){
|
|
76
|
+
plus.remove();
|
|
77
|
+
}
|
|
78
|
+
// Delete this javascript once loaded to avoid the "add new" link duplicates it
|
|
79
|
+
django.jQuery('#{{id}}_javascript').remove();
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
84
|
+
{% endspaceless %}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
<div class="flex items-center gap-4 flex-shrink-0 related-item-buttons">
|
|
3
|
+
{% if widget.attrs.related_edit_url or widget.attrs.related_add_url %}
|
|
4
|
+
<a
|
|
5
|
+
class="btn btn-icon edit-button {% if not widget.attrs.related_edit_url %}hidden{% endif %}"
|
|
6
|
+
title="{% trans 'Edit' %}"
|
|
7
|
+
{% if widget.attrs.related_edit_url %}
|
|
8
|
+
hx-get="{{ widget.attrs.related_edit_url }}?_popup=1&sbadmin_is_modal=1&sb_admin_source_field={{ widget.attrs.id }}{% if reload_on_save %}&sbadmin_reload_on_save=1{% endif %}"
|
|
9
|
+
{% endif %}
|
|
10
|
+
{% if widget.attrs.related_add_url and not widget.attrs.related_edit_url %}
|
|
11
|
+
data-add-url="{{ widget.attrs.related_add_url }}?_popup=1&sbadmin_is_modal=1&sb_admin_source_field={{ widget.attrs.id }}"
|
|
12
|
+
{% endif %}
|
|
13
|
+
data-bs-toggle="modal"
|
|
14
|
+
data-bs-target="#sb-admin-modal"
|
|
15
|
+
hx-target="#sb-admin-modal"
|
|
16
|
+
hx-select="#modal-content"
|
|
17
|
+
hx-swap="innerHTML">
|
|
18
|
+
<svg class="w-20 h-20">
|
|
19
|
+
<use xlink:href="#Edit"></use>
|
|
20
|
+
</svg>
|
|
21
|
+
</a>
|
|
22
|
+
{% endif %}
|
|
23
|
+
{% if widget.attrs.related_add_url %}
|
|
24
|
+
<a
|
|
25
|
+
class="btn btn-icon"
|
|
26
|
+
title="{% trans 'Add' %}"
|
|
27
|
+
data-bs-toggle="modal"
|
|
28
|
+
data-bs-target="#sb-admin-modal"
|
|
29
|
+
hx-get="{{ widget.attrs.related_add_url }}?_popup=1&sbadmin_is_modal=1&sb_admin_source_field={{ widget.attrs.id }}"
|
|
30
|
+
hx-target="#sb-admin-modal"
|
|
31
|
+
hx-select="#modal-content"
|
|
32
|
+
hx-swap="innerHTML">
|
|
33
|
+
<svg class="w-20 h-20">
|
|
34
|
+
<use xlink:href="#Add-one"></use>
|
|
35
|
+
</svg>
|
|
36
|
+
</a>
|
|
37
|
+
{% endif %}
|
|
38
|
+
</div>
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
{% include 'sb_admin/widgets/includes/field_label.html' %}
|
|
1
2
|
{% with id=widget.attrs.id %}
|
|
2
3
|
<ul{% if id %} id="{{ id }}"{% endif %}>
|
|
3
4
|
{% for group, options, index in widget.optgroups %}
|
|
4
5
|
{% for option in options %}
|
|
5
|
-
<li>
|
|
6
|
+
<li class="relative py-8">
|
|
6
7
|
{% include option.template_name with widget=option %}
|
|
7
8
|
</li>
|
|
8
9
|
{% endfor %}
|
|
9
10
|
{% endfor %}
|
|
10
11
|
</ul>
|
|
11
12
|
{% endwith %}
|
|
12
|
-
|
|
13
|
+
{% include 'sb_admin/widgets/includes/help_text.html' %}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
|
|
3
|
+
<div class="relative">
|
|
4
|
+
{% include 'sb_admin/widgets/includes/simple_field_label.html' with label_classes="radio-dropdown-label inline-flex text-dark-900 mb-8 text-14 leading-18 font-medium" %}
|
|
5
|
+
<div id="{{ widget.attrs.id }}-wrapper" class="js-simple-multiselect-detail js-dropdown-wrapper">
|
|
6
|
+
<button
|
|
7
|
+
data-bs-toggle="dropdown"
|
|
8
|
+
aria-expanded="false"
|
|
9
|
+
data-bs-offset="[0, 8]"
|
|
10
|
+
class="btn px-10 font-normal w-full"
|
|
11
|
+
>
|
|
12
|
+
<span id="{{ widget.attrs.id }}-value" class="js-dropdown-label flex gap-8">{{ widget.form_field.empty_label }}</span>
|
|
13
|
+
<svg class="ml-8 no-rotate text-dark">
|
|
14
|
+
<use xlink:href="#Sort Alt"></use>
|
|
15
|
+
</svg>
|
|
16
|
+
</button>
|
|
17
|
+
<div id="{{ widget.attrs.id }}-dropdown" class="dropdown-menu">
|
|
18
|
+
<ul id="{{ widget.attrs.id }}" class="relative py-8">
|
|
19
|
+
{% for group, options, index in widget.optgroups %}
|
|
20
|
+
{% for option in options %}
|
|
21
|
+
<li>
|
|
22
|
+
{% include option.template_name with widget=option %}
|
|
23
|
+
</li>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
{% endfor %}
|
|
26
|
+
</ul>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
{% include 'sb_admin/widgets/includes/help_text.html' %}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
{% include 'sb_admin/widgets/includes/field_label.html' %}
|
|
2
2
|
<div {% include "sb_admin/widgets/attrs.html" %}>
|
|
3
3
|
<p class="text-14 leading-18 read-only-password-hash">{{ widget.form_field.help_text|safe }}</p>
|
|
4
|
+
{% if button_label %}
|
|
5
|
+
<p class="mt-8"><a class="btn btn-small" href="{{ password_url|default:"../password/" }}">{{ button_label }}</a></p>
|
|
6
|
+
{% endif %}
|
|
4
7
|
</div>
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
{% include 'sb_admin/widgets/includes/field_label.html' %}
|
|
2
|
-
|
|
2
|
+
<div class="relative flex time-wrapper">
|
|
3
|
+
{% include "sb_admin/widgets/input.html" %}
|
|
4
|
+
<label class="absolute right-10 top-0 bottom-0 h-full flex items-center pointer-events-none" for="{{ widget.attrs.id }}">
|
|
5
|
+
<svg class="w-20 h-20">
|
|
6
|
+
<use xlink:href="#Time"></use>
|
|
7
|
+
</svg>
|
|
8
|
+
</label>
|
|
9
|
+
</div>
|
|
3
10
|
{% include 'sb_admin/widgets/includes/help_text.html' %}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{% load static sb_admin_tags i18n %}
|
|
2
|
+
|
|
3
|
+
{% if not hide_tree_search %}
|
|
4
|
+
<div class="border-b border-dark-200 {{ search_wrapper_classes }}">
|
|
5
|
+
<div class="js-tree-widget-search relative">
|
|
6
|
+
{% trans 'Search' as search_string %}
|
|
7
|
+
<input id="{{ tree_component_id }}_search" name="search" placeholder="{{ tree_filter_placeholder|default:search_string }}" autocomplete="off" class="input pl-36" type="search">
|
|
8
|
+
<div class="absolute pl-10 left-0 top-0 bottom-0 flex items-center gap-8 rounded-r">
|
|
9
|
+
<svg class="w-20 h-20">
|
|
10
|
+
<use xlink:href="#Search"></use>
|
|
11
|
+
</svg>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
{% endif %}
|
|
16
|
+
<div class="{{ table_wrapper_classes }}">
|
|
17
|
+
<table style="width: 100%;" id="{{ tree_component_id }}_tree" class="js-tree-widget fancytree-ext-table" data-tree-data-id="{{ tree_component_id }}_data" data-tree-additional-columns-id="{{ tree_component_id }}_additional_columns" data-tree-strings-id="{{ tree_component_id }}_tree_strings">
|
|
18
|
+
<thead>
|
|
19
|
+
<tr>
|
|
20
|
+
{% if tree_show_checkbox %}
|
|
21
|
+
<th class="select-all-checkbox-wrapper"></th>
|
|
22
|
+
{% endif %}
|
|
23
|
+
<th class="text-left">{{ tree_main_column_name }}<span id="{{ tree_component_id }}_matches" class="ml-4"></span></th>
|
|
24
|
+
{% for column in tree_additional_columns %}
|
|
25
|
+
<th>{{ column.title }}</th>
|
|
26
|
+
{% endfor %}
|
|
27
|
+
</tr>
|
|
28
|
+
</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
<tr>
|
|
31
|
+
{% if tree_show_checkbox %}
|
|
32
|
+
<td class="alignCenter fancytree-checkbox-column"></td>{% endif %}
|
|
33
|
+
<td></td>
|
|
34
|
+
{% for column in tree_additional_columns %}
|
|
35
|
+
<td></td>
|
|
36
|
+
{% endfor %}
|
|
37
|
+
</tr>
|
|
38
|
+
</tbody>
|
|
39
|
+
</table>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<script id="{{ tree_component_id }}_data" type="application/json">
|
|
43
|
+
{
|
|
44
|
+
"filter": {% if tree_filter %}true{% else %}false{% endif %},
|
|
45
|
+
"input_id": "{{ tree_component_id }}",
|
|
46
|
+
"data_url": "{{ tree_data_url }}",
|
|
47
|
+
"checkbox": {% if tree_show_checkbox %}true{% else %}false{% endif %},
|
|
48
|
+
"multiselect": {% if tree_multiselect %}2{% else %}1{% endif %},
|
|
49
|
+
"detail_url":"{{ tree_detail_url }}",
|
|
50
|
+
"reorder_url":"{{ tree_reorder_url }}",
|
|
51
|
+
"filter_by_table_data": {% if filter_by_table_data %}true{% else %}false{% endif %},
|
|
52
|
+
"allow_select_all": {% if allow_select_all %}true{% else %}false{% endif %}
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
{{ fancytree_filter_settings|get_json_script:'fancytree_filter_settings' }}
|
|
56
|
+
<script id="{{ tree_component_id }}_additional_columns" type="application/json">{{ tree_additional_columns|get_json|safe }}</script>
|
|
57
|
+
<script id="{{ tree_component_id }}_tree_strings" type="application/json">
|
|
58
|
+
{{ tree_strings|get_json|safe }}
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{% load sb_admin_tags %}
|
|
2
|
+
|
|
3
|
+
<div class="relative">
|
|
4
|
+
{% include 'sb_admin/widgets/includes/field_label.html' %}
|
|
5
|
+
<div id="{{ widget.attrs.id }}-wrapper" class="tree-widget-wrapper">
|
|
6
|
+
<input type="hidden" id="{{ widget.attrs.id }}" name="{{ widget.name }}"{% if widget.raw_value %} value="{{ widget.raw_value|get_json }}"{% endif %}>
|
|
7
|
+
<button
|
|
8
|
+
data-bs-toggle="dropdown"
|
|
9
|
+
aria-expanded="false"
|
|
10
|
+
data-bs-offset="[0, 8]"
|
|
11
|
+
class="btn px-10 font-normal w-full"
|
|
12
|
+
>
|
|
13
|
+
<span id="{{ widget.attrs.id }}_label">{% get_item widget.value_dict widget.raw_value widget.form_field.empty_label %}</span>
|
|
14
|
+
<svg class="ml-8">
|
|
15
|
+
<use xlink:href="#Down"></use>
|
|
16
|
+
</svg>
|
|
17
|
+
</button>
|
|
18
|
+
<div class="dropdown-menu max-h-none w-248">
|
|
19
|
+
<div class="py-8">
|
|
20
|
+
{% include "sb_admin/widgets/tree_base.html" with tree_additional_columns=widget.additional_columns tree_strings=widget.tree_strings tree_value=widget.raw_value tree_multiselect=filter_widget.multiselect tree_component_id=widget.attrs.id tree_data_url=filter_widget.to_json.autocomplete_url tree_show_checkbox=True table_wrapper_classes="max-h-432 overflow-auto custom-scrollbar" %}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{% load sb_admin_tags %}
|
|
2
|
+
|
|
3
|
+
<div class="relative -mb-2">
|
|
4
|
+
<script>
|
|
5
|
+
document.currentScript.closest('.djn-table').querySelector('.djn-thead').classList.add('hidden');
|
|
6
|
+
</script>
|
|
7
|
+
<div id="{{ widget.attrs.id }}-wrapper" class="tree-widget-wrapper">
|
|
8
|
+
<input type="hidden" id="{{ widget.attrs.id }}" name="{{ widget.name }}"{% if widget.raw_value %}
|
|
9
|
+
value="{{ widget.raw_value|get_json }}"{% endif %}>
|
|
10
|
+
{% include "sb_admin/widgets/tree_base.html" with tree_main_column_name=filter_widget.form_field.label tree_strings=widget.tree_strings tree_additional_columns=widget.additional_columns tree_value=widget.raw_value tree_multiselect=filter_widget.multiselect tree_component_id=widget.attrs.id tree_data_url=filter_widget.to_json.autocomplete_url tree_show_checkbox=True table_wrapper_classes="max-h-432 overflow-auto custom-scrollbar" search_wrapper_classes="pb-16 px-16" allow_select_all=True %}
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
from django import template
|
|
4
5
|
from django.contrib.admin.templatetags.admin_modify import submit_row
|
|
6
|
+
from django.contrib.admin.utils import lookup_field
|
|
7
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
5
8
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
6
9
|
from django.template.defaultfilters import json_script
|
|
10
|
+
from django.templatetags.static import static
|
|
7
11
|
from django.utils.safestring import mark_safe
|
|
8
12
|
from django.utils.text import get_text_list
|
|
9
13
|
from django.utils.translation import gettext
|
|
14
|
+
from easy_thumbnails.exceptions import InvalidImageFormatError
|
|
10
15
|
|
|
16
|
+
from django_smartbase_admin.engine.const import (
|
|
17
|
+
ROW_CLASS_FIELD,
|
|
18
|
+
SUPPORTED_FILE_TYPE_ICONS,
|
|
19
|
+
)
|
|
11
20
|
from django_smartbase_admin.templatetags.base import InclusionSBAdminNode
|
|
12
21
|
|
|
13
22
|
register = template.Library()
|
|
@@ -78,7 +87,7 @@ def sb_admin_render_form_field(context, form_field, label_as_placeholder=False):
|
|
|
78
87
|
@register.simple_tag
|
|
79
88
|
def get_tabular_context(fieldsets, inlines, tabs):
|
|
80
89
|
default_tabs = False
|
|
81
|
-
|
|
90
|
+
any_error = False
|
|
82
91
|
first_error_tab = True
|
|
83
92
|
tabular_context = {}
|
|
84
93
|
inlines_map = {inline.opts.__class__: inline for inline in inlines}
|
|
@@ -90,6 +99,7 @@ def get_tabular_context(fieldsets, inlines, tabs):
|
|
|
90
99
|
default_tabs = True
|
|
91
100
|
for key, values in tabs.items():
|
|
92
101
|
for value in values:
|
|
102
|
+
has_error = False
|
|
93
103
|
fieldset_value = fieldsets_map.get(value)
|
|
94
104
|
inline_value = inlines_map.get(value)
|
|
95
105
|
tabular_context[key] = tabular_context.get(
|
|
@@ -99,7 +109,15 @@ def get_tabular_context(fieldsets, inlines, tabs):
|
|
|
99
109
|
tabular_context[key]["content"].append(
|
|
100
110
|
{"type": "fieldset", "value": fieldset_value}
|
|
101
111
|
)
|
|
102
|
-
|
|
112
|
+
fieldset_fields = set()
|
|
113
|
+
for field in fieldset_value.fields:
|
|
114
|
+
if isinstance(field, (list, tuple)):
|
|
115
|
+
fieldset_fields.update(set(field))
|
|
116
|
+
else:
|
|
117
|
+
fieldset_fields.add(field)
|
|
118
|
+
error_present = bool(
|
|
119
|
+
set(fieldset_value.form.errors.keys()).intersection(fieldset_fields)
|
|
120
|
+
)
|
|
103
121
|
has_error = has_error or error_present
|
|
104
122
|
tabular_context[key]["error"] = (
|
|
105
123
|
tabular_context[key]["error"] or error_present
|
|
@@ -114,18 +132,19 @@ def get_tabular_context(fieldsets, inlines, tabs):
|
|
|
114
132
|
tabular_context[key]["error"] or error_present
|
|
115
133
|
)
|
|
116
134
|
if has_error:
|
|
135
|
+
any_error = True
|
|
117
136
|
tabular_context[key]["classes"].add("error")
|
|
118
137
|
if first_error_tab:
|
|
119
138
|
tabular_context[key]["classes"].update(["active", "show"])
|
|
120
139
|
first_error_tab = False
|
|
121
|
-
if not
|
|
140
|
+
if not any_error:
|
|
122
141
|
tabular_context[list(tabular_context.keys())[0]]["classes"].update(
|
|
123
142
|
["active", "show"]
|
|
124
143
|
)
|
|
125
144
|
return {
|
|
126
145
|
"context": tabular_context,
|
|
127
146
|
"default_tabs": default_tabs,
|
|
128
|
-
"has_error":
|
|
147
|
+
"has_error": any_error,
|
|
129
148
|
}
|
|
130
149
|
|
|
131
150
|
|
|
@@ -202,3 +221,65 @@ def get_log_entry_message(log_entry):
|
|
|
202
221
|
return get_change_message_legacy(log_entry)
|
|
203
222
|
except Exception as e:
|
|
204
223
|
return ""
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@register.simple_tag
|
|
227
|
+
def call_method(obj, method_name, *args):
|
|
228
|
+
method = getattr(obj, method_name, None)
|
|
229
|
+
if method:
|
|
230
|
+
return method(*args)
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@register.simple_tag
|
|
235
|
+
def get_row_class(inline_admin_form):
|
|
236
|
+
try:
|
|
237
|
+
f, attr, value = lookup_field(
|
|
238
|
+
ROW_CLASS_FIELD, inline_admin_form.original, inline_admin_form.model_admin
|
|
239
|
+
)
|
|
240
|
+
except (AttributeError, ValueError, ObjectDoesNotExist):
|
|
241
|
+
return ""
|
|
242
|
+
return value
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@register.filter
|
|
246
|
+
def is_row_class_field(field):
|
|
247
|
+
return isinstance(field, dict) and field["name"] == ROW_CLASS_FIELD
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@register.simple_tag
|
|
251
|
+
def get_file_extension(file):
|
|
252
|
+
if not file:
|
|
253
|
+
return ""
|
|
254
|
+
name, extension = os.path.splitext(file.name)
|
|
255
|
+
return extension.lower().replace(".", "")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@register.simple_tag
|
|
259
|
+
def get_file_preview_image(
|
|
260
|
+
file, file_extension=None, thumbnail_width=64, thumbnail_height=64
|
|
261
|
+
):
|
|
262
|
+
file_extension = file_extension or get_file_extension(file)
|
|
263
|
+
if file_extension in ["jpg", "jpeg", "png"]:
|
|
264
|
+
from easy_thumbnails.files import get_thumbnailer
|
|
265
|
+
|
|
266
|
+
thumbnailer = get_thumbnailer(file)
|
|
267
|
+
try:
|
|
268
|
+
thumb = thumbnailer.get_thumbnail(
|
|
269
|
+
{
|
|
270
|
+
"size": (thumbnail_width, thumbnail_height),
|
|
271
|
+
"crop": True,
|
|
272
|
+
"replace_alpha": "#fff",
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
return thumb.url
|
|
276
|
+
except ValueError:
|
|
277
|
+
return file.url
|
|
278
|
+
except InvalidImageFormatError:
|
|
279
|
+
return static("sb_admin/images/file_types/file-other.svg")
|
|
280
|
+
if file_extension == "svg" or file_extension == "webp":
|
|
281
|
+
return file.url
|
|
282
|
+
for extension in SUPPORTED_FILE_TYPE_ICONS:
|
|
283
|
+
if file_extension == extension:
|
|
284
|
+
return static(f"sb_admin/images/file_types/file-{extension}.svg")
|
|
285
|
+
return static("sb_admin/images/file_types/file-other.svg")
|
django_smartbase_admin/utils.py
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
|
|
1
4
|
from django import forms
|
|
2
5
|
from django.conf import settings
|
|
3
6
|
from django.contrib.admin.helpers import Fieldset
|
|
4
7
|
from django.template.loader import render_to_string
|
|
5
8
|
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
6
11
|
|
|
7
12
|
class JSONSerializableMixin(object):
|
|
8
13
|
def to_json(self):
|
|
@@ -35,15 +40,29 @@ def render_notifications(request):
|
|
|
35
40
|
return render_to_string("sb_admin/includes/notifications.html", request=request)
|
|
36
41
|
|
|
37
42
|
|
|
43
|
+
def is_modal(request):
|
|
44
|
+
from django_smartbase_admin.engine.admin_base_view import SBADMIN_IS_MODAL_VAR
|
|
45
|
+
|
|
46
|
+
return request and (
|
|
47
|
+
SBADMIN_IS_MODAL_VAR in request.GET or SBADMIN_IS_MODAL_VAR in request.POST
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
38
51
|
class FormFieldsetMixin(forms.Form):
|
|
39
|
-
def
|
|
52
|
+
def get_fieldsets(self) -> Iterable[tuple[str | None, dict]]:
|
|
40
53
|
meta = getattr(self, "Meta", None)
|
|
54
|
+
return getattr(meta, "fieldsets", tuple())
|
|
41
55
|
|
|
42
|
-
|
|
56
|
+
def fieldsets(self) -> Iterable[Fieldset]:
|
|
57
|
+
if not (fieldsets := self.get_fieldsets()):
|
|
58
|
+
logger.warning(
|
|
59
|
+
"No fieldsets defined for form %s. Using form fields as fallback.",
|
|
60
|
+
self.__class__.__name__,
|
|
61
|
+
)
|
|
43
62
|
yield Fieldset(form=self, fields=self.fields)
|
|
44
63
|
return
|
|
45
64
|
|
|
46
|
-
for name, data in
|
|
65
|
+
for name, data in fieldsets:
|
|
47
66
|
yield Fieldset(
|
|
48
67
|
form=self,
|
|
49
68
|
name=name,
|
|
@@ -11,6 +11,7 @@ class SBAdminDashboardView(SBAdminView):
|
|
|
11
11
|
menu_action = Action.DASHBOARD.value
|
|
12
12
|
widgets = None
|
|
13
13
|
title = None
|
|
14
|
+
direct_sub_views = None
|
|
14
15
|
|
|
15
16
|
def __init__(self, title=None, widgets=None) -> None:
|
|
16
17
|
super().__init__()
|
|
@@ -27,16 +28,21 @@ class SBAdminDashboardView(SBAdminView):
|
|
|
27
28
|
view.init_view_dynamic(request, request_data, **kwargs)
|
|
28
29
|
|
|
29
30
|
def get_sub_views(self, configuration):
|
|
31
|
+
self.direct_sub_views = []
|
|
30
32
|
self.sub_views = []
|
|
31
33
|
for idx, widget_view in enumerate(self.widgets):
|
|
32
34
|
widget_view.widget_id = f"{self.get_id()}_{idx}"
|
|
33
35
|
widget_view.init_widget_static(configuration)
|
|
36
|
+
widget_view_sub_views = widget_view.get_sub_views(configuration) or []
|
|
34
37
|
self.sub_views.append(widget_view)
|
|
38
|
+
self.direct_sub_views.append(widget_view)
|
|
39
|
+
self.sub_views.extend(widget_view_sub_views)
|
|
35
40
|
return self.sub_views
|
|
36
41
|
|
|
37
42
|
def dashboard(self, request, modifier):
|
|
38
43
|
context = self.get_global_context(request)
|
|
39
44
|
context["sub_views"] = self.sub_views
|
|
45
|
+
context["direct_sub_views"] = self.direct_sub_views
|
|
40
46
|
context["title"] = self.get_title()
|
|
41
47
|
return TemplateResponse(
|
|
42
48
|
request,
|
|
@@ -4,7 +4,10 @@ from django.http import HttpResponse
|
|
|
4
4
|
from django.shortcuts import redirect
|
|
5
5
|
from django.views import View
|
|
6
6
|
|
|
7
|
-
from django_smartbase_admin.engine.const import
|
|
7
|
+
from django_smartbase_admin.engine.const import (
|
|
8
|
+
GLOBAL_FILTER_DATA_KEY,
|
|
9
|
+
TABLE_RELOAD_DATA_EVENT_NAME,
|
|
10
|
+
)
|
|
8
11
|
from django_smartbase_admin.utils import is_htmx_request, querydict_to_dict
|
|
9
12
|
|
|
10
13
|
|
|
@@ -13,10 +16,13 @@ class GlobalFilterView(View):
|
|
|
13
16
|
response = redirect(request.headers.get("referer", ""))
|
|
14
17
|
if is_htmx_request(request.META):
|
|
15
18
|
response = HttpResponse()
|
|
16
|
-
response["HX-Trigger"] = json.dumps({
|
|
19
|
+
response["HX-Trigger"] = json.dumps({TABLE_RELOAD_DATA_EVENT_NAME: ""})
|
|
17
20
|
new_global_filter_data = querydict_to_dict(request.POST)
|
|
18
21
|
request.request_data.global_filter = new_global_filter_data
|
|
19
22
|
request.request_data.configuration.init_global_filter_form_instance(request)
|
|
20
23
|
if request.request_data.global_filter_instance.is_valid():
|
|
21
24
|
request.session[GLOBAL_FILTER_DATA_KEY] = new_global_filter_data
|
|
25
|
+
response = request.request_data.configuration.process_global_filter_response(
|
|
26
|
+
response, request
|
|
27
|
+
)
|
|
22
28
|
return response
|
|
@@ -9,6 +9,7 @@ from django.forms import modelform_factory
|
|
|
9
9
|
from django.http import HttpResponse, HttpResponseRedirect
|
|
10
10
|
from django.template.response import TemplateResponse
|
|
11
11
|
from django.utils.html import format_html
|
|
12
|
+
from django.utils.safestring import mark_safe
|
|
12
13
|
from django.utils.translation import gettext_lazy as _
|
|
13
14
|
|
|
14
15
|
from django_smartbase_admin.actions.admin_action_list import SBAdminListAction
|
|
@@ -125,7 +126,7 @@ class ModelTranslationView(SBAdminView, SBAdminBaseListView):
|
|
|
125
126
|
lang_codes.extend(selected_lang_codes)
|
|
126
127
|
return lang_codes
|
|
127
128
|
|
|
128
|
-
def init_fields_cache(self, fields_source=None, configuration=None):
|
|
129
|
+
def init_fields_cache(self, fields_source=None, configuration=None, force=False):
|
|
129
130
|
return
|
|
130
131
|
|
|
131
132
|
def get_field_map(self, request):
|
|
@@ -161,7 +162,7 @@ class ModelTranslationView(SBAdminView, SBAdminBaseListView):
|
|
|
161
162
|
annotate=Case(
|
|
162
163
|
When(
|
|
163
164
|
**{
|
|
164
|
-
f"{annotate_name}
|
|
165
|
+
f"{annotate_name}_count__gte": F(
|
|
165
166
|
f"{main_lang_annotate_name}_count"
|
|
166
167
|
),
|
|
167
168
|
"then": Value(self.TRANSLATION_TRANSLATED),
|
|
@@ -207,11 +208,17 @@ class ModelTranslationView(SBAdminView, SBAdminBaseListView):
|
|
|
207
208
|
|
|
208
209
|
def format_translation_status(self, object_id, value):
|
|
209
210
|
if value == self.TRANSLATION_NOT_TRANSLATED:
|
|
210
|
-
return
|
|
211
|
+
return mark_safe(
|
|
212
|
+
f'<span class="badge badge-simple badge-neutral"><svg class="w-16 h-16 mr-4 text-invisible"><use xlink:href="#Add-one"></use></svg>{_("Not Translated")}</span>'
|
|
213
|
+
)
|
|
211
214
|
if value == self.TRANSLATION_INCOMPLETE:
|
|
212
|
-
return
|
|
215
|
+
return mark_safe(
|
|
216
|
+
f'<span class="badge badge-simple badge-warning"><svg class="w-16 h-16 mr-4 text-warning"><use xlink:href="#Attention"></use></svg>{_("Incomplete")}</span>'
|
|
217
|
+
)
|
|
213
218
|
if value == self.TRANSLATION_TRANSLATED:
|
|
214
|
-
return
|
|
219
|
+
return mark_safe(
|
|
220
|
+
f'<span class="badge badge-simple badge-positive"><svg class="w-16 h-16 mr-4 text-success"><use xlink:href="#Check"></use></svg>{_("Translated")}</span>'
|
|
221
|
+
)
|
|
215
222
|
|
|
216
223
|
def get_translated_fields(self):
|
|
217
224
|
return SBAdminTranslationsService.get_translated_fields_for_model(self.model)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.http import HttpResponse
|
|
3
|
+
from django.utils.html import format_html
|
|
4
|
+
from django.views.generic import FormView
|
|
5
|
+
|
|
6
|
+
from django_smartbase_admin.admin.admin_base import SBAdminBaseFormInit
|
|
7
|
+
from django_smartbase_admin.admin.widgets import (
|
|
8
|
+
SBAdminRadioDropdownWidget,
|
|
9
|
+
)
|
|
10
|
+
from django_smartbase_admin.models import SBAdminUserConfiguration, ColorScheme
|
|
11
|
+
from django_smartbase_admin.services.configuration import (
|
|
12
|
+
SBAdminUserConfigurationService,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ColorSchemeForm(SBAdminBaseFormInit, forms.ModelForm):
|
|
17
|
+
color_scheme_icons = {
|
|
18
|
+
ColorScheme.AUTO.value: "Translation",
|
|
19
|
+
ColorScheme.DARK.value: "Moon",
|
|
20
|
+
ColorScheme.LIGHT.value: "Sun-one",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class Meta:
|
|
24
|
+
model = SBAdminUserConfiguration
|
|
25
|
+
fields = ("color_scheme",)
|
|
26
|
+
widgets = {
|
|
27
|
+
"color_scheme": SBAdminRadioDropdownWidget(),
|
|
28
|
+
}
|
|
29
|
+
required = []
|
|
30
|
+
|
|
31
|
+
def __init__(self, *args, **kwargs):
|
|
32
|
+
super().__init__(*args, **kwargs)
|
|
33
|
+
choices_formatted = []
|
|
34
|
+
for choice in self.fields["color_scheme"].choices:
|
|
35
|
+
choice_label = format_html(
|
|
36
|
+
f'<span class="flex gap-8"><svg class="w-20 h-20"><use href="#{self.color_scheme_icons.get(choice[0])}"></use></svg><span>{choice[1]}</span></span>'
|
|
37
|
+
)
|
|
38
|
+
choices_formatted.append((choice[0], choice_label))
|
|
39
|
+
self.fields["color_scheme"].choices = choices_formatted
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ColorSchemeView(FormView):
|
|
43
|
+
form_class = ColorSchemeForm
|
|
44
|
+
|
|
45
|
+
def form_valid(self, form):
|
|
46
|
+
instance = form.save(commit=False)
|
|
47
|
+
sb_admin_user_config = SBAdminUserConfigurationService.get_user_config(
|
|
48
|
+
self.request
|
|
49
|
+
)
|
|
50
|
+
sb_admin_user_config.color_scheme = instance.color_scheme
|
|
51
|
+
sb_admin_user_config.save(update_fields=["color_scheme"])
|
|
52
|
+
return HttpResponse(status=200)
|