accrete 0.0.99__py3-none-any.whl → 0.0.109__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 (254) hide show
  1. accrete/contrib/ui/__init__.py +6 -27
  2. accrete/contrib/ui/context.py +26 -294
  3. accrete/contrib/ui/filter.py +309 -280
  4. accrete/contrib/ui/middleware.py +44 -0
  5. accrete/contrib/ui/models.py +3 -0
  6. accrete/contrib/ui/static/bulma/README.md +4 -2
  7. accrete/contrib/ui/static/bulma/bulma.css +21551 -0
  8. accrete/contrib/ui/static/bulma/bulma.css.map +1 -0
  9. accrete/contrib/ui/static/bulma/bulma.scss +1 -1
  10. accrete/contrib/ui/static/bulma/css/bulma.css +1988 -2874
  11. accrete/contrib/ui/static/bulma/css/bulma.css.map +1 -1
  12. accrete/contrib/ui/static/bulma/css/bulma.min.css +2 -2
  13. accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css +19648 -0
  14. accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css.map +1 -0
  15. accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css +2 -2
  16. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css +11136 -0
  17. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css.map +1 -0
  18. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css +2 -2
  19. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css +11136 -0
  20. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css.map +1 -0
  21. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css +2 -2
  22. accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css +21550 -2
  23. accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css.map +1 -1
  24. accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.min.css +3 -0
  25. accrete/contrib/ui/static/bulma/package.json +12 -11
  26. accrete/contrib/ui/static/bulma/sass/base/animations.css +15 -0
  27. accrete/contrib/ui/static/bulma/sass/base/animations.css.map +1 -0
  28. accrete/contrib/ui/static/bulma/sass/base/generic.css +196 -0
  29. accrete/contrib/ui/static/bulma/sass/base/generic.css.map +1 -0
  30. accrete/contrib/ui/static/bulma/sass/base/minireset.css +82 -0
  31. accrete/contrib/ui/static/bulma/sass/base/minireset.css.map +1 -0
  32. accrete/contrib/ui/static/bulma/sass/base/skeleton.css +113 -0
  33. accrete/contrib/ui/static/bulma/sass/base/skeleton.css.map +1 -0
  34. accrete/contrib/ui/static/bulma/sass/base/skeleton.scss +0 -12
  35. accrete/contrib/ui/static/bulma/sass/components/breadcrumb.css +108 -0
  36. accrete/contrib/ui/static/bulma/sass/components/breadcrumb.css.map +1 -0
  37. accrete/contrib/ui/static/bulma/sass/components/card.css +130 -0
  38. accrete/contrib/ui/static/bulma/sass/components/card.css.map +1 -0
  39. accrete/contrib/ui/static/bulma/sass/components/dropdown.css +119 -0
  40. accrete/contrib/ui/static/bulma/sass/components/dropdown.css.map +1 -0
  41. accrete/contrib/ui/static/bulma/sass/components/menu.css +119 -0
  42. accrete/contrib/ui/static/bulma/sass/components/menu.css.map +1 -0
  43. accrete/contrib/ui/static/bulma/sass/components/message.css +191 -0
  44. accrete/contrib/ui/static/bulma/sass/components/message.css.map +1 -0
  45. accrete/contrib/ui/static/bulma/sass/components/modal.css +194 -0
  46. accrete/contrib/ui/static/bulma/sass/components/modal.css.map +1 -0
  47. accrete/contrib/ui/static/bulma/sass/components/navbar.css +768 -0
  48. accrete/contrib/ui/static/bulma/sass/components/navbar.css.map +1 -0
  49. accrete/contrib/ui/static/bulma/sass/components/navbar.scss +41 -30
  50. accrete/contrib/ui/static/bulma/sass/components/pagination.css +302 -0
  51. accrete/contrib/ui/static/bulma/sass/components/pagination.css.map +1 -0
  52. accrete/contrib/ui/static/bulma/sass/components/panel.css +224 -0
  53. accrete/contrib/ui/static/bulma/sass/components/panel.css.map +1 -0
  54. accrete/contrib/ui/static/bulma/sass/components/panel.scss +2 -2
  55. accrete/contrib/ui/static/bulma/sass/components/tabs.css +192 -0
  56. accrete/contrib/ui/static/bulma/sass/components/tabs.css.map +1 -0
  57. accrete/contrib/ui/static/bulma/sass/elements/block.css +17 -0
  58. accrete/contrib/ui/static/bulma/sass/elements/block.css.map +1 -0
  59. accrete/contrib/ui/static/bulma/sass/elements/box.css +43 -0
  60. accrete/contrib/ui/static/bulma/sass/elements/box.css.map +1 -0
  61. accrete/contrib/ui/static/bulma/sass/elements/button.css +685 -0
  62. accrete/contrib/ui/static/bulma/sass/elements/button.css.map +1 -0
  63. accrete/contrib/ui/static/bulma/sass/elements/button.scss +9 -2
  64. accrete/contrib/ui/static/bulma/sass/elements/content.css +208 -0
  65. accrete/contrib/ui/static/bulma/sass/elements/content.css.map +1 -0
  66. accrete/contrib/ui/static/bulma/sass/elements/content.scss +8 -2
  67. accrete/contrib/ui/static/bulma/sass/elements/delete.css +60 -0
  68. accrete/contrib/ui/static/bulma/sass/elements/delete.css.map +1 -0
  69. accrete/contrib/ui/static/bulma/sass/elements/icon.css +51 -0
  70. accrete/contrib/ui/static/bulma/sass/elements/icon.css.map +1 -0
  71. accrete/contrib/ui/static/bulma/sass/elements/image.css +253 -0
  72. accrete/contrib/ui/static/bulma/sass/elements/image.css.map +1 -0
  73. accrete/contrib/ui/static/bulma/sass/elements/loader.css +14 -0
  74. accrete/contrib/ui/static/bulma/sass/elements/loader.css.map +1 -0
  75. accrete/contrib/ui/static/bulma/sass/elements/notification.css +213 -0
  76. accrete/contrib/ui/static/bulma/sass/elements/notification.css.map +1 -0
  77. accrete/contrib/ui/static/bulma/sass/elements/progress.css +119 -0
  78. accrete/contrib/ui/static/bulma/sass/elements/progress.css.map +1 -0
  79. accrete/contrib/ui/static/bulma/sass/elements/table.css +294 -0
  80. accrete/contrib/ui/static/bulma/sass/elements/table.css.map +1 -0
  81. accrete/contrib/ui/static/bulma/sass/elements/tag.css +252 -0
  82. accrete/contrib/ui/static/bulma/sass/elements/tag.css.map +1 -0
  83. accrete/contrib/ui/static/bulma/sass/elements/title.css +131 -0
  84. accrete/contrib/ui/static/bulma/sass/elements/title.css.map +1 -0
  85. accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.css +72 -0
  86. accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.css.map +1 -0
  87. accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.scss +7 -3
  88. accrete/contrib/ui/static/bulma/sass/form/file.css +374 -0
  89. accrete/contrib/ui/static/bulma/sass/form/file.css.map +1 -0
  90. accrete/contrib/ui/static/bulma/sass/form/input-textarea.css +284 -0
  91. accrete/contrib/ui/static/bulma/sass/form/input-textarea.css.map +1 -0
  92. accrete/contrib/ui/static/bulma/sass/form/input-textarea.scss +0 -10
  93. accrete/contrib/ui/static/bulma/sass/form/select.css +347 -0
  94. accrete/contrib/ui/static/bulma/sass/form/select.css.map +1 -0
  95. accrete/contrib/ui/static/bulma/sass/form/select.scss +1 -0
  96. accrete/contrib/ui/static/bulma/sass/form/shared.css +48 -0
  97. accrete/contrib/ui/static/bulma/sass/form/shared.css.map +1 -0
  98. accrete/contrib/ui/static/bulma/sass/form/shared.scss +5 -1
  99. accrete/contrib/ui/static/bulma/sass/form/tools.css +356 -0
  100. accrete/contrib/ui/static/bulma/sass/form/tools.css.map +1 -0
  101. accrete/contrib/ui/static/bulma/sass/form/tools.scss +23 -12
  102. accrete/contrib/ui/static/bulma/sass/grid/columns-v2.css +1635 -0
  103. accrete/contrib/ui/static/bulma/sass/grid/columns-v2.css.map +1 -0
  104. accrete/contrib/ui/static/bulma/sass/grid/columns.css +1652 -0
  105. accrete/contrib/ui/static/bulma/sass/grid/columns.css.map +1 -0
  106. accrete/contrib/ui/static/bulma/sass/grid/columns.scss +109 -25
  107. accrete/contrib/ui/static/bulma/sass/grid/grid.css +3011 -0
  108. accrete/contrib/ui/static/bulma/sass/grid/grid.css.map +1 -0
  109. accrete/contrib/ui/static/bulma/sass/grid/grid.scss +3 -3
  110. accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.css +61 -0
  111. accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.css.map +1 -0
  112. accrete/contrib/ui/static/bulma/sass/helpers/border.css +17 -0
  113. accrete/contrib/ui/static/bulma/sass/helpers/border.css.map +1 -0
  114. accrete/contrib/ui/static/bulma/sass/helpers/color.css +5582 -0
  115. accrete/contrib/ui/static/bulma/sass/helpers/color.css.map +1 -0
  116. accrete/contrib/ui/static/bulma/sass/helpers/color.scss +166 -186
  117. accrete/contrib/ui/static/bulma/sass/helpers/flexbox.css +217 -0
  118. accrete/contrib/ui/static/bulma/sass/helpers/flexbox.css.map +1 -0
  119. accrete/contrib/ui/static/bulma/sass/helpers/float.css +37 -0
  120. accrete/contrib/ui/static/bulma/sass/helpers/float.css.map +1 -0
  121. accrete/contrib/ui/static/bulma/sass/helpers/gap.css +209 -0
  122. accrete/contrib/ui/static/bulma/sass/helpers/gap.css.map +1 -0
  123. accrete/contrib/ui/static/bulma/sass/helpers/other.css +34 -0
  124. accrete/contrib/ui/static/bulma/sass/helpers/other.css.map +1 -0
  125. accrete/contrib/ui/static/bulma/sass/helpers/overflow.css +65 -0
  126. accrete/contrib/ui/static/bulma/sass/helpers/overflow.css.map +1 -0
  127. accrete/contrib/ui/static/bulma/sass/helpers/position.css +45 -0
  128. accrete/contrib/ui/static/bulma/sass/helpers/position.css.map +1 -0
  129. accrete/contrib/ui/static/bulma/sass/helpers/spacing.css +489 -0
  130. accrete/contrib/ui/static/bulma/sass/helpers/spacing.css.map +1 -0
  131. accrete/contrib/ui/static/bulma/sass/helpers/typography.css +423 -0
  132. accrete/contrib/ui/static/bulma/sass/helpers/typography.css.map +1 -0
  133. accrete/contrib/ui/static/bulma/sass/helpers/visibility.css +485 -0
  134. accrete/contrib/ui/static/bulma/sass/helpers/visibility.css.map +1 -0
  135. accrete/contrib/ui/static/bulma/sass/layout/container.scss +16 -8
  136. accrete/contrib/ui/static/bulma/sass/layout/footer.css +9 -0
  137. accrete/contrib/ui/static/bulma/sass/layout/footer.css.map +1 -0
  138. accrete/contrib/ui/static/bulma/sass/layout/hero.css +534 -0
  139. accrete/contrib/ui/static/bulma/sass/layout/hero.css.map +1 -0
  140. accrete/contrib/ui/static/bulma/sass/layout/level.css +102 -0
  141. accrete/contrib/ui/static/bulma/sass/layout/level.css.map +1 -0
  142. accrete/contrib/ui/static/bulma/sass/layout/media.css +90 -0
  143. accrete/contrib/ui/static/bulma/sass/layout/media.css.map +1 -0
  144. accrete/contrib/ui/static/bulma/sass/layout/section.css +23 -0
  145. accrete/contrib/ui/static/bulma/sass/layout/section.css.map +1 -0
  146. accrete/contrib/ui/static/bulma/sass/layout/section.scss +4 -0
  147. accrete/contrib/ui/static/bulma/sass/themes/dark.css +3 -0
  148. accrete/contrib/ui/static/bulma/sass/themes/dark.css.map +1 -0
  149. accrete/contrib/ui/static/bulma/sass/themes/light.css +3 -0
  150. accrete/contrib/ui/static/bulma/sass/themes/light.css.map +1 -0
  151. accrete/contrib/ui/static/bulma/sass/themes/light.scss +1 -0
  152. accrete/contrib/ui/static/bulma/sass/themes/setup.css +3 -0
  153. accrete/contrib/ui/static/bulma/sass/themes/setup.css.map +1 -0
  154. accrete/contrib/ui/static/bulma/sass/utilities/controls.css +13 -0
  155. accrete/contrib/ui/static/bulma/sass/utilities/controls.css.map +1 -0
  156. accrete/contrib/ui/static/bulma/sass/utilities/css-variables.css +3 -0
  157. accrete/contrib/ui/static/bulma/sass/utilities/css-variables.css.map +1 -0
  158. accrete/contrib/ui/static/bulma/sass/utilities/css-variables.scss +3 -2
  159. accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.css +3 -0
  160. accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.css.map +1 -0
  161. accrete/contrib/ui/static/bulma/sass/utilities/extends.css +13 -0
  162. accrete/contrib/ui/static/bulma/sass/utilities/extends.css.map +1 -0
  163. accrete/contrib/ui/static/bulma/sass/utilities/functions.scss +2 -2
  164. accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.css +3 -0
  165. accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.css.map +1 -0
  166. accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.scss +4 -4
  167. accrete/contrib/ui/static/bulma/sass/utilities/mixins.css +3 -0
  168. accrete/contrib/ui/static/bulma/sass/utilities/mixins.css.map +1 -0
  169. accrete/contrib/ui/static/bulma/sass/utilities/mixins.scss +1 -1
  170. accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.css +19648 -0
  171. accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.css.map +1 -0
  172. accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.scss +2 -1
  173. accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.css +11136 -0
  174. accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.css.map +1 -0
  175. accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.scss +1 -1
  176. accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.css +11136 -0
  177. accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.css.map +1 -0
  178. accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.scss +1 -1
  179. accrete/contrib/ui/static/bulma/versions/bulma-prefixed.css +21551 -0
  180. accrete/contrib/ui/static/bulma/versions/bulma-prefixed.css.map +1 -0
  181. accrete/contrib/ui/static/bulma/versions/bulma-prefixed.scss +1 -1
  182. accrete/contrib/ui/static/css/accrete.css +20757 -19996
  183. accrete/contrib/ui/static/css/accrete.css.map +1 -1
  184. accrete/contrib/ui/static/css/accrete.scss +185 -461
  185. accrete/contrib/ui/static/js/filter.js +98 -679
  186. accrete/contrib/ui/static/js/htmx.min.js +1 -1
  187. accrete/contrib/ui/templates/django/forms/widgets/date.html +5 -0
  188. accrete/contrib/ui/templates/django/forms/widgets/input.html +1 -1
  189. accrete/contrib/ui/templates/django/forms/widgets/select.html +7 -5
  190. accrete/contrib/ui/templates/ui/content_right.html +7 -0
  191. accrete/contrib/ui/templates/ui/filter/filter.html +27 -0
  192. accrete/contrib/ui/templates/ui/filter/query_input.html +31 -0
  193. accrete/contrib/ui/templates/ui/filter/query_operator.html +11 -0
  194. accrete/contrib/ui/templates/ui/filter/query_params.html +106 -0
  195. accrete/contrib/ui/templates/ui/filter/query_tags.html +26 -0
  196. accrete/contrib/ui/templates/ui/layout.html +175 -232
  197. accrete/contrib/ui/templates/ui/list.html +39 -28
  198. accrete/contrib/ui/templates/ui/list_update.html +3 -0
  199. accrete/contrib/ui/templates/ui/message.html +13 -0
  200. accrete/contrib/ui/templates/ui/{partials/modal.html → modal.html} +9 -5
  201. accrete/contrib/ui/templates/ui/oob.html +3 -0
  202. accrete/contrib/ui/templates/ui/table.html +67 -71
  203. accrete/contrib/ui/templates/ui/table_row_update.html +14 -0
  204. accrete/contrib/ui/templates/ui/widgets/model_search_select.html +42 -0
  205. accrete/contrib/ui/templates/ui/widgets/model_search_select_multi.html +78 -0
  206. accrete/contrib/ui/templates/ui/widgets/model_search_select_options.html +3 -0
  207. accrete/contrib/ui/templatetags/{accrete_ui.py → ui.py} +69 -57
  208. accrete/contrib/ui/urls.py +3 -1
  209. accrete/contrib/ui/views.py +33 -3
  210. accrete/contrib/ui/widgets/__init__.py +1 -0
  211. accrete/contrib/ui/widgets/search_select.py +106 -0
  212. accrete/contrib/user/forms.py +1 -1
  213. accrete/contrib/user/templates/user/change_email.html +22 -27
  214. accrete/contrib/user/templates/user/change_password.html +24 -36
  215. accrete/contrib/user/templates/user/login.html +72 -23
  216. accrete/contrib/user/templates/user/user_form.html +50 -52
  217. accrete/contrib/user/urls.py +2 -3
  218. accrete/contrib/user/views.py +39 -46
  219. accrete/forms.py +0 -2
  220. accrete/managers.py +4 -4
  221. accrete/middleware.py +43 -68
  222. accrete/models.py +7 -1
  223. accrete/storage.py +4 -1
  224. accrete/tenant.py +11 -5
  225. accrete/utils/__init__.py +2 -0
  226. accrete/utils/models.py +9 -1
  227. accrete/utils/views.py +36 -20
  228. accrete/views.py +9 -5
  229. {accrete-0.0.99.dist-info → accrete-0.0.109.dist-info}/METADATA +2 -2
  230. accrete-0.0.109.dist-info/RECORD +369 -0
  231. {accrete-0.0.99.dist-info → accrete-0.0.109.dist-info}/WHEEL +1 -1
  232. accrete/contrib/ui/elements.py +0 -93
  233. accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css.map +0 -1
  234. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css.map +0 -1
  235. accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css.map +0 -1
  236. accrete/contrib/ui/static/bulma/sass/grid/columns-v2.scss +0 -957
  237. accrete/contrib/ui/static/js/ui.js +0 -30
  238. accrete/contrib/ui/templates/ui/dashboard.html +0 -7
  239. accrete/contrib/ui/templates/ui/detail.html +0 -19
  240. accrete/contrib/ui/templates/ui/form.html +0 -21
  241. accrete/contrib/ui/templates/ui/partials/filter.html +0 -143
  242. accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -34
  243. accrete/contrib/ui/templates/ui/partials/header.html +0 -123
  244. accrete/contrib/ui/templates/ui/partials/modal_form.html +0 -45
  245. accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -1
  246. accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -23
  247. accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -28
  248. accrete/contrib/ui/templates/ui/partials/table_field.html +0 -16
  249. accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -1
  250. accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -4
  251. accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -1
  252. accrete/contrib/user/templates/user/user_detail.html +0 -43
  253. accrete-0.0.99.dist-info/RECORD +0 -231
  254. {accrete-0.0.99.dist-info → accrete-0.0.109.dist-info}/licenses/LICENSE +0 -0
@@ -1,29 +1,24 @@
1
- {% extends 'ui/form.html' %}
1
+ {% extends 'ui/modal.html' %}
2
+ {% load i18n %}
2
3
 
3
- {% block form %}
4
- <form id="form" method="post" action="">
5
- {% csrf_token %}
6
- <div class="columns">
7
- <div class="column is-6">
8
- <label class="label has-field is-required">
9
- {{ form.email.label_tag }}
10
- <span class="has-text-danger is-size-7">{{ form.email.errors }}</span>
11
- <div class="field">
12
- <p class="control is-fullwidth">{{ form.email }}</p>
13
- </div>
14
- </label>
15
- </div>
16
- </div>
17
- <div class="columns">
18
- <div class="column is-6">
19
- <label class="label has-field is-required">
20
- {{ form.password.label_tag }}
21
- <span class="has-text-danger is-size-7">{{ form.password.errors }}</span>
22
- <div class="field">
23
- <p class="control is-fullwidth">{{ form.password }}</p>
24
- </div>
25
- </label>
26
- </div>
27
- </div>
28
- </form>
4
+ {% block modal_content %}
5
+ <form id="{{ modal_id }}-form" hx-post="{% url 'user:change_email' %}">
6
+ {% csrf_token %}
7
+ <label class="label has-field is-required">
8
+ {{ form.email.label }}
9
+ {{ form.email }}
10
+ <span class="has-text-danger is-size-7">{{ form.email.errors }}</span>
11
+ </label>
12
+ <label class="label has-field is-required">
13
+ {{ form.password.label }}
14
+ {{ form.password }}
15
+ <span class="has-text-danger is-size-7">{{ form.password.errors }}</span>
16
+ </label>
17
+ </form>
29
18
  {% endblock %}
19
+
20
+ {% block modal_buttons %}
21
+ <button type="submit" form="{{ modal_id }}-form" class="button is-success">{% translate 'Save' %}</button>
22
+ {{ block.super }}
23
+ {% endblock %}
24
+
@@ -1,40 +1,28 @@
1
- {% extends 'ui/form.html' %}
1
+ {% extends 'ui/modal.html' %}
2
+ {% load i18n %}
2
3
 
3
- {% block form %}
4
- <form id="form" method="post" action="">
4
+ {% block modal_content %}
5
+ <form id="{{ modal_id }}-form" hx-post="{% url 'user:change_password' %}">
5
6
  {% csrf_token %}
6
- <div class="columns">
7
- <div class="column is-6">
8
- <label class="label has-field is-required">
9
- {{ form.old_password.label_tag }}
10
- <span class="has-text-danger is-size-7">{{ form.old_password.errors }}</span>
11
- <div class="field">
12
- <p class="control is-fullwidth">{{ form.old_password }}</p>
13
- </div>
14
- </label>
15
- </div>
16
- </div>
17
- <div class="columns">
18
- <div class="column is-6">
19
- <label class="label has-field is-required">
20
- {{ form.new_password.label_tag }}
21
- <span class="has-text-danger is-size-7">{{ form.new_password.errors }}</span>
22
- <div class="field">
23
- <p class="control is-fullwidth">{{ form.new_password }}</p>
24
- </div>
25
- </label>
26
- </div>
27
- </div>
28
- <div class="columns">
29
- <div class="column is-6">
30
- <label class="label has-field is-required">
31
- {{ form.new_password_confirm.label_tag }}
32
- <span class="has-text-danger is-size-7">{{ form.new_password_confirm.errors }}</span>
33
- <div class="field">
34
- <p class="control is-fullwidth">{{ form.new_password_confirm }}</p>
35
- </div>
36
- </label>
37
- </div>
38
- </div>
7
+ <label class="label">
8
+ {{ form.old_password.label }}
9
+ {{ form.old_password }}
10
+ <span class="has-text-danger is-size-7">{{ form.old_password.errors }}</span>
11
+ </label>
12
+ <label class="label has-field is-required">
13
+ {{ form.new_password.label }}
14
+ {{ form.new_password }}
15
+ <span class="has-text-danger is-size-7">{{ form.new_password.errors }}</span>
16
+ </label>
17
+ <label class="label has-field is-required">
18
+ {{ form.new_password_confirm.label }}
19
+ {{ form.new_password_confirm }}
20
+ <span class="has-text-danger is-size-7">{{ form.new_password_confirm.errors }}</span>
21
+ </label>
39
22
  </form>
40
23
  {% endblock %}
24
+
25
+ {% block modal_buttons %}
26
+ <button type="submit" form="{{ modal_id }}-form" class="button is-success">{% translate 'Save' %}</button>
27
+ {{ block.super }}
28
+ {% endblock %}
@@ -2,33 +2,82 @@
2
2
  {% load static %}
3
3
  {% load i18n %}
4
4
 
5
- {% block title %}Login | Accrete{% endblock %}
5
+ {% block title %}Login{% endblock %}
6
6
 
7
7
  {% block body %}
8
- <section class="hero is-primary is-medium is-bold is-fullheight">
9
- <div class="hero-body">
10
- <div class="container">
11
- <div class="columns">
12
- <div class="column box is-8-tablet is-offset-2-tablet is-6-desktop is-offset-3-desktop is-4-fullhd is-offset-4-fullhd p-3">
13
- <form method="POST" action="" hx-boost="false">
14
- {% csrf_token %}
15
- <span class="has-text-danger is-size-7">{{ form.non_field_errors }}</span>
16
-
17
- <label class="label">{{ form.username.label_tag }}
18
- {{ form.username }}
19
- <span class="has-text-danger is-size-7">{{ form.username.errors }}</span>
20
- </label>
21
-
22
- <label class="label">{{ form.password.label_tag }}
23
- {{form.password}}
24
- <span class="has-text-danger is-size-7">{{ form.password.errors }}</span>
25
- </label>
8
+ <section class="hero is-success is-fullheight">
9
+ <!-- Hero head: will stick at the top -->
10
+ <div class="hero-head">
11
+ <header class="navbar">
12
+ <div class="container">
13
+ <div class="navbar-brand">
14
+ <p class="title navbar-item">HRP</p>
15
+ <span class="navbar-burger" data-target="navbarMenuHeroC">
16
+ <span></span>
17
+ <span></span>
18
+ <span></span>
19
+ <span></span>
20
+ </span>
21
+ </div>
22
+ {# <div id="navbarMenuHeroC" class="navbar-menu">#}
23
+ {# <div class="navbar-end">#}
24
+ {# <a class="navbar-item is-active"> Home </a>#}
25
+ {# <a class="navbar-item"> Examples </a>#}
26
+ {# <a class="navbar-item"> Documentation </a>#}
27
+ {# <span class="navbar-item">#}
28
+ {# <a class="button is-success is-inverted">#}
29
+ {# <span class="icon">#}
30
+ {# <i class="fab fa-github"></i>#}
31
+ {# </span>#}
32
+ {# <span>Download</span>#}
33
+ {# </a>#}
34
+ {# </span>#}
35
+ {# </div>#}
36
+ {# </div>#}
37
+ </div>
38
+ </header>
39
+ </div>
26
40
 
27
- <input type="submit" class="button is-fullwidth is-success is-medium" value="{% translate 'Login' %}">
28
- </form>
41
+ <!-- Hero content: will be in the middle -->
42
+ <div class="hero-body">
43
+ <div class="container">
44
+ <div class="columns">
45
+ <div class="column box is-8-tablet is-offset-2-tablet is-6-desktop is-offset-3-desktop is-4-fullhd is-offset-4-fullhd p-3" data-theme="light">
46
+ <form method="POST" action="" hx-boost="false">
47
+ {% csrf_token %}
48
+ <span class="has-text-danger is-size-7">{{ form.non_field_errors }}</span>
49
+ <label class="label">
50
+ <span class="has-text-black">{{ form.username.label_tag }}</span>
51
+ {{ form.username }}
52
+ <span class="has-text-danger is-size-7">{{ form.username.errors }}</span>
53
+ </label>
54
+ <label class="label">
55
+ <span class="has-text-black">{{ form.password.label_tag }}</span>
56
+ {{ form.password }}
57
+ <span class="has-text-danger is-size-7">{{ form.password.errors }}</span>
58
+ </label>
59
+ <input type="submit" class="button is-fullwidth is-success is-medium"
60
+ value="{% translate 'Login' %}">
61
+ </form>
62
+ </div>
29
63
  </div>
30
64
  </div>
31
65
  </div>
32
- </div>
33
- </section>
66
+
67
+ <!-- Hero footer: will stick at the bottom -->
68
+ <div class="hero-foot">
69
+ <nav class="tabs is-boxed is-fullwidth">
70
+ <div class="container">
71
+ <ul>
72
+ {# <li class="is-active"><a>Overview</a></li>#}
73
+ {# <li><a>Modifiers</a></li>#}
74
+ {# <li><a>Grid</a></li>#}
75
+ {# <li><a>Elements</a></li>#}
76
+ {# <li><a>Components</a></li>#}
77
+ {# <li><a>Layout</a></li>#}
78
+ </ul>
79
+ </div>
80
+ </nav>
81
+ </div>
82
+ </section>
34
83
  {% endblock %}
@@ -1,56 +1,54 @@
1
- {% extends 'ui/form.html' %}
1
+ {% extends 'ui/layout.html' %}
2
2
  {% load i18n %}
3
3
 
4
- {% block form %}
5
- <form id="form" method="post" action="">
6
- {% csrf_token %}
7
- <div class="columns">
8
- <div class="column is-6">
9
- <label class="label has-field">
10
- {{ form.username.label }}
11
- <span class="has-text-danger is-size-7">{{ form.username.errors }}</span>
12
- <div class="field">
13
- <p class="control is-expanded">{{ form.username }}</p>
14
- </div>
15
- </label>
16
- </div>
17
- </div>
18
-
19
- <div class="columns">
20
- <div class="column is-6">
21
- <label class="label has-field">
22
- {{ form.first_name.label }}
23
- <span class="has-text-danger is-size-7">{{ form.first_name.errors }}</span>
24
- <div class="field">
25
- <p class="control is-fullwidth is-expanded">{{ form.first_name }}</p>
26
- </div>
27
-
28
- </label>
29
- </div>
30
- </div>
31
-
32
- <div class="columns">
33
- <div class="column is-6">
34
- <label class="label has-field">
35
- {{ form.last_name.label }}
36
- <span class="has-text-danger is-size-7">{{ form.last_name.errors }}</span>
37
- <div class="field">
38
- <p class="control is-fullwidth is-expanded">{{ form.last_name }}</p>
39
- </div>
40
- </label>
41
- </div>
42
- </div>
4
+ {% block header_left %}
5
+ {{ block.super }}
6
+ <button class="button p-2 my-1 mr-1 is-light" hx-get="{% url 'user:change_password' %}" hx-target="body" hx-swap="beforeend">
7
+ <span>{% translate 'Change Password' %}</span>
8
+ </button>
9
+ <button class="button p-2 my-1 mr-1 is-light" hx-get="{% url 'user:change_email' %}" hx-target="body" hx-swap="beforeend">
10
+ <span>{% translate 'Change E-Mail' %}</span>
11
+ </button>
12
+ {% endblock %}
43
13
 
44
- <div class="columns">
45
- <div class="column is-6">
46
- <label class="label has-field is-required">
47
- {{ form.language_code.label }}
48
- <span class="has-text-danger is-size-7">{{ form.language_code.errors }}</span>
49
- <div class="field">
50
- <p class="control is-fullwidth is-expanded"><span class="select is-fullwidth">{{ form.language_code }}</span></p>
51
- </div>
52
- </label>
14
+ {% block content_left %}
15
+ <form id="user-form" hx-post="{% url 'user:detail' %}" hx-trigger="change changed" hx-select="#user-form" hx-swap="outerHTML" hx-select-oob="#navbar-user-name">
16
+ {% csrf_token %}
17
+ <div class="columns m-0">
18
+ <div class="column is-6">
19
+ {% if form.save_error or form.non_field_errors %}
20
+ <div class="notification is-danger is-light">
21
+ {{ form.save_error|default_if_none:'' }}
22
+ {{ form.non_field_errors|default_if_none:'' }}
23
+ </div>
24
+ {% endif %}
25
+ <label class="label">
26
+ {{ form.username.label }}
27
+ {{ form.username }}
28
+ <span class="has-text-danger is-size-7">{{ form.username.errors }}</span>
29
+ </label>
30
+ <label class="label">
31
+ {{ form.first_name.label }}
32
+ {{ form.first_name }}
33
+ <span class="has-text-danger is-size-7">{{ form.first_name.errors }}</span>
34
+ </label>
35
+ <label class="label">
36
+ {{ form.last_name.label }}
37
+ {{ form.last_name }}
38
+ <span class="has-text-danger is-size-7">{{ form.last_name.errors }}</span>
39
+ </label>
40
+ <label class="label">
41
+ {{ form.language_code.label }}
42
+ {{ form.language_code }}
43
+ <span class="has-text-danger is-size-7">{{ form.language_code.errors }}</span>
44
+ </label>
45
+ <label class="label">
46
+ {% translate 'E-Mail' %}
47
+ <div class="field has-addons">
48
+ <p class="control input has-text-weight-normal">{{ user.email }}</p>
49
+ </div>
50
+ </label>
51
+ </div>
53
52
  </div>
54
- </div>
55
- </form>
56
- {% endblock %}
53
+ </form>
54
+ {% endblock %}
@@ -7,7 +7,6 @@ urlpatterns = [
7
7
  path('login/', views.LoginView.as_view(), name='login'),
8
8
  path('logout/', views.LogoutView.as_view(), name='logout'),
9
9
  path('detail/', views.user_detail, name='detail'),
10
- path('edit/', views.user_edit, name='edit'),
11
- path('edit/password', views.user_change_password, name='edit_password'),
12
- path('edit/email', views.user_change_email, name='edit_email')
10
+ path('password/', views.user_change_password, name='change_password'),
11
+ path('email/', views.user_change_email, name='change_email')
13
12
  ]
@@ -1,8 +1,7 @@
1
1
  from django.contrib.auth.forms import AuthenticationForm
2
2
  from django.contrib.auth import views, update_session_auth_hash
3
3
  from django.contrib.auth.decorators import login_required
4
- from django.contrib import messages
5
- from django.shortcuts import redirect, render, reverse, resolve_url
4
+ from django.shortcuts import redirect, render, resolve_url
6
5
  from django.utils.translation import gettext_lazy as _
7
6
  from django.conf import settings
8
7
 
@@ -36,21 +35,6 @@ class LogoutView(views.LogoutView):
36
35
 
37
36
  @login_required()
38
37
  def user_detail(request):
39
- ctx = ui.DetailContext(
40
- title=_('Preferences'),
41
- object=request.user,
42
- breadcrumbs=[],
43
- actions=[
44
- ui.ClientAction(_('Edit'), url=reverse('user:edit')),
45
- ui.ClientAction(_('Change E-Mail'), url=reverse('user:edit_email')),
46
- ui.ClientAction(_('Change Password'), url=reverse('user:edit_password'))
47
- ]
48
- ).dict()
49
- return render(request, 'user/user_detail.html', ctx)
50
-
51
-
52
- @login_required()
53
- def user_edit(request):
54
38
  form = UserForm(
55
39
  initial={'language_code': request.user.language_code},
56
40
  instance=request.user
@@ -58,12 +42,13 @@ def user_edit(request):
58
42
  if request.method == 'POST':
59
43
  form = save_form(UserForm(request.POST, instance=request.user))
60
44
  if form.is_saved:
61
- return redirect('user:detail')
62
- ctx = ui.FormContext(
63
- title=_('Preferences'),
64
- form=form,
65
- form_id='form',
66
- actions=ui.form_actions(reverse('user:detail'))
45
+ return redirect(resolve_url('user:detail') + f'?{request.GET.urlencode()}')
46
+ ctx = ui.Context(
47
+ title=_('User Preferences'),
48
+ extra=dict(
49
+ user=request.user,
50
+ form=form
51
+ )
67
52
  ).dict()
68
53
  return render(request, 'user/user_form.html', ctx)
69
54
 
@@ -71,42 +56,50 @@ def user_edit(request):
71
56
  @login_required()
72
57
  def user_change_password(request):
73
58
  form = ChangePasswordForm(instance=request.user)
59
+ ctx = ui.ModalContext(
60
+ title=_('Change Password'),
61
+ modal_id='change-password',
62
+ blocking=True,
63
+ extra=dict(
64
+ form=form,
65
+ user=request.user
66
+ )
67
+ ).dict()
74
68
  if request.method == 'POST':
75
69
  form = save_form(ChangePasswordForm(request.POST, instance=request.user))
76
70
  if form.is_saved:
77
71
  update_session_auth_hash(request, form.instance)
78
- messages.add_message(
79
- request, messages.SUCCESS,
80
- str(_('Your password has been changed.')),
81
- fail_silently=True
72
+ return redirect(
73
+ resolve_url('user:detail')
74
+ + f'?{request.GET.urlencode()}'
82
75
  )
83
- return redirect('user:detail')
84
- ctx = ui.FormContext(
85
- title=_('Change Password'),
86
- form=form,
87
- form_id='form',
88
- actions=ui.form_actions(reverse('user:detail'))
89
- ).dict()
76
+ ctx.update(form=form)
77
+ res = render(request, 'user/change_password.html', ctx)
78
+ res.headers['HX-Retarget'] = f'#{ctx["modal_id"]}'
79
+ res.headers['HX-Reswap'] = 'outerHTML'
80
+ return res
90
81
  return render(request, 'user/change_password.html', ctx)
91
82
 
92
83
 
93
84
  @login_required()
94
85
  def user_change_email(request):
95
86
  form = ChangeEmailForm(instance=request.user)
87
+ ctx = ui.ModalContext(
88
+ title=_('Change E-Mail'),
89
+ modal_id='change-email',
90
+ blocking=True,
91
+ extra=dict(
92
+ form=form,
93
+ user=request.user
94
+ )
95
+ ).dict()
96
96
  if request.method == 'POST':
97
97
  form = save_form(ChangeEmailForm(request.POST, instance=request.user))
98
98
  if form.is_saved:
99
- messages.add_message(
100
- request, messages.SUCCESS,
101
- str(_('Your email address has been changed.')),
102
- fail_silently=True
103
- )
104
99
  return redirect('user:detail')
105
-
106
- ctx = ui.FormContext(
107
- title=_('Change Email'),
108
- form=form,
109
- form_id='form',
110
- actions=ui.form_actions(reverse('user:detail'))
111
- ).dict()
100
+ ctx.update(form=form)
101
+ res = render(request, 'user/change_email.html', ctx)
102
+ res.headers['HX-Retarget'] = f'#{ctx["modal_id"]}'
103
+ res.headers['HX-Reswap'] = 'outerHTML'
104
+ return res
112
105
  return render(request, 'user/change_email.html', ctx)
accrete/forms.py CHANGED
@@ -1,10 +1,8 @@
1
1
  import logging
2
2
  from django import forms
3
- from django.contrib.auth import get_user_model
4
3
  from accrete.tenant import get_tenant
5
4
 
6
5
  _logger = logging.getLogger(__name__)
7
- User = get_user_model()
8
6
 
9
7
 
10
8
  class TenantForm(forms.Form):
accrete/managers.py CHANGED
@@ -1,15 +1,15 @@
1
1
  from django.db import models
2
2
  from accrete.tenant import get_tenant
3
- from accrete.annotation import AnnotationManagerMixin
4
3
 
5
4
 
6
- class TenantManager(models.Manager, AnnotationManagerMixin):
5
+ class TenantManager(models.Manager):
7
6
 
8
7
  def get_queryset(self):
9
8
  queryset = super().get_queryset()
10
- if tenant := get_tenant():
9
+ tenant = get_tenant()
10
+ if tenant:
11
11
  queryset = queryset.filter(tenant=tenant)
12
- return queryset.annotate(**self.get_annotations(queryset))
12
+ return queryset
13
13
 
14
14
  def bulk_create(
15
15
  self,
accrete/middleware.py CHANGED
@@ -1,94 +1,69 @@
1
+ import logging
2
+
1
3
  from django.utils.deprecation import MiddlewareMixin
2
- from django.conf import settings
3
- from django.http.response import HttpResponseRedirect, HttpResponsePermanentRedirect
4
- from accrete.tenant import set_tenant, set_member, unscoped
4
+ from accrete.tenant import set_tenant, set_member, get_tenant
5
+
6
+ from .models import Tenant, Member
5
7
 
6
- from .models import Tenant
8
+ _logger = logging.getLogger(__name__)
7
9
 
8
10
 
9
11
  class TenantMiddleware(MiddlewareMixin):
10
12
  @staticmethod
11
13
  def get_tenant_id_from_request(request):
12
- if tenant := request.POST.get('tenant'):
13
- tenant_id = tenant
14
- elif tenant := request.META.get('X_TENANT_ID'):
15
- tenant_id = tenant
16
- elif tenant_id := request.GET.get('tenant_id'):
17
- tenant_id = tenant_id
18
- elif tenant := request.COOKIES.get('tenant_id'):
19
- tenant_id = tenant
20
- else:
21
- tenant_id = None
22
-
14
+ tenant_id = (
15
+ request.GET.get('tenant_id')
16
+ or request.META.get('HTTP_X_TENANT_ID')
17
+ )
23
18
  try:
24
19
  tenant_id = int(tenant_id)
25
- except (ValueError, TypeError):
20
+ assert tenant_id > 0
21
+ except (ValueError, TypeError, AssertionError):
26
22
  tenant_id = None
27
-
28
23
  return tenant_id
29
24
 
30
25
  def process_request(self, request):
31
26
  request.tenant = None
32
27
  request.member = None
28
+ set_member(False)
33
29
  if request.path.startswith('/admin'):
30
+ return
31
+ if not request.user.is_authenticated:
34
32
  set_member(None)
35
33
  return
34
+ tenant_id = self.get_tenant_id_from_request(request)
35
+ tenant = Tenant.objects.none()
36
+ memberships = Member.objects.filter(
37
+ user=request.user, is_active=True, tenant__is_active=True
38
+ ).prefetch_related('tenant', 'user').order_by('pk')
39
+ if tenant_id:
40
+ tenant = Tenant.objects.get(pk=tenant_id)
41
+ memberships = memberships.filter(tenant=tenant)
42
+ if memberships.count() == 1:
43
+ request.member = memberships.first()
44
+ request.tenant = request.member.tenant
45
+ set_member(request.member)
46
+ self.update_post_data(request)
47
+ return
48
+ if memberships.count() > 1:
49
+ set_member(None)
50
+ return
51
+ if request.user.is_staff and tenant:
52
+ set_member(None)
53
+ set_tenant(tenant)
54
+ request.tenant = tenant
55
+ return
56
+ set_member(None)
57
+ return
36
58
 
37
- if request.user.is_authenticated:
38
- with unscoped():
39
- memberships = request.user.memberships.filter(
40
- is_active=True,
41
- tenant__is_active=True
42
- )
43
-
44
- if request.user.is_staff:
45
- tenant_id = self.get_tenant_id_from_request(request)
46
- if not tenant_id:
47
- tenant_id = memberships and memberships.first().tenant.pk
48
- request.tenant = tenant_id and Tenant.objects.filter(pk=tenant_id).first()
49
- request.member = request.tenant.members.filter(user=request.user).first()
50
-
51
- elif memberships and len(memberships) == 1:
52
- request.member = memberships.first()
53
- request.tenant = request.member.tenant
54
- elif memberships and len(memberships) > 1:
55
- tenant_id = self.get_tenant_id_from_request(request)
56
- if not tenant_id:
57
- tenant_id = memberships.first().tenant.pk
58
- request.member = memberships.filter(tenant_id=tenant_id).first()
59
- request.tenant = request.member.tenant
60
-
61
- set_member(request.member)
62
- if request.user.is_staff:
63
- set_tenant(request.tenant)
64
-
59
+ @staticmethod
60
+ def update_post_data(request):
65
61
  if request.POST and not request.POST.get('tenant') and request.tenant:
66
62
  request.POST = request.POST.copy()
67
63
  request.POST['tenant'] = request.tenant.pk
68
64
 
69
65
  @staticmethod
70
66
  def process_response(request, response):
71
- if request.tenant:
72
- response.set_cookie(
73
- 'tenant_id',
74
- request.tenant.pk,
75
- samesite=settings.SESSION_COOKIE_SAMESITE,
76
- max_age=31536000
77
- )
78
- return response
79
-
80
-
81
- class HtmxRedirectMiddleware(MiddlewareMixin):
82
-
83
- @staticmethod
84
- def process_response(request, response):
85
- is_htmx = request.headers.get('HX-Request', 'false') == 'true'
86
- if not is_htmx:
87
- return response
88
- is_redirect = isinstance(response, HttpResponseRedirect)
89
- is_permanent_redirect = isinstance(response, HttpResponsePermanentRedirect)
90
- if is_redirect or is_permanent_redirect:
91
- response.status_code = 200
92
- header = 'HX-Location' if is_redirect else 'HX-Redirect'
93
- response[header] = response['Location']
67
+ tenant = get_tenant()
68
+ response['X-TENANT-ID'] = tenant and tenant.id or 0
94
69
  return response