accrete 0.0.35__py3-none-any.whl → 0.0.37__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 (31) hide show
  1. accrete/annotation.py +46 -0
  2. accrete/contrib/ui/__init__.py +12 -3
  3. accrete/contrib/ui/context.py +184 -257
  4. accrete/contrib/ui/elements.py +78 -2
  5. accrete/contrib/ui/filter.py +110 -44
  6. accrete/contrib/ui/static/css/accrete.css +128 -128
  7. accrete/contrib/ui/static/css/accrete.css.map +1 -1
  8. accrete/contrib/ui/static/css/accrete.scss +9 -9
  9. accrete/contrib/ui/static/js/filter.js +24 -0
  10. accrete/contrib/ui/static/js/htmx.min.js +1 -0
  11. accrete/contrib/ui/templates/ui/layout.html +134 -129
  12. accrete/contrib/ui/templates/ui/list.html +3 -3
  13. accrete/contrib/ui/templates/ui/partials/filter.html +29 -5
  14. accrete/contrib/ui/templates/ui/partials/header.html +7 -7
  15. accrete/contrib/ui/templates/ui/partials/pagination_detail.html +3 -3
  16. accrete/contrib/ui/templates/ui/partials/pagination_list.html +4 -4
  17. accrete/contrib/ui/templates/ui/table.html +18 -13
  18. accrete/contrib/ui/templatetags/accrete_ui.py +12 -1
  19. accrete/contrib/user/forms.py +0 -4
  20. accrete/contrib/user/templates/user/login.html +6 -12
  21. accrete/contrib/user/views.py +31 -21
  22. accrete/middleware.py +15 -0
  23. accrete/models.py +9 -7
  24. accrete/querystring.py +11 -8
  25. accrete/utils/models.py +14 -0
  26. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/METADATA +1 -1
  27. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/RECORD +29 -28
  28. accrete/contrib/ui/components.py +0 -96
  29. accrete/contrib/ui/querystring.py +0 -19
  30. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/WHEEL +0 -0
  31. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/licenses/LICENSE +0 -0
@@ -15,159 +15,164 @@
15
15
  <link rel="stylesheet" type="text/css" href="{% static "css/icons.css" %}">
16
16
  {% endblock %}
17
17
  {% block htmx %}
18
- <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
18
+ <script src="{% static "js/htmx.min.js" %}" defer type="text/javascript"></script>
19
19
  {% endblock %}
20
20
  {% block script %}{% endblock %}
21
- <title>{% block title %}{{ title }}{% endblock %}</title>
21
+ {% block title_tag %}<title>{% block title %}{{ title }}{% endblock %}</title>{% endblock %}
22
22
  {% endblock %}
23
23
  </head>
24
24
 
25
25
 
26
- <body {% block body_attrs %}hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'{% endblock %}>
27
- {% block navbar %}
28
- <nav id="navbar" class="navbar is-success is-fixed-top" role="navigation" aria-label="main navigation">
29
- <div class="navbar-brand">
30
- {% block navbar_brand %}
31
- <div class="navbar-item is-unselectable">{{ request.tenant.name }}</div>
32
- {% endblock %}
33
- <a id="navbar-burger" role="button" class="navbar-burger"
34
- aria-label="menu" aria-expanded="false"
35
- onclick="this.classList.toggle('is-active'); document.getElementById('navbar-menu').classList.toggle('is-active');"
36
- >
37
- {# Display hamburger menu on mobile #}
38
- <span aria-hidden="true"></span>
39
- <span aria-hidden="true"></span>
40
- <span aria-hidden="true"></span>
41
- </a>
42
- </div>
43
-
44
- <div id="navbar-menu" class="navbar-menu is-fixed-top">
45
- <div class="navbar-start">
46
- {% block navbar_start %}
47
- {% combine_templates 'accrete_menu.html' %}
26
+ <body {% block body_attrs %}hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' hx-boost="true" hx-history="false" {% endblock %}>
27
+ {% block body %}
28
+ {% block navbar %}
29
+ <nav id="navbar" class="navbar is-success is-fixed-top" role="navigation" aria-label="main navigation">
30
+ <div class="navbar-brand">
31
+ {% block navbar_brand %}
32
+ <div class="navbar-item is-unselectable">{{ request.tenant.name }}</div>
48
33
  {% endblock %}
34
+ <a id="navbar-burger" role="button" class="navbar-burger"
35
+ aria-label="menu" aria-expanded="false"
36
+ onclick="this.classList.toggle('is-active'); document.getElementById('navbar-menu').classList.toggle('is-active');"
37
+ >
38
+ {# Display hamburger menu on mobile #}
39
+ <span aria-hidden="true"></span>
40
+ <span aria-hidden="true"></span>
41
+ <span aria-hidden="true"></span>
42
+ </a>
49
43
  </div>
50
44
 
51
- <div class="navbar-end">
52
- {% block navbar_end %}
53
- <div class="navbar-item has-dropdown is-hoverable" onclick="this.classList.toggle('is-active');">
54
- <a class="navbar-link is-arrowless {% if request.member.name %}is-size-7 has-text-centered-desktop{% endif %}">
55
- {% if request.member.name %}
56
- {{ request.member }}<br>{{ user }}
57
- {% else %}
58
- {{ user }}
59
- {% endif %}
60
- </a>
61
- <div id="navbar-end-dropdown" class="navbar-dropdown is-right">
62
- {% combine_templates 'accrete_navbar_end_dropdown.html' request=request %}
45
+ <div id="navbar-menu" class="navbar-menu is-fixed-top">
46
+ <div class="navbar-start">
47
+ {% block navbar_start %}
48
+ {% combine_templates 'accrete_menu.html' %}
49
+ {% endblock %}
50
+ </div>
51
+
52
+ <div class="navbar-end">
53
+ {% block navbar_end %}
54
+ <div class="navbar-item has-dropdown is-hoverable" onclick="this.classList.toggle('is-active');">
55
+ <a class="navbar-link is-arrowless {% if request.member.name %}is-size-7 has-text-centered-desktop{% endif %}">
56
+ {% if request.member.name %}
57
+ {{ request.member }}<br>{{ user }}
58
+ {% else %}
59
+ {{ user }}
60
+ {% endif %}
61
+ </a>
62
+ <div id="navbar-end-dropdown" class="navbar-dropdown is-right">
63
+ {% combine_templates 'accrete_navbar_end_dropdown.html' request=request %}
64
+ </div>
63
65
  </div>
64
- </div>
65
- {% endblock %}
66
+ {% endblock %}
67
+ </div>
66
68
  </div>
67
- </div>
68
- </nav>
69
- {% endblock %}
69
+ </nav>
70
+ {% endblock %}
70
71
 
71
- <div id="main" class="is-flex is-flex-direction-row">
72
- <div class="side-panel is-hidden-touch is-hidden-desktop-only is-hidden-widescreen-only p-0">
73
- <nav class="panel is-shadowless pb-5" style="height: 100%; overflow-y: auto; position: sticky; top: 0">
74
- {% if list_pagination %}
75
- <div class="panel-block pb-0">
76
- {% include 'ui/partials/pagination_list.html' %}
77
- </div>
78
- {% endif %}
79
- {% if detail_pagination %}
80
- <div class="panel-block pb-0">
81
- {% include 'ui/partials/pagination_detail.html' %}
82
- </div>
83
- {% endif %}
84
- <div id="panel-actions">
85
- {% for action in actions %}
72
+ <div id="main" class="is-flex is-flex-direction-row">
73
+ <div class="side-panel is-hidden-touch is-hidden-desktop-only is-hidden-widescreen-only p-0">
74
+ <nav class="panel is-shadowless pb-5" style="height: 100%; overflow-y: auto; position: sticky; top: 0">
75
+ {% if list_page.paginator %}
86
76
  <div class="panel-block pb-0">
87
- {% if action.submit %}
88
- <button class="button is-fullwidth {{ action.class_list|join:' ' }}"
89
- type="submit" form={{ action.form_id }} value="{{ action.name }}"
90
- {{ action.attrs_str }} style="padding-right: 0.7em;"
91
- >
92
- <div class="side-panel-action-content"><span>{{ action.name }}</span>
93
- {% if action.icon %}<i class="{{ action.icon.value }}"></i>{% endif %}
94
- </div>
95
- </button>
96
- {% else %}
97
- <a class="button is-fullwidth {{ action.class_list|join:' ' }}"
98
- {% if action.url %}
99
- {{ action.method.value }}="{{ action.url }}{% if action.add_url_params %}{{ url_params|default_if_none:'?' }}{% else %}?{% endif %}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
100
- {% endif %}
101
- {{ action.attrs_str }} style="padding-right: 0.7em;"
102
- >
103
- <div class="side-panel-action-content">
104
- <span>{{ action.name }}</span>
105
- {% if action.icon %}<i class="{{ action.icon.value }}"></i>{% endif %}
106
- </div>
107
- </a>
108
- {% endif %}
77
+ {% include 'ui/partials/pagination_list.html' %}
109
78
  </div>
110
- {% endfor %}
111
- </div>
112
- {% block filter %}
113
- {% if filter %}
114
- <div id="filter-panel" class="mt-2">
115
- <div class="panel-list mx-3 mb-1 has-text-centered" style="border-bottom: 1px #ccc solid">
116
- <span>{% translate 'Filter' %}</span>
117
- </div>
118
- {% include 'ui/partials/filter.html' %}
79
+ {% endif %}
80
+ {% if detail_page %}
81
+ <div class="panel-block pb-0">
82
+ {% include 'ui/partials/pagination_detail.html' %}
119
83
  </div>
120
84
  {% endif %}
121
- {% endblock %}
122
- </nav>
123
- </div>
85
+ <div id="panel-actions">
86
+ {% for action in actions %}
87
+ <div class="panel-block pb-0">
88
+ {% if action.submit %}
89
+ <button class="button is-fullwidth {{ action.class_list|join:' ' }}"
90
+ type="submit" form={{ action.form_id }} value="{{ action.name }}"
91
+ {{ action.attrs_str }} style="padding-right: 0.7em;"
92
+ >
93
+ <div class="side-panel-action-content"><span>{{ action.name }}</span>
94
+ {% if action.icon %}<i class="{{ action.icon.value }}"></i>{% endif %}
95
+ </div>
96
+ </button>
97
+ {% else %}
98
+ <a class="button is-fullwidth {{ action.class_list|join:' ' }}"
99
+ {% if action.url %}
100
+ {{ action.method.value }}="{{ action.url }}"
101
+ {% endif %}
102
+ {{ action.attrs_str }} style="padding-right: 0.7em;"
103
+ >
104
+ <div class="side-panel-action-content">
105
+ <span>{{ action.name }}</span>
106
+ {% if action.icon %}<i class="{{ action.icon.value }}"></i>{% endif %}
107
+ </div>
108
+ </a>
109
+ {% endif %}
110
+ </div>
111
+ {% endfor %}
112
+ </div>
113
+ {% block filter %}
114
+ {% if filter %}
115
+ <div id="filter-panel" class="mt-2">
116
+ <div class="panel-list mx-3 mb-1 has-text-centered" style="border-bottom: 1px #ccc solid">
117
+ <span>{% translate 'Filter' %}</span>
118
+ </div>
119
+ {% include 'ui/partials/filter.html' %}
120
+ </div>
121
+ {% endif %}
122
+ {% endblock %}
123
+ </nav>
124
+ </div>
124
125
 
125
- <div class="is-flex-grow-1" style="overflow-x: auto">
126
- <div class="is-flex is-flex-direction-column" style="height: 100%; overflow: hidden">
127
- <div>
128
- {% include 'ui/partials/header.html' %}
129
- </div>
130
- <div id="content" class="px-3 pb-4" style="overflow: auto; flex-grow: 1">
131
- {% block messages %}{% endblock %}
132
- {% block content %}{% endblock %}
133
- </div>
134
- <div>
135
- {% if list_pagination or detail_pagination %}
136
- <div class="level is-hidden-tablet is-align-self-flex-start m-3">
137
- <div class="level-item is-align-content-flex-start">
138
- {% if list_pagination %}
126
+ <div class="is-flex-grow-1" style="overflow-x: auto">
127
+ <div class="is-flex is-flex-direction-column" style="height: 100%; overflow: hidden">
128
+ <div>
129
+ {% include 'ui/partials/header.html' %}
130
+ </div>
131
+ <div id="content" class="px-3 pb-4" style="overflow: auto; flex-grow: 1">
132
+ {% block messages %}{% endblock %}
133
+ {% block content %}{% endblock %}
134
+ </div>
135
+ <div>
136
+ {% if list_page.paginator %}
137
+ <div class="level is-hidden-tablet is-align-self-flex-start m-3">
138
+ <div class="level-item is-align-content-flex-start">
139
139
  {% include 'ui/partials/pagination_list.html' %}
140
- {% elif detail_pagination %}
140
+ </div>
141
+ </div>
142
+ {% endif %}
143
+ {% if detail_page %}
144
+ <div class="level is-hidden-tablet is-align-self-flex-start m-3">
145
+ <div class="level-item is-align-content-flex-start">
141
146
  {% include 'ui/partials/pagination_detail.html' %}
142
- {% endif %}
147
+ </div>
143
148
  </div>
144
- </div>
145
- {% endif %}
149
+ {% endif %}
150
+ </div>
146
151
  </div>
147
152
  </div>
148
153
  </div>
149
- </div>
150
154
 
151
- {% if filter %}
152
- <div id="filter-modal" class="modal">
153
- <div class="modal-background filter-modal-close" onclick="hideFilterModal()"></div>
154
- <div class="modal-card" style="height: 100%">
155
- <header class="modal-card-head">
156
- <p class="modal-card-title">
157
- {% block search_modal_title %}{% translate 'Filter' %}{% endblock %}</p>
158
- <button class="delete filter-modal-close" aria-label="close" onclick="hideFilterModal()"></button>
159
- </header>
160
- <section id="modal-content" class="modal-card-body px-0">
161
- </section>
162
- <footer class="modal-card-foot">
163
- <button id="applyFilterFromModalButton"
164
- class="button is-fullwidth is-success ml-1"
165
- onclick="hideFilterModal()"
166
- >{% translate 'Filter' %}</button>
167
- </footer>
155
+ {% if filter %}
156
+ <div id="filter-modal" class="modal">
157
+ <div class="modal-background filter-modal-close" onclick="hideFilterModal()"></div>
158
+ <div class="modal-card" style="height: 100%">
159
+ <header class="modal-card-head">
160
+ <p class="modal-card-title">
161
+ {% block search_modal_title %}{% translate 'Filter' %}{% endblock %}</p>
162
+ <button class="delete filter-modal-close" aria-label="close" onclick="hideFilterModal()"></button>
163
+ </header>
164
+ <section id="modal-content" class="modal-card-body px-0">
165
+ </section>
166
+ <footer class="modal-card-foot">
167
+ <button id="applyFilterFromModalButton"
168
+ class="button is-fullwidth is-success ml-1"
169
+ onclick="hideFilterModal()"
170
+ >{% translate 'Filter' %}</button>
171
+ </footer>
172
+ </div>
168
173
  </div>
169
- </div>
170
- {% endif %}
174
+ {% endif %}
171
175
 
172
- <div id="form-modal" class="modal"></div>
176
+ <div id="form-modal" class="modal"></div>
177
+ {% endblock %}
173
178
  </body>
@@ -4,9 +4,9 @@
4
4
 
5
5
  {% block content %}
6
6
  <div class="columns is-multiline">
7
- {% for obj in page %}
8
- <div class="list-column column {% block column_width %}is-4{% endblock %}-fullhd is-12-touch is-12-desktop is-12-widescreen"
9
- style="height: {% block column_height %}9rem{% endblock %}"
7
+ {% for obj in list_page %}
8
+ <div class="list-column column is-{{ column_width }}-fullhd is-12-touch is-12-desktop is-12-widescreen"
9
+ style="height: {{ column_height }}rem"
10
10
  {% if endless_scroll and forloop.last and page.has_next %}
11
11
  hx-get="{{ url_params }}&page={{ page.next_page_number }}"
12
12
  hx-trigger="intersect once"
@@ -5,10 +5,11 @@
5
5
 
6
6
  <div id="query-block" class="panel-block pt-0" style="position: relative; display: inline-block; width: 100%">
7
7
  <script src="{% static "js/filter.js" %}" defer type="text/javascript"></script>
8
+
8
9
  <div id="query-apply" hx-get="" hx-trigger="click" hx-replace-url="true"
9
10
  hx-select-oob="#content,#list-pagination,#panel-actions,#header-actions,#query-apply">
10
-
11
11
  </div>
12
+
12
13
  <div id="query-tags" class="is-flex-direction-column is-flex-grow-1 is-multiline mb-1"
13
14
  data-or-label="{% translate 'OR' %}" data-and-label="{% translate 'AND' %}" data-xor-label="{% translate 'XOR' %}">
14
15
  </div>
@@ -30,14 +31,14 @@
30
31
  </div>
31
32
  </div>
32
33
 
33
- <div id="query-input-fieldset" class="field has-addons pt-1">
34
+ <div id="query-input-fieldset" class="field has-addons pt-1 mb-0">
34
35
  <p id="query-input-control" class="control is-expanded">
35
36
  <input id="query-input"
36
37
  type="text"
37
38
  aria-label="Query Input"
38
39
  class="input"
39
40
  placeholder="Enter a search term"
40
- data-default-term="{{ default_filter_term }}"
41
+ data-default-term="{{ filter.default_filter_term }}"
41
42
  onkeyup="inputConfirm()"
42
43
  >
43
44
  </p>
@@ -50,7 +51,30 @@
50
51
  </div>
51
52
 
52
53
 
53
- <div id="query-params-dropdown" class="box mt-1 mx-0 p-1 is-hidden" tabindex="-1" style="z-index: 10; background: white; word-break: break-word; position: inherit; max-height: 300px; overflow-y: auto" onclick="toggleParams()">
54
- {{ filter.to_html }}
54
+ <div id="query-params-dropdown" class="box mt-0 mx-0 p-1 is-hidden is-fullwidth" tabindex="-1" style="z-index: 900; background: white; word-break: break-word; position: absolute; width: 300px; max-height: 300px; overflow-y: auto" onclick="toggleParams()">
55
+ {{ filter.to_html.params }}
56
+ </div>
57
+ <div id="field-selection" class="button mt-1" style="z-index: 10; position: relative" onclick="toggleFieldSelection()">
58
+ {% translate 'Fields' %}
59
+ </div>
60
+ <div id="field-paths" class="box mt-1 mx-0 p-1 is-hidden" tabindex="-1" style="z-index: 800; outline: none; background: white; word-break: break-word; position: absolute; width: 300px; max-height: 300px; overflow-y: auto">
61
+ <div class="field has-addons mr-1">
62
+ <p class="control is-expanded">
63
+ <input id="field-path-search-input" class="input is-small" type="text" aria-label="Query Input" oninput="filterFieldPaths()">
64
+ </p>
65
+ <p class="control">
66
+ <button id="field-path-search-clear" class="button is-small has-icon"><i class="icon-clear"></i></button>
67
+ </p>
68
+ <p class="control">
69
+ <button id="field-path-search-reset" class="button is-small has-icon"><i class="icon-delete-filter"></i></button>
70
+ </p>
71
+ </div>
72
+ <div id="field-path-checkboxes">
73
+ {{ filter.to_html.field_paths }}
74
+ </div>
75
+ <div id="field-path-change">
76
+
77
+ </div>
55
78
  </div>
79
+
56
80
  </div>
@@ -1,14 +1,14 @@
1
1
  {% load i18n %}
2
2
 
3
3
  <div id="header" class="pt-4">
4
- <div class="level level-is-shrinkable is-flex mb-2 pl-3 {% if detail_pagination %}is-mobile {% endif %}">
4
+ <div class="level level-is-shrinkable is-flex mb-2 pl-3 {% if detail_page %}is-mobile {% endif %}">
5
5
  <div class="level-left">
6
6
  <div class="level-item has-text-weight-bold">
7
7
  <nav id="breadcrumbs" class="breadcrumb" aria-label="breadcrumbs" style="white-space: unset; word-break: break-word">
8
8
  <ul>
9
9
  {% for crumb in breadcrumbs %}
10
- <li><a class="is-underlined" hx-get="{{ crumb.url|default_if_none:'#' }}{% if crumb.add_url_params %}{{ url_params }}{% endif %}"
11
- hx-target="body" hx-push-url="true" aria-current="page"
10
+ <li><a class="is-underlined" href="{{ crumb.url|default_if_none:'#' }}"
11
+ aria-current="page" hx-boost="true"
12
12
  >{{ crumb.name }}</a></li>
13
13
  {% endfor %}
14
14
  <li class="is-active"><a aria-current="page">{{ title }}</a></li>
@@ -16,12 +16,12 @@
16
16
  </nav>
17
17
  </div>
18
18
  </div>
19
- {% if list_pagination or detail_pagination %}
19
+ {% if list_page.paginator or detail_page %}
20
20
  <div class="level-right is-mobile is-hidden-mobile is-hidden-fullhd is-align-self-flex-start px-3">
21
21
  <div class="level-item">
22
- {% if list_pagination %}
22
+ {% if list_page.paginator %}
23
23
  {% include 'ui/partials/pagination_list.html' %}
24
- {% elif detail_pagination %}
24
+ {% elif detail_page %}
25
25
  {% include 'ui/partials/pagination_detail.html' %}
26
26
  {% endif %}
27
27
  </div>
@@ -40,7 +40,7 @@
40
40
  {% else %}
41
41
  <a class="button mr-2 {{ action.class_list|join:' ' }}"
42
42
  {% if action.url %}
43
- {{ action.method.value }}="{{ action.url }}{% if action.add_url_params %}{{ url_params|default_if_none:'?' }}{% else %}?{% endif %}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
43
+ {{ action.method.value }}="{{ action.url }}{% if action.add_url_params %}{{ url_params|default_if_none:'?' }}{% else %}?{% endif %}{% if list_page.paginator %}&page={{ list_page.number }}{% endif %}{{ action.query_params }}"
44
44
  {% endif %}
45
45
  {{ action.attrs_str }}
46
46
  >{{ action.name }}
@@ -1,7 +1,7 @@
1
1
  <div id="detail-pagination" class="field has-addons" style="width: 100%">
2
2
  <p class="control">
3
3
  <button class="button"
4
- hx-get="{{ previous_object_url }}{{ url_params }}"
4
+ hx-get="{{ detail_page.previous_object_url }}{{pagination_param_str}}"
5
5
  hx-replace-url="true"
6
6
  hx-select-oob="#content,#detail-pagination,#panel-actions,#header-actions,#breadcrumbs"
7
7
  ><
@@ -10,11 +10,11 @@
10
10
  </p>
11
11
  <p class="control is-expanded">
12
12
  <button class="button is-fullwidth px-1" style="white-space: normal">
13
- <span class="{% if total_objects > 99999 %}is-size-7{% endif %}">{{ current_object_idx }} / {{ total_objects }}</span>
13
+ <span class="{% if detail_page.total_objects > 99999 %}is-size-7{% endif %}">{{ detail_page.current_object_idx }} / {{ detail_page.total_objects }}</span>
14
14
  </button>
15
15
  </p>
16
16
  <p class="control">
17
- <button class="button" hx-get="{{ next_object_url }}{{ url_params }}"
17
+ <button class="button" hx-get="{{ detail_page.next_object_url }}{{pagination_param_str}}"
18
18
  hx-replace-url="true"
19
19
  hx-select-oob="#content,#detail-pagination,#panel-actions,#header-actions,#breadcrumbs"
20
20
  >>
@@ -2,7 +2,7 @@
2
2
  <div id="list-pagination" class="field has-addons" style="min-width: 100%; max-width: 100%">
3
3
  <p class="control">
4
4
  <button class="button"
5
- hx-get="{{ url_params }}&page={% if page.has_previous %}{{ page.previous_page_number }}{% else %}{{ paginator.num_pages }}{% endif %}"
5
+ hx-get="{{ pagination_param_str }}&page={% if list_page.has_previous %}{{ list_page.previous_page_number }}{% else %}{{ list_page.paginator.num_pages }}{% endif %}"
6
6
  hx-replace-url="true"
7
7
  hx-select-oob="#content,#list-pagination,#panel-actions,#header-actions,#breadcrumbs"
8
8
  >
@@ -11,14 +11,14 @@
11
11
  </p>
12
12
  <p class="control is-expanded">
13
13
  <button class="button is-fullwidth px-1" style="white-space: normal">
14
- <span class="{% if paginator.count > 9999 %}is-size-7{% endif %}" style="display: flex">
15
- {{ page.start_index }}-<div id="list-pagination-end-index">{{ page.end_index }}</div> / {{ paginator.count }}
14
+ <span class="{% if list_page.paginator.count > 9999 %}is-size-7{% endif %}" style="display: flex">
15
+ {{ list_page.start_index }}-<div id="list-pagination-end-index">{{ list_page.end_index }}</div> / {{ list_page.paginator.count }}
16
16
  </span>
17
17
  </button>
18
18
  </p>
19
19
  <p class="control">
20
20
  <button id="list-pagination-next-button" class="button"
21
- hx-get="{{ url_params }}&page={% if page.has_next %}{{ page.next_page_number }}{% else %}1{% endif %}"
21
+ hx-get="{{ pagination_param_str }}&page={% if list_page.has_next %}{{ list_page.next_page_number }}{% else %}1{% endif %}"
22
22
  hx-replace-url="true"
23
23
  hx-select-oob="#content,#list-pagination,#panel-actions,#header-actions,#breadcrumbs"
24
24
  >
@@ -4,7 +4,7 @@
4
4
  {% load accrete_ui %}
5
5
 
6
6
  {% block content %}
7
- <table class="table hax-box is-fullwidth is-hoverable" hx-indicator=".htmx-indicator">
7
+ <table class="table is-fullwidth is-hoverable hax-box" hx-indicator=".htmx-indicator">
8
8
  <thead style="position: sticky; top: 0; background: white; z-index: 10">
9
9
  {% block table_header_row %}
10
10
  <tr>
@@ -20,20 +20,14 @@
20
20
  </tr>
21
21
  {% endblock %}
22
22
  </thead>
23
- <tbody style="z-index: 9">
24
- {% for obj in page %}
23
+ <tbody id="content-table-body" style="z-index: 9">
24
+ {% for obj in list_page %}
25
25
  {% block table_data_row %}
26
- <tr {% if forloop.last and page.has_next %}
27
- hx-get="{{ url_params }}&page={{ page.next_page_number }}"
28
- hx-trigger="intersect once"
29
- hx-select="tbody > tr"
30
- hx-select-oob="#list-pagination-end-index,#list-pagination-next-button"
31
- hx-swap="afterend"
32
- {% endif %}>
26
+ <tr>
33
27
  {% block table_data %}
34
- <td><a class="is-underlined" hx-get="{{ obj.get_absolute_url }}{{ url_params }}&page={{ page.number }}"
35
- hx-target="body" hx-push-url="true"
36
- >{{ obj }}</a></td>
28
+ <td>
29
+ <a class="is-underlined" href="{{ obj.get_absolute_url }}{{ object_param_str }}">{{ obj }}</a>
30
+ </td>
37
31
  {% for field in fields %}
38
32
  {% if field.template %}
39
33
  {% include field.template %}
@@ -64,6 +58,17 @@
64
58
  </tbody>
65
59
  </table>
66
60
  {% if endless_scroll %}
61
+ <div id="endless-scroller"
62
+ {% if list_page.has_next %}
63
+ hx-get="{{ pagination_param_str }}&page={{ list_page.next_page_number }}"
64
+ hx-trigger="intersect once"
65
+ hx-select="tbody > tr"
66
+ hx-target="#content-table-body"
67
+ hx-swap="beforeend"
68
+ hx-select-oob="#list-pagination-end-index,#list-pagination-next-button,#endless-scroller"
69
+ {% endif %}
70
+ >
71
+ </div>
67
72
  <progress class="htmx-indicator progress is-small is-primary" max="100">15%</progress>
68
73
  {% endif %}
69
74
  {% endblock %}
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  from django import template
3
+ from django.db.models import Model
3
4
  from django.core.cache import cache
4
5
  from django.conf import settings
5
6
  from django.template.loader import render_to_string
@@ -26,7 +27,7 @@ def combine_templates(template_name, request=None):
26
27
 
27
28
 
28
29
  @register.filter(name='get_attr')
29
- def get_attr_from_string(param, value):
30
+ def get_attr_from_string(param: object, value: str):
30
31
  try:
31
32
  attr = getattr(param, value)
32
33
  except AttributeError:
@@ -38,6 +39,16 @@ def get_attr_from_string(param, value):
38
39
  return attr
39
40
 
40
41
 
42
+ def get_field_value(param: type[Model], value: str):
43
+ model = param
44
+ vals = value.split('__')
45
+ if len(vals) == 1:
46
+ return get_attr_from_string(model, vals[0])
47
+ for val in value.split('__'):
48
+ pass
49
+
50
+
51
+
41
52
  @register.filter(name='message_class')
42
53
  def message_class(param):
43
54
  if param.level == 25:
@@ -65,10 +65,6 @@ class ChangePasswordForm(ModelForm):
65
65
  super().clean()
66
66
  new_password = self.cleaned_data.get('new_password')
67
67
  new_password_confirm = self.cleaned_data.get('new_password_confirm')
68
- if not new_password or not new_password_confirm:
69
- raise ValidationError(_(
70
- 'New password and password confirmation are required'
71
- ))
72
68
  if new_password != new_password_confirm:
73
69
  raise ValidationError(
74
70
  _('New password and confirmation did not match.')
@@ -1,17 +1,11 @@
1
- <!doctype html>
1
+ {% extends 'ui/layout.html' %}
2
2
  {% load static %}
3
3
  {% load i18n %}
4
4
 
5
- <html lang="en">
6
- <head>
7
- <meta charset="utf-8">
8
- <meta name="viewport" content="width=device-width, initial-scale=1">
9
- <link rel="stylesheet" type="text/css" href="{% static "css/bulma.min.css" %}">
10
- <link rel="shortcut icon" type="image/png" href="{% static 'icons/accrete.svg' %}"/>
11
- <title>Login | Accrete</title>
12
- </head>
13
- <body>
14
- <section class="hero is-primary is-medium is-bold is-fullheight">
5
+ {% block title %}Login | Accrete{% endblock %}
6
+
7
+ {% block body %}
8
+ <section class="hero is-primary is-medium is-bold is-fullheight">
15
9
  <div class="hero-body">
16
10
  <div class="container">
17
11
  <div class="columns">
@@ -37,4 +31,4 @@
37
31
  </div>
38
32
  </div>
39
33
  </section>
40
- </body>
34
+ {% endblock %}