accrete 0.0.149__py3-none-any.whl → 0.0.151__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. accrete/contrib/log/queries.py +3 -1
  2. accrete/contrib/ui/admin.py +9 -1
  3. accrete/contrib/ui/forms.py +57 -0
  4. accrete/contrib/ui/migrations/0001_initial.py +39 -0
  5. accrete/contrib/ui/migrations/0002_alter_theme_color_danger_alter_theme_color_link_and_more.py +38 -0
  6. accrete/contrib/ui/migrations/0003_alter_theme_check_user_or_tenant.py +21 -0
  7. accrete/contrib/ui/migrations/0004_theme_force_tenant_theme.py +18 -0
  8. accrete/contrib/ui/models.py +115 -1
  9. accrete/contrib/ui/response.py +14 -5
  10. accrete/contrib/ui/static/css/accrete.css +23 -57
  11. accrete/contrib/ui/static/css/accrete.css.map +1 -1
  12. accrete/contrib/ui/static/css/accrete.scss +76 -55
  13. accrete/contrib/ui/templates/django/forms/widgets/input.html +1 -1
  14. accrete/contrib/ui/templates/ui/custom_theme.html +19 -0
  15. accrete/contrib/ui/templates/ui/layout.html +9 -9
  16. accrete/contrib/ui/templates/ui/list.html +2 -2
  17. accrete/contrib/ui/templates/ui/message.html +2 -2
  18. accrete/contrib/ui/templates/ui/modal.html +3 -3
  19. accrete/contrib/ui/templates/ui/table.html +5 -5
  20. accrete/contrib/ui/templates/ui/templatetags/field.html +50 -11
  21. accrete/contrib/ui/templates/ui/widgets/date_weekday.html +10 -0
  22. accrete/contrib/ui/templates/ui/widgets/model_search_select.html +4 -3
  23. accrete/contrib/ui/templates/ui/widgets/model_search_select_multi.html +5 -4
  24. accrete/contrib/ui/templatetags/ui.py +37 -5
  25. accrete/contrib/ui/views.py +90 -2
  26. accrete/contrib/ui/widgets/__init__.py +1 -0
  27. accrete/contrib/ui/widgets/date_weekday.py +6 -0
  28. accrete/contrib/ui/widgets/search_select.py +2 -2
  29. accrete/contrib/user/forms.py +1 -1
  30. accrete/contrib/user/migrations/0009_alter_user_theme.py +18 -0
  31. accrete/contrib/user/migrations/0010_alter_user_theme.py +18 -0
  32. accrete/contrib/user/models.py +5 -3
  33. accrete/contrib/user/templates/user/login.html +3 -11
  34. accrete/contrib/user/templates/user/user_preferences.html +27 -15
  35. accrete/contrib/user/views.py +7 -2
  36. accrete/fields.py +4 -2
  37. accrete/managers.py +9 -0
  38. accrete/migrations/0009_alter_accessgroup_name.py +30 -0
  39. accrete/models.py +6 -4
  40. accrete/views.py +32 -20
  41. {accrete-0.0.149.dist-info → accrete-0.0.151.dist-info}/METADATA +1 -1
  42. {accrete-0.0.149.dist-info → accrete-0.0.151.dist-info}/RECORD +44 -33
  43. {accrete-0.0.149.dist-info → accrete-0.0.151.dist-info}/WHEEL +0 -0
  44. {accrete-0.0.149.dist-info → accrete-0.0.151.dist-info}/licenses/LICENSE +0 -0
@@ -1,34 +1,33 @@
1
- @import "../bulma/css/bulma.css";
1
+ @import "../bulma/css/bulma.min.css";
2
2
 
3
3
  :root {
4
4
  --bulma-body-size: .9em;
5
5
  --bulma-navbar-height: 40px;
6
- --bulma-menu-item-selected-h: --bulma-success-h;
7
- --accrete-detail-width: 500px;
6
+ --bulma-menu-item-selected-h: var(--bulma-primary-h);
7
+ --accrete-detail-width: 35em;
8
+ --bulma-primary-h: 153.28deg;
9
+ --bulma-primary-s: 52.89%;
10
+ --bulma-primary-l: 52.55%;
8
11
  --accrete-action-panel-width: 320px;
9
- --bulma-input-icon-focus-color: var(--bulma-success);
10
- --bulma-input-arrow: var(--bulma-success);
11
- --bulma-arrow-color: var(--bulma-success);
12
- --bulma-menu-nested-list-margin: .5em 0 0.5em 0.75em;
13
- --bulma-navbar-burger-color: var(--bulma-success);
12
+ --bulma-input-arrow: var(--bulma-primary);
13
+ --bulma-arrow-color: var(--bulma-primary);
14
14
  --accrete-hover-color: #F0F2F4;
15
-
16
15
  }
17
16
 
18
17
  html[data-theme='light'] {
19
- --bulma-menu-item-h: var(--bulma-success-h);
20
- --bulma-menu-item-s: var(--bulma-success-s);
21
- --bulma-menu-item-l: var(--bulma-success-l);
22
- --bulma-menu-item-background-l: 50%;
23
- --bulma-menu-item-color-l: 10%;
24
- --bulma-navbar-burger-color: var(--bulma-success);
25
- --bulma-input-disabled-border-color: var(--bulma-input-border-color) !important;
26
- --accrete-hover-color: #F0F2F4;
18
+ //--bulma-menu-item-h: var(--bulma-success-h);
19
+ //--bulma-menu-item-s: var(--bulma-success-s);
20
+ //--bulma-menu-item-l: var(--bulma-success-l);
21
+ //--bulma-menu-item-background-l: 50%;
22
+ //--bulma-menu-item-color-l: 10%;
23
+ //--bulma-navbar-burger-color: var(--bulma-success);
24
+ //--bulma-input-disabled-border-color: var(--bulma-input-border-color) !important;
25
+ //--accrete-hover-color: #F0F2F4;
27
26
  }
28
27
 
29
28
  html[data-theme='dark'] {
30
- --bulma-navbar-burger-color: var(--bulma-success);
31
- --bulma-input-disabled-border-color: var(--bulma-input-border-color) !important;
29
+ //--bulma-navbar-burger-color: var(--bulma-success);
30
+ //--bulma-input-disabled-border-color: var(--bulma-input-border-color) !important;
32
31
  --accrete-hover-color: #1E2128;
33
32
 
34
33
  .button.is-light {
@@ -78,21 +77,22 @@ a {
78
77
  border-left-color: transparent;
79
78
  }
80
79
 
81
- .input {
82
- border-top: none;
83
- border-right: none;
84
- border-left: none;
85
- border-top-left-radius: 0;
86
- border-top-right-radius: 0;
87
- border-bottom-right-radius: 0;
88
- border-bottom-left-radius: 0;
89
- box-shadow: none;
90
- font-weight: bold;
91
- }
80
+ //.input {
81
+ // border-top: none;
82
+ // border-right: none;
83
+ // border-left: none;
84
+ // border-top-left-radius: 0;
85
+ // border-top-right-radius: 0;
86
+ // border-bottom-right-radius: 0;
87
+ // border-bottom-left-radius: 0;
88
+ // box-shadow: none;
89
+ // //box-shadow: inset 0 -10px 10px -16px #000000;
90
+ // font-weight: bold;
91
+ //}
92
92
 
93
93
  .input:focus, .input:focus-within {
94
94
  box-shadow: none;
95
- border-bottom: 1px solid var(--bulma-success);
95
+ border: 1px solid var(--bulma-primary);
96
96
  //border-color: var(--bulma-success);
97
97
  }
98
98
 
@@ -110,7 +110,7 @@ textarea {
110
110
  }
111
111
 
112
112
  textarea:focus {
113
- border-color: var(--bulma-success) !important;
113
+ border-color: var(--bulma-primary) !important;
114
114
  }
115
115
 
116
116
  .is-small.textarea, .is-small.input {
@@ -118,29 +118,52 @@ textarea:focus {
118
118
  font-size: var(--bulma-size-small);
119
119
  }
120
120
 
121
+ td:first-child > .input {
122
+ //border-top-left-radius: var(--bulma-radius);
123
+ //border-bottom-left-radius: var(--bulma-radius);
124
+
125
+ //border-right: 0;
126
+ //border-top: none!important;
127
+ //border-right: none!important;
128
+ //border-bottom: none!important;
129
+ //border-left: none!important;
130
+ }
131
+
132
+ td:last-child > .input {
133
+ //border-top-right-radius: var(--bulma-radius);
134
+ //border-bottom-right-radius: var(--bulma-radius);
135
+
136
+ //border-right: 0;
137
+ //border-top: none!important;
138
+ //border-right: none!important;
139
+ //border-bottom: none!important;
140
+ //border-left: none!important;
141
+ }
142
+
121
143
  td > .input, td * .input, td * select, td * .select {
122
- border-top: none!important;
123
- border-right: none!important;
124
- border-bottom: none!important;
125
- border-left: none!important;
144
+ //border-top: none!important;
145
+ //border-right: none!important;
146
+ //border-bottom: none!important;
147
+ //border-left: none!important;
148
+ border-radius: 0;
126
149
  }
127
150
 
128
151
  td:has(.input:focus) {
129
- border-bottom: 1px solid var(--bulma-success)!important;
152
+ //border-bottom: 1px solid var(--bulma-primary)!important;
130
153
  }
131
154
 
132
155
  select {
133
- border-top: transparent !important;
134
- border-right: transparent !important;
135
- border-left: transparent !important;
136
- border-radius: 0 !important;
156
+ //border-top: transparent !important;
157
+ //border-right: transparent !important;
158
+ //border-left: transparent !important;
159
+ //border-radius: 0 !important;
137
160
  box-shadow: none !important;
138
- font-weight: bold;
161
+ //font-weight: bold;
139
162
  }
140
163
 
141
164
  select:focus {
142
165
  box-shadow: none;
143
- border-bottom: 1px solid var(--bulma-success)!important;
166
+ border: 1px solid var(--bulma-primary)!important;
144
167
  }
145
168
 
146
169
  #detail-container {
@@ -195,7 +218,7 @@ select:focus {
195
218
  }
196
219
 
197
220
  .list-entry > .box:hover, .box.selected {
198
- box-shadow: 0 0 5px 2px var(--bulma-success);
221
+ box-shadow: 0 0 5px 2px var(--bulma-primary);
199
222
  }
200
223
 
201
224
  .helptext {
@@ -234,12 +257,12 @@ select:focus {
234
257
  border-top: 1px solid var(--bulma-grey-light);
235
258
  }
236
259
 
237
- table.can-compact * tr.is-success {
238
- background-color: var(--bulma-success);
260
+ table.can-compact * tr.is-primary {
261
+ background-color: var(--bulma-primary);
239
262
  }
240
263
 
241
- table.can-compact * tr.is-success:hover {
242
- background-color: var(--bulma-success)!important;
264
+ table.can-compact * tr.is-primary:hover {
265
+ background-color: var(--bulma-primary)!important;
243
266
  }
244
267
 
245
268
  table.can-compact > thead {
@@ -258,8 +281,6 @@ select:focus {
258
281
  @media screen and (min-width: 769px) {
259
282
  .modal-card {
260
283
  max-width: 100%;
261
- //min-width: var(--bulma-modal-content-width);
262
- //width: unset;
263
284
  }
264
285
  }
265
286
 
@@ -305,11 +326,11 @@ select:focus {
305
326
  }
306
327
 
307
328
  .menu-list a.is-active, .menu-list a.is-selected, .menu-list button.is-active, .menu-list button.is-selected, .menu-list .menu-item.is-active, .menu-list .menu-item.is-selected {
308
- --bulma-menu-item-h: var(--bulma-success-h);
309
- --bulma-menu-item-s: var(--bulma-success-s);
310
- --bulma-menu-item-l: var(--bulma-success-l);
311
- --bulma-menu-item-background-l: var(--bulma-success-l);
312
- --bulma-menu-item-color-l: var(--bulma-success-invert-l);
329
+ --bulma-menu-item-h: var(--bulma-primary-h);
330
+ --bulma-menu-item-s: var(--bulma-primary-s);
331
+ --bulma-menu-item-l: var(--bulma-primary-l);
332
+ --bulma-menu-item-background-l: var(--bulma-primary-l);
333
+ --bulma-menu-item-color-l: var(--bulma-primary-invert-l);
313
334
  }
314
335
 
315
336
  .hoverable *:hover {
@@ -1 +1 @@
1
- <input class="{% if widget.type != 'checkbox' %}input{% endif %}" type="{{ widget.type }}" name="{{ widget.name }}" autocomplete="off" {% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}>
1
+ <input class="{% if widget.type != 'checkbox' and widget.type != 'radio' %}input{% endif %}" type="{{ widget.type }}" name="{{ widget.name }}" autocomplete="off" {% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}>
@@ -0,0 +1,19 @@
1
+ <style>
2
+ :root {
3
+ --bulma-primary-h: {{ primary.h }}deg;
4
+ --bulma-primary-s: {{ primary.s }}%;
5
+ --bulma-primary-l: {{ primary.l }}%;
6
+ --bulma-success-h: {{ success.h }}deg;
7
+ --bulma-success-s: {{ success.s }}%;
8
+ --bulma-success-l: {{ success.l }}%;
9
+ --bulma-link-h: {{ link.h }}deg;
10
+ --bulma-link-s: {{ link.s }}%;
11
+ --bulma-link-l: {{ link.l }}%;
12
+ --bulma-warning-h: {{ warning.h }}deg;
13
+ --bulma-warning-s: {{ warning.s }}%;
14
+ --bulma-warning-l: {{ warning.l }}%;
15
+ --bulma-danger-h: {{ danger.h }}deg;
16
+ --bulma-danger-s: {{ danger.s }}%;
17
+ --bulma-danger-l: {{ danger.l }}%;
18
+ }
19
+ </style>
@@ -4,17 +4,17 @@
4
4
  {% load partials %}
5
5
  {% load ui %}
6
6
 
7
- <html lang="en" style="overflow: hidden" {% block theme %}{% if request.user.theme %}data-theme="{{ request.user.theme }}"{% endif %}{% endblock %}>
7
+ <html lang="en" style="overflow: hidden;" {% block theme %}{% if request.user.theme %}data-theme="{{ request.user.theme }}"{% endif %}{% endblock %}>
8
8
 
9
9
  <head>
10
10
  {% block head %}
11
11
  <meta charset="utf-8">
12
12
  <meta name="viewport" content="width=device-width, initial-scale=1">
13
13
  {% include 'ui/favicon.html' %}
14
- <link rel="stylesheet" type="text/css" href="{% static "css/accrete.css" %}?v=0.0.144">
14
+ <link rel="stylesheet" type="text/css" href="{% static "css/accrete.css" %}?v=0.0.151">
15
15
  <link rel="stylesheet" type="text/css" href="{% static "css/icons.css" %}">
16
16
  <link rel="stylesheet" type="text/css" href="{% static "css/fa.css" %}">
17
-
17
+ {% custom_theme request.user %}
18
18
  <script src="{% static "js/filter.js" %}" type="text/javascript"></script>
19
19
  <script src="{% static "js/htmx.min.js" %}" defer type="text/javascript"></script>
20
20
  <script src="{% static "js/alpine-focus-3.14.9.js" %}" defer type="text/javascript"></script>
@@ -51,7 +51,7 @@
51
51
  <body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' data-tenant-id="{{ tenant.id }}" hx-boost="true" hx-history="false"
52
52
  x-data="{ showPanel: false, openPanel() { if(window.innerWidth >= 1216) {this.showPanel = true} } }"
53
53
  >
54
- <nav id="navbar" class="navbar is-success is-fixed-top" role="navigation" aria-label="main navigation">
54
+ <nav id="navbar" class="navbar is-primary is-fixed-top" role="navigation" aria-label="main navigation">
55
55
  <div class="navbar-brand">
56
56
  {% if has_panel %}
57
57
  <div class="navbar-item is-hidden-widescreen pr-2" x-on:click.stop="$dispatch('toggle-panel')">
@@ -131,7 +131,7 @@
131
131
  <div id="message" class="" style="position: fixed; top: calc(var(--bulma-navbar-height) + 5px); left: 50%; transform: translateX(-50%); z-index: 999">
132
132
  {% for message in messages %}
133
133
  <div class="mb-2" style="min-width: 330px; max-width: 330px" x-data="{ show: false }" x-show="show" x-cloak="" x-init="show = true; setTimeout(() => show = false, 4000)" x-transition.duration.200ms>
134
- <div class="notification is-light has-text-centered {{ message|message_class }}" style="box-shadow: 2px 2px 5px">
134
+ <div class="notification is-light has-text-centered" style="box-shadow: 2px 2px 5px">
135
135
  {{ message }}
136
136
  </div>
137
137
  </div>
@@ -148,9 +148,9 @@
148
148
  <div id="overview-container" class="table-container is-flex is-flex-grow-1 is-flex-direction-column mb-0" x-bind:class="showContentRight ? 'is-hidden-mobile' : ''">
149
149
  <div id="overview-header">
150
150
  <div class="is-flex is-justify-content-space-between is-flex-wrap-wrap {% if is_centered %}container{% endif %}">
151
- <div class="is-flex is-flex-wrap-wrap is-flex-grow-5 is-align-self-center py-2 header-items">
151
+ <div class="is-flex is-flex-wrap-wrap is-flex-grow-5 is-align-self-center py-2 mx-1 header-items">
152
152
  {% block overview_header %}
153
- <div class="ml-0" style="align-self: center">
153
+ <div class="" style="align-self: center">
154
154
  <p class="title is-5">{{ title }}</p>
155
155
  </div>
156
156
  {% if header_template %}{% include header_template %}{% endif %}
@@ -197,9 +197,9 @@
197
197
  </section>
198
198
  </div>
199
199
  </div>
200
-
200
+
201
201
  <div id="detail-container" x-show="showContentRight" {% if show_content_right != 'true' %}style="display: none;"{% endif %}
202
- x-transition:enter.duration.200ms x-transition:leave.duration.50ms
202
+ x-transition:enter.duration.200ms x-transition:leave.duration.50ms
203
203
  >
204
204
  <div id="detail-indicator" class="htmx-indicator">
205
205
  <progress class="progress is-success" max="100"></progress>
@@ -8,8 +8,8 @@
8
8
  {% partialdef entry inline=True %}
9
9
  <div id="list-entry-{{ instance.pk }}" class="list-entry cell pb-0" style="height: {{ column_height }}">
10
10
  <div class="box p-3"
11
- style="word-break: break-word; height: 100%; overflow-y: auto; {% if instance.get_absolute_url %}cursor:pointer;{% endif %}"
12
- {% if instance.get_absolute_url %}
11
+ style="word-break: break-word; height: 100%; overflow-y: auto; {% if instance.get_absolute_url and detail_enabled %}cursor:pointer;{% endif %}"
12
+ {% if instance.get_absolute_url and detail_enabled %}
13
13
  hx-get="{{ instance.get_absolute_url }}{% querystring %}" hx-swap="none" hx-indicator="#detail-indicator" hx-replace-url="{% querystring detail=instance.pk %}"
14
14
  x-on:click="$dispatch('unselect-list-entry'); selected = true; $nextTick(() => { $el.scrollIntoView({block: 'nearest'}) });" x-bind:class="selected ? 'selected' : ''"
15
15
  {% endif %}
@@ -4,8 +4,8 @@
4
4
  {% for message in messages %}
5
5
  <div hx-swap-oob="{% if append %}beforeend:#message{% else %}innerHTML:#message{% endif %}">
6
6
  <div class="mb-2" style="min-width: 340px; max-width: 340px" x-data="{ show: false }" x-show="show" x-cloak="" x-init="show = true; {% if not persistent %}setTimeout(() => show = false, 4000){% endif %}" x-transition.duration.200ms>
7
- <div class="notification is-light has-text-centered {{ message|message_class }}" style="box-shadow: 2px 2px 5px">
8
- {% if persistent %}<button class="delete" x-on:click="show = false;"></button>{% endif %}
7
+ <div class="notification has-text-centered " style="box-shadow: 2px 2px 5px">
8
+ <button class="delete" x-on:click="show = false;"></button>
9
9
  {{ message }}
10
10
  </div>
11
11
  </div>
@@ -1,13 +1,13 @@
1
1
  {% load i18n %}
2
2
  {% load ui %}
3
3
 
4
- <div hx-swap-oob="{% if is_update %}outerHTML:#{{ modal_id }}{% else %}beforeend:body{% endif %}">
5
- <div id="{{ modal_id }}" x-data="{showModal: false, close() {$dispatch('close-modal-{{ modal_id }}')} }" x-ref="modal" class="modal is-active is-justify-content-flex-start"
4
+ <div hx-swap-oob="{% if is_update %}innerHTML:#{{ modal_id }}{% else %}beforeend:body{% endif %}">
5
+ <div id="{{ modal_id }}" x-data="{showModal: false, close() {$dispatch('close-modal-{{ modal_id }}')} }" x-ref="modal" x-trap="true" class="modal is-active is-justify-content-flex-start"
6
6
  style="padding-top: calc(var(--bulma-navbar-height) + 20px); padding-bottom: 40px;"
7
7
  @close-modal-{{ modal_id }}.window="showModal = false; setTimeout(() => $refs.modal.remove(), 200);"
8
8
  @close-modals.window="showModal = false; setTimeout(() => $refs.modal.remove(), 200);"
9
9
  {% if not is_blocking %}@keyup.escape.window="if (! $refs.{{ modal_id|xrefsave }}Indicator.classList.contains('htmx-request')) {$dispatch('close-modal-{{ modal_id }}')}"{% endif %}
10
- {% if is_blocking %}{% endif %}hx-indicator="#{{ modal_id }}-indicator" hx-disabled-elt="#{{ modal_id }}-background"
10
+ hx-indicator="#{{ modal_id }}-indicator" hx-disabled-elt="#{{ modal_id }}-background"
11
11
  >
12
12
  <button id="{{ modal_id }}-background" class="modal-background" {% if not is_blocking %}@click="$dispatch('close-modal-{{ modal_id }}')"{% endif %} style="cursor: default"></button>
13
13
 
@@ -5,7 +5,7 @@
5
5
  <table id="content-table" class="table can-compact is-fullwidth is-hoverable is-narrow my-0" hx-indicator=".htmx-indicator" x-data="">
6
6
  <thead style="position: sticky; top: 0; z-index: 10; background-color: var(--bulma-scheme-main)">
7
7
  <tr>
8
- <th>{{ instance_label }}</th>
8
+ {% if instance_label %}<th>{{ instance_label }}</th>{% endif %}
9
9
  {% for field in fields %}
10
10
  <th style="text-align: {{ page.object_list|table_alignment:field }}">{{ page.object_list|verbose_field_name:field }}</th>
11
11
  {% endfor %}
@@ -20,14 +20,14 @@
20
20
  hx-get="{{ instance.get_absolute_url }}{% querystring %}" hx-swap="none"
21
21
  hx-replace-url="{% querystring detail=instance.pk %}"
22
22
  x-data="{selected: false}" @unselect-tr.window="selected = false"
23
- x-on:click="$dispatch('unselect-tr'); selected = true; $nextTick(() => { $el.scrollIntoView( {block: 'nearest'} ) });" x-bind:class="selected ? 'is-success' : ''"
23
+ x-on:click="$dispatch('unselect-tr'); selected = true; $nextTick(() => { $el.scrollIntoView( {block: 'nearest'} ) });" x-bind:class="selected ? 'is-primary' : ''"
24
24
  {% endif %}
25
25
  >
26
26
  {% partialdef td inline=True %}
27
- <td>{{ instance }}</td>
27
+ {% if instance_label %}<td>{{ instance }}</td>{% endif %}
28
28
  {% for field in fields %}
29
29
  <td style="text-align: {{ instance|table_alignment:field }}">
30
- <span class="">
30
+ <span>
31
31
  <span class="responsive-heading has-text-weight-light" style="margin-right: .2rem">{{ instance|verbose_field_name:field }}:</span>
32
32
  {{ instance|table_display:field|default_if_none:'' }}
33
33
  </span>
@@ -72,4 +72,4 @@
72
72
  {% endif %}
73
73
  >
74
74
  </div>
75
- <progress id="endless-scroll-indicator" class="htmx-indicator progress is-small is-success" max="100">15%</progress>
75
+ <progress id="endless-scroll-indicator" class="htmx-indicator progress is-small is-primary" max="100">15%</progress>
@@ -1,27 +1,66 @@
1
1
  {% load ui %}
2
2
  {% load partials %}
3
3
 
4
+ {#{% partialdef form_field %}#}
5
+ {# <label class="label mt-2 mb-5">#}
6
+ {# {{ field.label }}#}
7
+ {# {{ field }}#}
8
+ {# <span class="has-text-danger is-size-6">{{ field.errors }}</span>#}
9
+ {# <span class="helptext is-size-7">{{ field.help_text }}</span>#}
10
+ {# <span class="helptext is-size-7">{% if label %}{{ label }}{% else %}{{ field.help_text|default_if_falsy:field.label }}{% endif %}</span>#}
11
+ {# </label>#}
12
+ {#{% endpartialdef %}#}
13
+
14
+ {#{% partialdef form_field %}#}
15
+ {# <label class="label mt-2 mb-5" style="border: 1px solid; border-radius: var(--bulma-radius);">#}
16
+ {# <span style="justify-content: left" class="is-fullwidth button is-static helptext is-size-7 {% if field.field.required %}has-text-weight-bold{% endif %}">{% if label %}{{ label }}{% else %}{{ field.help_text|default_if_falsy:field.label }}{% endif %}</span>#}
17
+ {# {{ field }}#}
18
+ {# <span class="has-text-danger is-size-6">{{ field.errors }}Testmessage</span>#}
19
+ {##}
20
+ {# </label>#}
21
+ {#{% endpartialdef %}#}
22
+
4
23
  {% partialdef form_field %}
5
- <label class="label mt-2 mb-5">
6
- {{ field }}
24
+ <div class="mb-5">
25
+ <label class="label mb-0 {% if field.field.required %}has-text-weight-medium{% endif %}" for="{{ field.id_for_label }}">{{ field.label }}</label>
26
+ <div class="field has-addons mb-0">
27
+ {% if icon %}
28
+ <p class="control">
29
+ <button tabindex="-1" type="button" class="button is-static"><span class="icon"><i class="{{ icon }}"></i></span></button>
30
+ </p>
31
+ {% endif %}
32
+ <div class="control is-expanded">{{ field }}</div>
33
+ </div>
7
34
  <span class="has-text-danger is-size-6">{{ field.errors }}</span>
8
- <span class="helptext {% if field.field.required %}has-text-weight-bold{% endif %}">{% if label %}{{ label }}{% else %}{{ field.help_text|default_if_falsy:field.label }}{% endif %}</span>
9
- </label>
35
+ <span class="helptext is-size-7">{{ field.help_text|default_if_falsy:'' }}</span>
36
+ </div>
10
37
  {% endpartialdef %}
11
38
 
12
39
  {% partialdef textarea %}
13
40
  <label class="label mt-2 mb-5">
14
- <span class="helptext {% if field.field.required %}has-text-weight-bold{% endif %}">{% if label %}{{ label }}{% else %}{{ field.help_text|default_if_falsy:field.label }}{% endif %}</span>
41
+ <span class="{% if field.field.required %}has-text-weight-normal{% endif %}">{{ field.label }}</span>
15
42
  <span class="has-text-danger is-size-6">{{ field.errors }}</span>
16
43
  {{ field }}
17
44
  </label>
18
45
  {% endpartialdef %}
19
46
 
20
47
  {% partialdef model_field %}
21
- <label class="label mt-2 mb-5">
22
- <span class="field has-addons mb-0">
23
- <span class="control input has-text-weight-normal">{{ value|default_if_none:'' }}</span>
24
- </span>
25
- <span class="helptext {% if required %}has-text-weight-bold{% endif %}">{{ label }}</span>
26
- </label>
48
+ {# <label class="label mt-2 mb-5">#}
49
+ {# <span class="field has-addons mb-0">#}
50
+ {# <span class="control input has-text-weight-bold">{{ value|default_if_none:'' }}</span>#}
51
+ {# </span>#}
52
+ {# <span class="helptext">{{ label }}</span>#}
53
+ {# </label>#}
54
+ <div class="mb-5">
55
+ <label class="label mb-0 {% if field.field.required %}has-text-weight-bold{% endif %}" for="{{ field.id_for_label }}">{{ label }}</label>
56
+ <div class="field has-addons mb-0">
57
+ {% if icon %}
58
+ <p class="control">
59
+ <button tabindex="-1" type="button" class="button is-static"><span class="icon"><i class="{{ icon }}"></i></span></button>
60
+ </p>
61
+ {% endif %}
62
+ <div class="control input is-expanded">{{ value|default_if_none:'' }}</div>
63
+ </div>
64
+ <span class="helptext is-size-7">{{ field.help_text|default_if_falsy:'' }}</span>
65
+ </div>
27
66
  {% endpartialdef %}
@@ -0,0 +1,10 @@
1
+ {% load ui %}
2
+
3
+ <div class="field has-addons mb-0">
4
+ <p class="control">
5
+ <p class="control"><a class="button is-static" style="height: 100%; border-top-left-radius: var(--bulma-radius-medium); border-bottom-left-radius: var(--bulma-radius-medium);">{{ widget.value|weekday:'' }}</a></p>
6
+ </p>
7
+ <p class="control is-expanded">
8
+ <input class="{% if widget.type != 'checkbox' %}input{% endif %} is-fullwidth" name="{{ widget.name }}" type="{{ widget.type }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %} {% include "django/forms/widgets/attrs.html" %}>
9
+ </p>
10
+ </div>
@@ -11,12 +11,13 @@
11
11
  >
12
12
  <input id="id_{{ widget.name }}_display" class="input" type="text" readonly x-ref="inputDisplay" value="{{ widget.value_display }}" aria-label="inputDisplay"
13
13
  style="padding-right: 30px"
14
+ {% include "django/forms/widgets/attrs.html" %}
14
15
  x-on:keydown="if (!open && [38, 40].includes($event.keyCode)) {}; if (![9, 27, 38, 40].includes($event.keyCode)) {$refs.searchInput.focus();}"
15
16
  x-on:click.stop="if (open) {open = false; } else {open = true; $refs.searchInput.focus()}"
16
17
  >
17
- <div style="position: relative; top: 1.5rem; z-index: 90;">
18
- <div x-show="open" x-effect="if (open) {$nextTick(() => { $refs.dropdownContent.scrollIntoView(true) });}" x-cloak="" class="box pt-1 px-3 mb-2" x-ref="dropdownContent" tabindex="-1" x-transition style="position: absolute; width: 100%">
19
- <input id="id_{{ widget.name }}_search" type="search" autocomplete="off" class="input mb-2" x-ref="searchInput" aria-label="search"
18
+ <div style="position: relative; top: .5rem; z-index: 90;">
19
+ <div x-show="open" x-effect="if (open) {$nextTick(() => { $refs.dropdownContent.scrollIntoView(true) });}" x-cloak="" class="box pt-3 px-3 mb-2" x-ref="dropdownContent" tabindex="-1" x-transition style="position: absolute; width: 100%">
20
+ <input id="id_{{ widget.name }}_search" type="search" autocomplete="off" class="input my-2" x-ref="searchInput" aria-label="search"
20
21
  name="{{ widget.search_parameter }}"
21
22
  placeholder="{% translate 'Type to search' %}"
22
23
  hx-get="{{ widget.search_url }}"
@@ -10,8 +10,8 @@
10
10
  <option selected value="{{ obj.pk }}"></option>
11
11
  {% endfor %}
12
12
  </select>
13
- <div id="{{ widget.attrs.id }}_display" class="input select py-1 pl-1" tabindex="0" x-ref="inputDisplay" aria-label="inputDisplay"
14
- style="padding-right: 30px; border-radius: 0; border-top: 0; border-right:0; border-left: 0; min-height: var(--bulma-control-height); height: fit-content; width: 100%"
13
+ <div id="{{ widget.attrs.id }}_display" class="input select py-1" tabindex="0" x-ref="inputDisplay" aria-label="inputDisplay"
14
+ style="padding-right: 30px; min-height: var(--bulma-control-height); height: fit-content; width: 100%"
15
15
  x-on:keydown="if (![9, 27, 38, 40].includes($event.keyCode)) {$refs.searchInput.focus();}"
16
16
  x-on:click.stop="if (!$event.target.classList.contains('delete')) {if (open) {open = false; } else {open = true; $refs.searchInput.focus();}}"
17
17
  >
@@ -33,8 +33,8 @@
33
33
  {% endfor %}
34
34
  </div>
35
35
  </div>
36
- <div style="position: relative; top: 1.5rem; z-index: 9">
37
- <div x-show="open" x-effect="if (open) {$nextTick(() => { $refs.dropdownContent.scrollIntoView(true) });}" class="box pt-1 px-3 mb-2" x-ref="dropdownContent" x-cloak="" tabindex="-1" x-transition style="position: absolute; width: 100%">
36
+ <div style="position: relative; top: .5rem; z-index: 9">
37
+ <div x-show="open" x-effect="if (open) {$nextTick(() => { $refs.dropdownContent.scrollIntoView(true) });}" class="box pt-3 px-3 mb-2" x-ref="dropdownContent" x-cloak="" tabindex="-1" x-transition style="position: absolute; width: 100%">
38
38
  <input id="id_{{ widget.uuid }}_search" type="search" autocomplete="off" class="input mb-2" x-ref="searchInput" aria-label="search"
39
39
  name="{{ widget.search_parameter }}"
40
40
  placeholder="{% translate 'Type to search' %}"
@@ -42,6 +42,7 @@
42
42
  hx-trigger="input changed delay:300ms, search"
43
43
  hx-target="#id_{{ widget.uuid }}_dropdown_items"
44
44
  hx-swap="innerHTML"
45
+ style="border-radius: var(--bulma-radius)"
45
46
  x-on:keydown="if (![9, 27, 38, 40].includes($event.keyCode)) {$refs.searchInput.focus();}"
46
47
  @change.stop=""
47
48
  >
@@ -2,16 +2,22 @@ import logging
2
2
  import re
3
3
  from datetime import datetime, date, timedelta
4
4
 
5
+ from django.contrib.auth import get_user_model
5
6
  from django.utils.translation import gettext_lazy as _
6
7
  from django import template
7
- from django.db.models import Manager, DecimalField, IntegerField, FloatField, Model
8
+ from django.db.models import (
9
+ Manager, DecimalField, IntegerField, FloatField, Model, Q
10
+ )
8
11
  from django.apps import apps
9
12
  from django.template.loader import render_to_string
10
13
  from django.utils.safestring import mark_safe
11
14
  from django.forms import widgets
15
+ from accrete.contrib.ui.models import Theme
16
+ from accrete.tenant import get_tenant
12
17
 
13
18
  _logger = logging.getLogger(__name__)
14
19
  register = template.Library()
20
+ User = get_user_model()
15
21
 
16
22
 
17
23
  @register.simple_tag(name='combine_templates')
@@ -130,9 +136,11 @@ def timedelta_cast(td: timedelta, code: str) -> str | None:
130
136
 
131
137
 
132
138
  @register.filter(name='weekday')
133
- def datetime_to_weekday(dt: datetime|date, default=None) -> str:
139
+ def datetime_to_weekday(dt: datetime|date|str, default=None) -> str:
134
140
  if dt is None:
135
141
  return default
142
+ if isinstance(dt, str):
143
+ dt = datetime.strptime(dt, '%Y-%m-%d')
136
144
  mapping = {
137
145
  1: _('Mon'),
138
146
  2: _('Tue'),
@@ -151,11 +159,17 @@ def x_ref_save(param: str):
151
159
 
152
160
 
153
161
  @register.filter(name='wrap_form_field')
154
- def wrap_form_fields(field, label=None):
162
+ def wrap_form_fields(field, icon=None):
155
163
  if isinstance(field.field.widget, widgets.Textarea):
156
- html = render_to_string('ui/templatetags/field.html#textarea', {'field': field, 'label': label})
164
+ html = render_to_string(
165
+ 'ui/templatetags/field.html#textarea',
166
+ {'field': field, 'icon': None}
167
+ )
157
168
  return mark_safe(html)
158
- html = render_to_string('ui/templatetags/field.html#form_field', {'field': field, 'label': label})
169
+ html = render_to_string(
170
+ 'ui/templatetags/field.html#form_field',
171
+ {'field': field, 'icon': icon}
172
+ )
159
173
  return mark_safe(html)
160
174
 
161
175
 
@@ -179,3 +193,21 @@ def default_if_falsy(value, default):
179
193
  if bool(value):
180
194
  return value
181
195
  return default
196
+
197
+
198
+ @register.simple_tag(name='custom_theme')
199
+ def custom_theme(user: User) -> str:
200
+ if user.is_anonymous:
201
+ return ''
202
+ tenant = get_tenant()
203
+ tenant_theme = Theme.objects.filter(
204
+ tenant=tenant, tenant__isnull=False
205
+ ).first()
206
+ if tenant_theme and tenant_theme.force_tenant_theme:
207
+ return mark_safe(tenant_theme.theme_markup)
208
+ if user.theme == 'custom':
209
+ theme = Theme.objects.filter(user=user).first()
210
+ return theme and mark_safe(theme.theme_markup) or ''
211
+ if user.theme == 'preset' and tenant_theme:
212
+ return mark_safe(tenant_theme.theme_markup)
213
+ return ''