From 6a61b79bbec299ea8cecba85df6e209faf89bfe0 Mon Sep 17 00:00:00 2001 From: badbl0cks <4161747+badbl0cks@users.noreply.github.com> Date: Tue, 1 Apr 2025 23:01:05 -0700 Subject: [PATCH] Fix pagination controls, move mixin to common app, fix pagination invocation on all views, and other random bug fixes --- accounts/views.py | 121 +- cards/views.py | 42 +- common/mixins.py | 34 + static/css/base.css | 36 + static/css/choices.min.css | 1 + static/css/hovercards.min.css | 3 + static/js/alpinejs.collapse@3.14.8.min.js | 1 + static/js/alpinejs@3.14.8.min.js | 5 + static/js/choices.min.js | 2 + static/js/floating-ui_core@1.6.9.9.min.js | 1 + static/js/floating-ui_dom@1.6.13.13.min.js | 1 + static/js/hovercards.min.js | 2 + staticfiles/admin/css/autocomplete.css | 260 + staticfiles/admin/css/autocomplete.css.gz | Bin 0 -> 1127 bytes staticfiles/admin/css/base.css | 1179 ++ staticfiles/admin/css/base.css.gz | Bin 0 -> 4939 bytes staticfiles/admin/css/base_tailwind.css | 1 + staticfiles/admin/css/base_tailwind.css.gz | Bin 0 -> 23509 bytes staticfiles/admin/css/changelists.css | 343 + staticfiles/admin/css/changelists.css.gz | Bin 0 -> 1633 bytes staticfiles/admin/css/customized_tailwind.css | 1 + .../admin/css/customized_tailwind.css.gz | Bin 0 -> 4485 bytes staticfiles/admin/css/dark_mode.css | 130 + staticfiles/admin/css/dark_mode.css.gz | Bin 0 -> 790 bytes staticfiles/admin/css/dashboard.css | 29 + staticfiles/admin/css/dashboard.css.gz | Bin 0 -> 267 bytes staticfiles/admin/css/forms.css | 512 + staticfiles/admin/css/forms.css.gz | Bin 0 -> 2216 bytes staticfiles/admin/css/login.css | 79 + staticfiles/admin/css/login.css.gz | Bin 0 -> 477 bytes staticfiles/admin/css/nav_sidebar.css | 150 + staticfiles/admin/css/nav_sidebar.css.gz | Bin 0 -> 811 bytes staticfiles/admin/css/responsive.css | 967 ++ staticfiles/admin/css/responsive.css.gz | Bin 0 -> 3444 bytes staticfiles/admin/css/responsive_rtl.css | 111 + staticfiles/admin/css/responsive_rtl.css.gz | Bin 0 -> 599 bytes staticfiles/admin/css/rtl.css | 291 + staticfiles/admin/css/rtl.css.gz | Bin 0 -> 1240 bytes staticfiles/admin/css/tailwind.css | 1 + staticfiles/admin/css/tailwind.css.gz | Bin 0 -> 27031 bytes staticfiles/admin/css/tom-select.min.css | 2 + staticfiles/admin/css/tom-select.min.css.gz | Bin 0 -> 2005 bytes .../admin/css/unusable_password_field.css | 19 + .../admin/css/unusable_password_field.css.gz | Bin 0 -> 228 bytes .../css/vendor/select2/LICENSE-SELECT2.md | 21 + .../css/vendor/select2/LICENSE-SELECT2.md.gz | Bin 0 -> 685 bytes .../admin/css/vendor/select2/select2.css | 630 + .../admin/css/vendor/select2/select2.css.gz | Bin 0 -> 2068 bytes .../admin/css/vendor/select2/select2.min.css | 1 + .../css/vendor/select2/select2.min.css.gz | Bin 0 -> 1978 bytes staticfiles/admin/css/widgets.css | 593 + staticfiles/admin/css/widgets.css.gz | Bin 0 -> 2422 bytes staticfiles/admin/fields/ckeditor_init.js | 150 + staticfiles/admin/fields/ckeditor_init.js.gz | Bin 0 -> 2218 bytes .../fields/json-editor/json_editor_init.js | 106 + .../fields/json-editor/json_editor_init.js.gz | Bin 0 -> 1368 bytes .../fields/json-editor/jsoneditor.min.js | 8 + .../fields/json-editor/jsoneditor.min.js.gz | Bin 0 -> 102745 bytes .../admin/fields/keyvalue_field/script.js | 47 + .../admin/fields/keyvalue_field/script.js.gz | Bin 0 -> 572 bytes .../admin/fields/keyvalue_field/style.css | 9 + .../admin/fields/keyvalue_field/style.css.gz | Bin 0 -> 132 bytes .../admin/fields/thousand_sep_field.js | 22 + .../admin/fields/thousand_sep_field.js.gz | Bin 0 -> 384 bytes .../admin/fonts/Cantarell/Cantarell-Bold.ttf | 0 .../fonts/Cantarell/Cantarell-Italic.ttf | 0 .../fonts/Cantarell/Cantarell-Regular.ttf | 0 staticfiles/admin/fonts/Cantarell/OFL.txt | 93 + staticfiles/admin/fonts/Cantarell/OFL.txt.gz | Bin 0 -> 1955 bytes staticfiles/admin/fonts/LICENSE.txt | 202 + staticfiles/admin/fonts/LICENSE.txt.gz | Bin 0 -> 3968 bytes staticfiles/admin/fonts/README.txt | 3 + staticfiles/admin/fonts/README.txt.gz | Bin 0 -> 180 bytes .../admin/fonts/Roboto-Bold-webfont.woff | Bin 0 -> 86184 bytes .../admin/fonts/Roboto-Light-webfont.woff | Bin 0 -> 85692 bytes .../admin/fonts/Roboto-Regular-webfont.woff | Bin 0 -> 85876 bytes staticfiles/admin/fonts/Vazir.woff2 | Bin 0 -> 38104 bytes .../admin/fonts/font-awesome-v6.5.2.css | 9 + .../admin/fonts/font-awesome-v6.5.2.css.gz | Bin 0 -> 21964 bytes .../fonts/fontawesome-free-6.6.0-web.zip | Bin 0 -> 6540184 bytes .../fontawesome-free-6.6.0-web/LICENSE.txt | 165 + .../fontawesome-free-6.6.0-web/LICENSE.txt.gz | Bin 0 -> 2872 bytes .../fontawesome-free-6.6.0-web/css/all.css | 7876 +++++++++++ .../fontawesome-free-6.6.0-web/css/all.css.gz | Bin 0 -> 23198 bytes .../css/all.min.css | 9 + .../css/all.min.css.gz | Bin 0 -> 21730 bytes .../fontawesome-free-6.6.0-web/css/brands.css | 1600 +++ .../css/brands.css.gz | Bin 0 -> 5035 bytes .../css/brands.min.css | 6 + .../css/brands.min.css.gz | Bin 0 -> 4870 bytes .../css/fontawesome.css | 6215 +++++++++ .../css/fontawesome.css.gz | Bin 0 -> 17817 bytes .../css/fontawesome.min.css | 9 + .../css/fontawesome.min.css.gz | Bin 0 -> 16656 bytes .../css/regular.css | 19 + .../css/regular.css.gz | Bin 0 -> 343 bytes .../css/regular.min.css | 6 + .../css/regular.min.css.gz | Bin 0 -> 328 bytes .../fontawesome-free-6.6.0-web/css/solid.css | 19 + .../css/solid.css.gz | Bin 0 -> 342 bytes .../css/solid.min.css | 6 + .../css/solid.min.css.gz | Bin 0 -> 325 bytes .../css/svg-with-js.css | 459 + .../css/svg-with-js.css.gz | Bin 0 -> 2315 bytes .../css/svg-with-js.min.css | 6 + .../css/svg-with-js.min.css.gz | Bin 0 -> 2176 bytes .../css/v4-font-face.css | 26 + .../css/v4-font-face.css.gz | Bin 0 -> 653 bytes .../css/v4-font-face.min.css | 6 + .../css/v4-font-face.min.css.gz | Bin 0 -> 633 bytes .../css/v4-shims.css | 2194 ++++ .../css/v4-shims.css.gz | Bin 0 -> 4376 bytes .../css/v4-shims.min.css | 6 + .../css/v4-shims.min.css.gz | Bin 0 -> 4272 bytes .../css/v5-font-face.css | 22 + .../css/v5-font-face.css.gz | Bin 0 -> 317 bytes .../css/v5-font-face.min.css | 6 + .../css/v5-font-face.min.css.gz | Bin 0 -> 309 bytes .../webfonts/fa-brands-400.ttf | Bin 0 -> 209376 bytes .../webfonts/fa-brands-400.ttf.gz | Bin 0 -> 122005 bytes .../webfonts/fa-brands-400.woff2 | Bin 0 -> 118072 bytes .../webfonts/fa-regular-400.ttf | Bin 0 -> 67976 bytes .../webfonts/fa-regular-400.ttf.gz | Bin 0 -> 26691 bytes .../webfonts/fa-regular-400.woff2 | Bin 0 -> 25464 bytes .../webfonts/fa-solid-900.ttf | Bin 0 -> 423676 bytes .../webfonts/fa-solid-900.ttf.gz | Bin 0 -> 171703 bytes .../webfonts/fa-solid-900.woff2 | Bin 0 -> 157192 bytes .../webfonts/fa-v4compatibility.ttf | Bin 0 -> 10836 bytes .../webfonts/fa-v4compatibility.ttf.gz | Bin 0 -> 5054 bytes .../webfonts/fa-v4compatibility.woff2 | Bin 0 -> 4800 bytes staticfiles/admin/img/LICENSE | 20 + staticfiles/admin/img/LICENSE.gz | Bin 0 -> 656 bytes staticfiles/admin/img/README.txt | 7 + staticfiles/admin/img/README.txt.gz | Bin 0 -> 212 bytes staticfiles/admin/img/calendar-icons.svg | 63 + staticfiles/admin/img/calendar-icons.svg.gz | Bin 0 -> 910 bytes staticfiles/admin/img/daisyui-logomark.svg | 6 + staticfiles/admin/img/daisyui-logomark.svg.gz | Bin 0 -> 227 bytes staticfiles/admin/img/gis/move_vertex_off.svg | 1 + .../admin/img/gis/move_vertex_off.svg.gz | Bin 0 -> 470 bytes staticfiles/admin/img/gis/move_vertex_on.svg | 1 + .../admin/img/gis/move_vertex_on.svg.gz | Bin 0 -> 472 bytes staticfiles/admin/img/icon-addlink.svg | 3 + staticfiles/admin/img/icon-addlink.svg.gz | Bin 0 -> 207 bytes staticfiles/admin/img/icon-alert.svg | 3 + staticfiles/admin/img/icon-alert.svg.gz | Bin 0 -> 329 bytes staticfiles/admin/img/icon-calendar.svg | 9 + staticfiles/admin/img/icon-calendar.svg.gz | Bin 0 -> 438 bytes staticfiles/admin/img/icon-changelink.svg | 3 + staticfiles/admin/img/icon-changelink.svg.gz | Bin 0 -> 269 bytes staticfiles/admin/img/icon-clock.svg | 9 + staticfiles/admin/img/icon-clock.svg.gz | Bin 0 -> 357 bytes staticfiles/admin/img/icon-deletelink.svg | 3 + staticfiles/admin/img/icon-deletelink.svg.gz | Bin 0 -> 221 bytes staticfiles/admin/img/icon-hidelink.svg | 3 + staticfiles/admin/img/icon-hidelink.svg.gz | Bin 0 -> 495 bytes staticfiles/admin/img/icon-no.svg | 3 + staticfiles/admin/img/icon-no.svg.gz | Bin 0 -> 297 bytes staticfiles/admin/img/icon-unknown-alt.svg | 3 + staticfiles/admin/img/icon-unknown-alt.svg.gz | Bin 0 -> 377 bytes staticfiles/admin/img/icon-unknown.svg | 3 + staticfiles/admin/img/icon-unknown.svg.gz | Bin 0 -> 377 bytes staticfiles/admin/img/icon-viewlink.svg | 3 + staticfiles/admin/img/icon-viewlink.svg.gz | Bin 0 -> 346 bytes staticfiles/admin/img/icon-yes.svg | 3 + staticfiles/admin/img/icon-yes.svg.gz | Bin 0 -> 266 bytes staticfiles/admin/img/inline-delete.svg | 3 + staticfiles/admin/img/inline-delete.svg.gz | Bin 0 -> 293 bytes staticfiles/admin/img/search.svg | 3 + staticfiles/admin/img/search.svg.gz | Bin 0 -> 264 bytes staticfiles/admin/img/selector-icons.svg | 34 + staticfiles/admin/img/selector-icons.svg.gz | Bin 0 -> 770 bytes staticfiles/admin/img/sorting-icons.svg | 19 + staticfiles/admin/img/sorting-icons.svg.gz | Bin 0 -> 366 bytes staticfiles/admin/img/tooltag-add.svg | 3 + staticfiles/admin/img/tooltag-add.svg.gz | Bin 0 -> 203 bytes staticfiles/admin/img/tooltag-arrowright.svg | 3 + .../admin/img/tooltag-arrowright.svg.gz | Bin 0 -> 194 bytes staticfiles/admin/js/SelectBox.js | 110 + staticfiles/admin/js/SelectBox.js.gz | Bin 0 -> 943 bytes staticfiles/admin/js/SelectFilter2.js | 236 + staticfiles/admin/js/SelectFilter2.js.gz | Bin 0 -> 2638 bytes staticfiles/admin/js/actions.js | 154 + staticfiles/admin/js/actions.js.gz | Bin 0 -> 1611 bytes staticfiles/admin/js/actions.min.js | 7 + staticfiles/admin/js/actions.min.js.gz | Bin 0 -> 1141 bytes .../admin/js/admin/DateTimeShortcuts.js | 417 + .../admin/js/admin/DateTimeShortcuts.js.gz | Bin 0 -> 3740 bytes .../admin/js/admin/RelatedObjectLookups.js | 277 + .../admin/js/admin/RelatedObjectLookups.js.gz | Bin 0 -> 2594 bytes staticfiles/admin/js/admin/dashboard.js | 45 + staticfiles/admin/js/admin/dashboard.js.gz | Bin 0 -> 393 bytes staticfiles/admin/js/autocomplete.js | 46 + staticfiles/admin/js/autocomplete.js.gz | Bin 0 -> 547 bytes staticfiles/admin/js/calendar.js | 207 + staticfiles/admin/js/calendar.js.gz | Bin 0 -> 2088 bytes staticfiles/admin/js/cancel.js | 28 + staticfiles/admin/js/cancel.js.gz | Bin 0 -> 422 bytes staticfiles/admin/js/change_form.js | 17 + staticfiles/admin/js/change_form.js.gz | Bin 0 -> 323 bytes staticfiles/admin/js/collapse.js | 48 + staticfiles/admin/js/collapse.js.gz | Bin 0 -> 623 bytes staticfiles/admin/js/collapse.min.js | 2 + staticfiles/admin/js/collapse.min.js.gz | Bin 0 -> 441 bytes staticfiles/admin/js/core.js | 164 + staticfiles/admin/js/core.js.gz | Bin 0 -> 1482 bytes staticfiles/admin/js/filterbucks.min.js | 2 + staticfiles/admin/js/filterbucks.min.js.gz | Bin 0 -> 3496 bytes staticfiles/admin/js/filters.js | 30 + staticfiles/admin/js/filters.js.gz | Bin 0 -> 502 bytes staticfiles/admin/js/inlines.js | 348 + staticfiles/admin/js/inlines.js.gz | Bin 0 -> 3657 bytes staticfiles/admin/js/inlines.min.js | 11 + staticfiles/admin/js/inlines.min.js.gz | Bin 0 -> 1764 bytes staticfiles/admin/js/jquery.init.js | 36 + staticfiles/admin/js/jquery.init.js.gz | Bin 0 -> 438 bytes staticfiles/admin/js/nav_sidebar.js | 79 + staticfiles/admin/js/nav_sidebar.js.gz | Bin 0 -> 845 bytes staticfiles/admin/js/popup_response.js | 23 + staticfiles/admin/js/popup_response.js.gz | Bin 0 -> 307 bytes staticfiles/admin/js/prepopulate.js | 43 + staticfiles/admin/js/prepopulate.js.gz | Bin 0 -> 536 bytes staticfiles/admin/js/prepopulate.min.js | 1 + staticfiles/admin/js/prepopulate.min.js.gz | Bin 0 -> 257 bytes staticfiles/admin/js/prepopulate_init.js | 11 + staticfiles/admin/js/prepopulate_init.js.gz | Bin 0 -> 267 bytes staticfiles/admin/js/query_filter.js | 97 + staticfiles/admin/js/query_filter.js.gz | Bin 0 -> 948 bytes staticfiles/admin/js/tailwind-3.4.5.js | 62 + staticfiles/admin/js/tailwind-3.4.5.js.gz | Bin 0 -> 108537 bytes staticfiles/admin/js/theme-change.js | 1 + staticfiles/admin/js/theme-change.js.gz | Bin 0 -> 680 bytes staticfiles/admin/js/theme.js | 51 + staticfiles/admin/js/theme.js.gz | Bin 0 -> 564 bytes .../admin/js/tom-select.complete.min.js.js | 440 + .../admin/js/tom-select.complete.min.js.js.gz | Bin 0 -> 17266 bytes .../admin/js/unusable_password_field.js | 29 + .../admin/js/unusable_password_field.js.gz | Bin 0 -> 580 bytes staticfiles/admin/js/urlify.js | 185 + staticfiles/admin/js/urlify.js.gz | Bin 0 -> 2840 bytes .../admin/js/vendor/jquery/LICENSE.txt | 20 + .../admin/js/vendor/jquery/LICENSE.txt.gz | Bin 0 -> 650 bytes staticfiles/admin/js/vendor/jquery/jquery.js | 10872 ++++++++++++++++ .../admin/js/vendor/jquery/jquery.js.gz | Bin 0 -> 84374 bytes .../admin/js/vendor/jquery/jquery.min.js | 2 + .../admin/js/vendor/jquery/jquery.min.js.gz | Bin 0 -> 30879 bytes .../admin/js/vendor/select2/LICENSE.md | 21 + .../admin/js/vendor/select2/LICENSE.md.gz | Bin 0 -> 685 bytes .../admin/js/vendor/select2/i18n/af.js | 3 + .../admin/js/vendor/select2/i18n/af.js.gz | Bin 0 -> 460 bytes .../admin/js/vendor/select2/i18n/ar.js | 3 + .../admin/js/vendor/select2/i18n/ar.js.gz | Bin 0 -> 498 bytes .../admin/js/vendor/select2/i18n/az.js | 3 + .../admin/js/vendor/select2/i18n/az.js.gz | Bin 0 -> 413 bytes .../admin/js/vendor/select2/i18n/bg.js | 3 + .../admin/js/vendor/select2/i18n/bg.js.gz | Bin 0 -> 541 bytes .../admin/js/vendor/select2/i18n/bn.js | 3 + .../admin/js/vendor/select2/i18n/bn.js.gz | Bin 0 -> 553 bytes .../admin/js/vendor/select2/i18n/bs.js | 3 + .../admin/js/vendor/select2/i18n/bs.js.gz | Bin 0 -> 523 bytes .../admin/js/vendor/select2/i18n/ca.js | 3 + .../admin/js/vendor/select2/i18n/ca.js.gz | Bin 0 -> 470 bytes .../admin/js/vendor/select2/i18n/cs.js | 3 + .../admin/js/vendor/select2/i18n/cs.js.gz | Bin 0 -> 623 bytes .../admin/js/vendor/select2/i18n/da.js | 3 + .../admin/js/vendor/select2/i18n/da.js.gz | Bin 0 -> 441 bytes .../admin/js/vendor/select2/i18n/de.js | 3 + .../admin/js/vendor/select2/i18n/de.js.gz | Bin 0 -> 467 bytes .../admin/js/vendor/select2/i18n/dsb.js | 3 + .../admin/js/vendor/select2/i18n/dsb.js.gz | Bin 0 -> 551 bytes .../admin/js/vendor/select2/i18n/el.js | 3 + .../admin/js/vendor/select2/i18n/el.js.gz | Bin 0 -> 644 bytes .../admin/js/vendor/select2/i18n/en.js | 3 + .../admin/js/vendor/select2/i18n/en.js.gz | Bin 0 -> 447 bytes .../admin/js/vendor/select2/i18n/es.js | 3 + .../admin/js/vendor/select2/i18n/es.js.gz | Bin 0 -> 474 bytes .../admin/js/vendor/select2/i18n/et.js | 3 + .../admin/js/vendor/select2/i18n/et.js.gz | Bin 0 -> 432 bytes .../admin/js/vendor/select2/i18n/eu.js | 3 + .../admin/js/vendor/select2/i18n/eu.js.gz | Bin 0 -> 450 bytes .../admin/js/vendor/select2/i18n/fa.js | 3 + .../admin/js/vendor/select2/i18n/fa.js.gz | Bin 0 -> 538 bytes .../admin/js/vendor/select2/i18n/fi.js | 3 + .../admin/js/vendor/select2/i18n/fi.js.gz | Bin 0 -> 429 bytes .../admin/js/vendor/select2/i18n/fr.js | 3 + .../admin/js/vendor/select2/i18n/fr.js.gz | Bin 0 -> 484 bytes .../admin/js/vendor/select2/i18n/gl.js | 3 + .../admin/js/vendor/select2/i18n/gl.js.gz | Bin 0 -> 465 bytes .../admin/js/vendor/select2/i18n/he.js | 3 + .../admin/js/vendor/select2/i18n/he.js.gz | Bin 0 -> 518 bytes .../admin/js/vendor/select2/i18n/hi.js | 3 + .../admin/js/vendor/select2/i18n/hi.js.gz | Bin 0 -> 572 bytes .../admin/js/vendor/select2/i18n/hr.js | 3 + .../admin/js/vendor/select2/i18n/hr.js.gz | Bin 0 -> 477 bytes .../admin/js/vendor/select2/i18n/hsb.js | 3 + .../admin/js/vendor/select2/i18n/hsb.js.gz | Bin 0 -> 556 bytes .../admin/js/vendor/select2/i18n/hu.js | 3 + .../admin/js/vendor/select2/i18n/hu.js.gz | Bin 0 -> 467 bytes .../admin/js/vendor/select2/i18n/hy.js | 3 + .../admin/js/vendor/select2/i18n/hy.js.gz | Bin 0 -> 530 bytes .../admin/js/vendor/select2/i18n/id.js | 3 + .../admin/js/vendor/select2/i18n/id.js.gz | Bin 0 -> 416 bytes .../admin/js/vendor/select2/i18n/is.js | 3 + .../admin/js/vendor/select2/i18n/is.js.gz | Bin 0 -> 465 bytes .../admin/js/vendor/select2/i18n/it.js | 3 + .../admin/js/vendor/select2/i18n/it.js.gz | Bin 0 -> 488 bytes .../admin/js/vendor/select2/i18n/ja.js | 3 + .../admin/js/vendor/select2/i18n/ja.js.gz | Bin 0 -> 511 bytes .../admin/js/vendor/select2/i18n/ka.js | 3 + .../admin/js/vendor/select2/i18n/ka.js.gz | Bin 0 -> 533 bytes .../admin/js/vendor/select2/i18n/km.js | 3 + .../admin/js/vendor/select2/i18n/km.js.gz | Bin 0 -> 540 bytes .../admin/js/vendor/select2/i18n/ko.js | 3 + .../admin/js/vendor/select2/i18n/ko.js.gz | Bin 0 -> 506 bytes .../admin/js/vendor/select2/i18n/lt.js | 3 + .../admin/js/vendor/select2/i18n/lt.js.gz | Bin 0 -> 521 bytes .../admin/js/vendor/select2/i18n/lv.js | 3 + .../admin/js/vendor/select2/i18n/lv.js.gz | Bin 0 -> 505 bytes .../admin/js/vendor/select2/i18n/mk.js | 3 + .../admin/js/vendor/select2/i18n/mk.js.gz | Bin 0 -> 557 bytes .../admin/js/vendor/select2/i18n/ms.js | 3 + .../admin/js/vendor/select2/i18n/ms.js.gz | Bin 0 -> 436 bytes .../admin/js/vendor/select2/i18n/nb.js | 3 + .../admin/js/vendor/select2/i18n/nb.js.gz | Bin 0 -> 413 bytes .../admin/js/vendor/select2/i18n/ne.js | 3 + .../admin/js/vendor/select2/i18n/ne.js.gz | Bin 0 -> 591 bytes .../admin/js/vendor/select2/i18n/nl.js | 3 + .../admin/js/vendor/select2/i18n/nl.js.gz | Bin 0 -> 469 bytes .../admin/js/vendor/select2/i18n/pl.js | 3 + .../admin/js/vendor/select2/i18n/pl.js.gz | Bin 0 -> 524 bytes .../admin/js/vendor/select2/i18n/ps.js | 3 + .../admin/js/vendor/select2/i18n/ps.js.gz | Bin 0 -> 587 bytes .../admin/js/vendor/select2/i18n/pt-BR.js | 3 + .../admin/js/vendor/select2/i18n/pt-BR.js.gz | Bin 0 -> 486 bytes .../admin/js/vendor/select2/i18n/pt.js | 3 + .../admin/js/vendor/select2/i18n/pt.js.gz | Bin 0 -> 470 bytes .../admin/js/vendor/select2/i18n/ro.js | 3 + .../admin/js/vendor/select2/i18n/ro.js.gz | Bin 0 -> 511 bytes .../admin/js/vendor/select2/i18n/ru.js | 3 + .../admin/js/vendor/select2/i18n/ru.js.gz | Bin 0 -> 632 bytes .../admin/js/vendor/select2/i18n/sk.js | 3 + .../admin/js/vendor/select2/i18n/sk.js.gz | Bin 0 -> 617 bytes .../admin/js/vendor/select2/i18n/sl.js | 3 + .../admin/js/vendor/select2/i18n/sl.js.gz | Bin 0 -> 487 bytes .../admin/js/vendor/select2/i18n/sq.js | 3 + .../admin/js/vendor/select2/i18n/sq.js.gz | Bin 0 -> 490 bytes .../admin/js/vendor/select2/i18n/sr-Cyrl.js | 3 + .../js/vendor/select2/i18n/sr-Cyrl.js.gz | Bin 0 -> 608 bytes .../admin/js/vendor/select2/i18n/sr.js | 3 + .../admin/js/vendor/select2/i18n/sr.js.gz | Bin 0 -> 552 bytes .../admin/js/vendor/select2/i18n/sv.js | 3 + .../admin/js/vendor/select2/i18n/sv.js.gz | Bin 0 -> 429 bytes .../admin/js/vendor/select2/i18n/th.js | 3 + .../admin/js/vendor/select2/i18n/th.js.gz | Bin 0 -> 515 bytes .../admin/js/vendor/select2/i18n/tk.js | 3 + .../admin/js/vendor/select2/i18n/tk.js.gz | Bin 0 -> 434 bytes .../admin/js/vendor/select2/i18n/tr.js | 3 + .../admin/js/vendor/select2/i18n/tr.js.gz | Bin 0 -> 423 bytes .../admin/js/vendor/select2/i18n/uk.js | 3 + .../admin/js/vendor/select2/i18n/uk.js.gz | Bin 0 -> 626 bytes .../admin/js/vendor/select2/i18n/vi.js | 3 + .../admin/js/vendor/select2/i18n/vi.js.gz | Bin 0 -> 479 bytes .../admin/js/vendor/select2/i18n/zh-CN.js | 3 + .../admin/js/vendor/select2/i18n/zh-CN.js.gz | Bin 0 -> 468 bytes .../admin/js/vendor/select2/i18n/zh-TW.js | 3 + .../admin/js/vendor/select2/i18n/zh-TW.js.gz | Bin 0 -> 451 bytes .../admin/js/vendor/select2/select2.full.js | 6820 ++++++++++ .../js/vendor/select2/select2.full.js.gz | Bin 0 -> 37925 bytes .../js/vendor/select2/select2.full.min.js | 2 + .../js/vendor/select2/select2.full.min.js.gz | Bin 0 -> 21986 bytes .../admin/js/vendor/xregexp/LICENSE.txt | 21 + .../admin/js/vendor/xregexp/LICENSE.txt.gz | Bin 0 -> 679 bytes .../admin/js/vendor/xregexp/xregexp.js | 4652 +++++++ .../admin/js/vendor/xregexp/xregexp.js.gz | Bin 0 -> 60899 bytes .../admin/js/vendor/xregexp/xregexp.min.js | 160 + .../admin/js/vendor/xregexp/xregexp.min.js.gz | Bin 0 -> 37609 bytes staticfiles/css/base.css | 57 + staticfiles/css/base.css.gz | Bin 0 -> 493 bytes staticfiles/css/choices.min.css | 1 + staticfiles/css/choices.min.css.gz | Bin 0 -> 1926 bytes staticfiles/css/hovercards.min.css | 3 + staticfiles/css/hovercards.min.css.gz | Bin 0 -> 1393 bytes staticfiles/css/hovercards.style.css | 3 + staticfiles/css/hovercards.style.css.gz | Bin 0 -> 1393 bytes staticfiles/el-pagination/js/el-pagination.js | 133 + .../el-pagination/js/el-pagination.js.gz | Bin 0 -> 1421 bytes staticfiles/images/favicon.ico | Bin 0 -> 549 bytes staticfiles/images/favicon.ico.gz | Bin 0 -> 212 bytes staticfiles/images/logo.png | Bin 0 -> 37370 bytes .../js/alpinejs.collapse@3.14.8.min.js | 1 + .../js/alpinejs.collapse@3.14.8.min.js.gz | Bin 0 -> 668 bytes staticfiles/js/alpinejs@3.14.8.min.js | 5 + staticfiles/js/alpinejs@3.14.8.min.js.gz | Bin 0 -> 16210 bytes staticfiles/js/base.js | 146 + staticfiles/js/base.js.gz | Bin 0 -> 1747 bytes staticfiles/js/choices.min.js | 2 + staticfiles/js/choices.min.js.gz | Bin 0 -> 20331 bytes staticfiles/js/choices.min.js.js | 2 + staticfiles/js/choices.min.js.js.gz | Bin 0 -> 20331 bytes staticfiles/js/floating-ui_core@1.6.9.9.js | 1 + staticfiles/js/floating-ui_core@1.6.9.9.js.gz | Bin 0 -> 4673 bytes .../js/floating-ui_core@1.6.9.9.min.js | 1 + .../js/floating-ui_core@1.6.9.9.min.js.gz | Bin 0 -> 4673 bytes staticfiles/js/floating-ui_dom@1.6.13.13.js | 1 + .../js/floating-ui_dom@1.6.13.13.js.gz | Bin 0 -> 3969 bytes .../js/floating-ui_dom@1.6.13.13.min.js | 1 + .../js/floating-ui_dom@1.6.13.13.min.js.gz | Bin 0 -> 3969 bytes staticfiles/js/hovercards.min.js | 2 + staticfiles/js/hovercards.min.js.gz | Bin 0 -> 5570 bytes staticfiles/js/tooltip.js | 123 + staticfiles/js/tooltip.js.gz | Bin 0 -> 1574 bytes theme/templates/account/dashboard.html | 155 +- theme/templates/base.html | 32 +- theme/templates/cards/_trade_offer_list.html | 2 +- .../cards/_trade_offer_want_list.html | 27 - theme/templates/cards/card_detail.html | 2 +- theme/templates/home/home.html | 4 +- theme/templates/trades/_trade_offer_list.html | 2 +- .../trades/trade_acceptance_update.html | 22 +- .../trades/trade_offer_all_list.html | 4 +- theme/templatetags/pagination_controls.html | 65 +- theme/templatetags/trade_acceptance.html | 2 +- theme/templatetags/trade_offer.html | 2 +- trades/urls.py | 4 +- trades/views.py | 56 +- 425 files changed, 51656 insertions(+), 243 deletions(-) create mode 100644 common/mixins.py create mode 100644 static/css/choices.min.css create mode 100644 static/css/hovercards.min.css create mode 100644 static/js/alpinejs.collapse@3.14.8.min.js create mode 100644 static/js/alpinejs@3.14.8.min.js create mode 100644 static/js/choices.min.js create mode 100644 static/js/floating-ui_core@1.6.9.9.min.js create mode 100644 static/js/floating-ui_dom@1.6.13.13.min.js create mode 100644 static/js/hovercards.min.js create mode 100644 staticfiles/admin/css/autocomplete.css create mode 100644 staticfiles/admin/css/autocomplete.css.gz create mode 100644 staticfiles/admin/css/base.css create mode 100644 staticfiles/admin/css/base.css.gz create mode 100644 staticfiles/admin/css/base_tailwind.css create mode 100644 staticfiles/admin/css/base_tailwind.css.gz create mode 100644 staticfiles/admin/css/changelists.css create mode 100644 staticfiles/admin/css/changelists.css.gz create mode 100644 staticfiles/admin/css/customized_tailwind.css create mode 100644 staticfiles/admin/css/customized_tailwind.css.gz create mode 100644 staticfiles/admin/css/dark_mode.css create mode 100644 staticfiles/admin/css/dark_mode.css.gz create mode 100644 staticfiles/admin/css/dashboard.css create mode 100644 staticfiles/admin/css/dashboard.css.gz create mode 100644 staticfiles/admin/css/forms.css create mode 100644 staticfiles/admin/css/forms.css.gz create mode 100644 staticfiles/admin/css/login.css create mode 100644 staticfiles/admin/css/login.css.gz create mode 100644 staticfiles/admin/css/nav_sidebar.css create mode 100644 staticfiles/admin/css/nav_sidebar.css.gz create mode 100644 staticfiles/admin/css/responsive.css create mode 100644 staticfiles/admin/css/responsive.css.gz create mode 100644 staticfiles/admin/css/responsive_rtl.css create mode 100644 staticfiles/admin/css/responsive_rtl.css.gz create mode 100644 staticfiles/admin/css/rtl.css create mode 100644 staticfiles/admin/css/rtl.css.gz create mode 100644 staticfiles/admin/css/tailwind.css create mode 100644 staticfiles/admin/css/tailwind.css.gz create mode 100644 staticfiles/admin/css/tom-select.min.css create mode 100644 staticfiles/admin/css/tom-select.min.css.gz create mode 100644 staticfiles/admin/css/unusable_password_field.css create mode 100644 staticfiles/admin/css/unusable_password_field.css.gz create mode 100644 staticfiles/admin/css/vendor/select2/LICENSE-SELECT2.md create mode 100644 staticfiles/admin/css/vendor/select2/LICENSE-SELECT2.md.gz create mode 100644 staticfiles/admin/css/vendor/select2/select2.css create mode 100644 staticfiles/admin/css/vendor/select2/select2.css.gz create mode 100644 staticfiles/admin/css/vendor/select2/select2.min.css create mode 100644 staticfiles/admin/css/vendor/select2/select2.min.css.gz create mode 100644 staticfiles/admin/css/widgets.css create mode 100644 staticfiles/admin/css/widgets.css.gz create mode 100644 staticfiles/admin/fields/ckeditor_init.js create mode 100644 staticfiles/admin/fields/ckeditor_init.js.gz create mode 100644 staticfiles/admin/fields/json-editor/json_editor_init.js create mode 100644 staticfiles/admin/fields/json-editor/json_editor_init.js.gz create mode 100644 staticfiles/admin/fields/json-editor/jsoneditor.min.js create mode 100644 staticfiles/admin/fields/json-editor/jsoneditor.min.js.gz create mode 100644 staticfiles/admin/fields/keyvalue_field/script.js create mode 100644 staticfiles/admin/fields/keyvalue_field/script.js.gz create mode 100644 staticfiles/admin/fields/keyvalue_field/style.css create mode 100644 staticfiles/admin/fields/keyvalue_field/style.css.gz create mode 100644 staticfiles/admin/fields/thousand_sep_field.js create mode 100644 staticfiles/admin/fields/thousand_sep_field.js.gz create mode 100644 staticfiles/admin/fonts/Cantarell/Cantarell-Bold.ttf create mode 100644 staticfiles/admin/fonts/Cantarell/Cantarell-Italic.ttf create mode 100644 staticfiles/admin/fonts/Cantarell/Cantarell-Regular.ttf create mode 100644 staticfiles/admin/fonts/Cantarell/OFL.txt create mode 100644 staticfiles/admin/fonts/Cantarell/OFL.txt.gz create mode 100644 staticfiles/admin/fonts/LICENSE.txt create mode 100644 staticfiles/admin/fonts/LICENSE.txt.gz create mode 100644 staticfiles/admin/fonts/README.txt create mode 100644 staticfiles/admin/fonts/README.txt.gz create mode 100644 staticfiles/admin/fonts/Roboto-Bold-webfont.woff create mode 100644 staticfiles/admin/fonts/Roboto-Light-webfont.woff create mode 100644 staticfiles/admin/fonts/Roboto-Regular-webfont.woff create mode 100644 staticfiles/admin/fonts/Vazir.woff2 create mode 100644 staticfiles/admin/fonts/font-awesome-v6.5.2.css create mode 100644 staticfiles/admin/fonts/font-awesome-v6.5.2.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web.zip create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/LICENSE.txt create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/LICENSE.txt.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/all.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/all.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/all.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/all.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/brands.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/brands.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/brands.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/brands.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/fontawesome.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/fontawesome.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/fontawesome.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/fontawesome.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/regular.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/regular.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/regular.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/regular.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/solid.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/solid.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/solid.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/solid.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/svg-with-js.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/svg-with-js.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/svg-with-js.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/svg-with-js.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-font-face.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-font-face.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-font-face.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-font-face.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-shims.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-shims.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-shims.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v4-shims.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v5-font-face.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v5-font-face.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v5-font-face.min.css create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/css/v5-font-face.min.css.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-brands-400.ttf create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-brands-400.ttf.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-brands-400.woff2 create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-regular-400.ttf create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-regular-400.ttf.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-regular-400.woff2 create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-solid-900.ttf create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-solid-900.ttf.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-solid-900.woff2 create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-v4compatibility.ttf create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-v4compatibility.ttf.gz create mode 100644 staticfiles/admin/fonts/fontawesome-free-6.6.0-web/webfonts/fa-v4compatibility.woff2 create mode 100644 staticfiles/admin/img/LICENSE create mode 100644 staticfiles/admin/img/LICENSE.gz create mode 100644 staticfiles/admin/img/README.txt create mode 100644 staticfiles/admin/img/README.txt.gz create mode 100644 staticfiles/admin/img/calendar-icons.svg create mode 100644 staticfiles/admin/img/calendar-icons.svg.gz create mode 100644 staticfiles/admin/img/daisyui-logomark.svg create mode 100644 staticfiles/admin/img/daisyui-logomark.svg.gz create mode 100644 staticfiles/admin/img/gis/move_vertex_off.svg create mode 100644 staticfiles/admin/img/gis/move_vertex_off.svg.gz create mode 100644 staticfiles/admin/img/gis/move_vertex_on.svg create mode 100644 staticfiles/admin/img/gis/move_vertex_on.svg.gz create mode 100644 staticfiles/admin/img/icon-addlink.svg create mode 100644 staticfiles/admin/img/icon-addlink.svg.gz create mode 100644 staticfiles/admin/img/icon-alert.svg create mode 100644 staticfiles/admin/img/icon-alert.svg.gz create mode 100644 staticfiles/admin/img/icon-calendar.svg create mode 100644 staticfiles/admin/img/icon-calendar.svg.gz create mode 100644 staticfiles/admin/img/icon-changelink.svg create mode 100644 staticfiles/admin/img/icon-changelink.svg.gz create mode 100644 staticfiles/admin/img/icon-clock.svg create mode 100644 staticfiles/admin/img/icon-clock.svg.gz create mode 100644 staticfiles/admin/img/icon-deletelink.svg create mode 100644 staticfiles/admin/img/icon-deletelink.svg.gz create mode 100644 staticfiles/admin/img/icon-hidelink.svg create mode 100644 staticfiles/admin/img/icon-hidelink.svg.gz create mode 100644 staticfiles/admin/img/icon-no.svg create mode 100644 staticfiles/admin/img/icon-no.svg.gz create mode 100644 staticfiles/admin/img/icon-unknown-alt.svg create mode 100644 staticfiles/admin/img/icon-unknown-alt.svg.gz create mode 100644 staticfiles/admin/img/icon-unknown.svg create mode 100644 staticfiles/admin/img/icon-unknown.svg.gz create mode 100644 staticfiles/admin/img/icon-viewlink.svg create mode 100644 staticfiles/admin/img/icon-viewlink.svg.gz create mode 100644 staticfiles/admin/img/icon-yes.svg create mode 100644 staticfiles/admin/img/icon-yes.svg.gz create mode 100644 staticfiles/admin/img/inline-delete.svg create mode 100644 staticfiles/admin/img/inline-delete.svg.gz create mode 100644 staticfiles/admin/img/search.svg create mode 100644 staticfiles/admin/img/search.svg.gz create mode 100644 staticfiles/admin/img/selector-icons.svg create mode 100644 staticfiles/admin/img/selector-icons.svg.gz create mode 100644 staticfiles/admin/img/sorting-icons.svg create mode 100644 staticfiles/admin/img/sorting-icons.svg.gz create mode 100644 staticfiles/admin/img/tooltag-add.svg create mode 100644 staticfiles/admin/img/tooltag-add.svg.gz create mode 100644 staticfiles/admin/img/tooltag-arrowright.svg create mode 100644 staticfiles/admin/img/tooltag-arrowright.svg.gz create mode 100644 staticfiles/admin/js/SelectBox.js create mode 100644 staticfiles/admin/js/SelectBox.js.gz create mode 100644 staticfiles/admin/js/SelectFilter2.js create mode 100644 staticfiles/admin/js/SelectFilter2.js.gz create mode 100644 staticfiles/admin/js/actions.js create mode 100644 staticfiles/admin/js/actions.js.gz create mode 100644 staticfiles/admin/js/actions.min.js create mode 100644 staticfiles/admin/js/actions.min.js.gz create mode 100644 staticfiles/admin/js/admin/DateTimeShortcuts.js create mode 100644 staticfiles/admin/js/admin/DateTimeShortcuts.js.gz create mode 100644 staticfiles/admin/js/admin/RelatedObjectLookups.js create mode 100644 staticfiles/admin/js/admin/RelatedObjectLookups.js.gz create mode 100644 staticfiles/admin/js/admin/dashboard.js create mode 100644 staticfiles/admin/js/admin/dashboard.js.gz create mode 100644 staticfiles/admin/js/autocomplete.js create mode 100644 staticfiles/admin/js/autocomplete.js.gz create mode 100644 staticfiles/admin/js/calendar.js create mode 100644 staticfiles/admin/js/calendar.js.gz create mode 100644 staticfiles/admin/js/cancel.js create mode 100644 staticfiles/admin/js/cancel.js.gz create mode 100644 staticfiles/admin/js/change_form.js create mode 100644 staticfiles/admin/js/change_form.js.gz create mode 100644 staticfiles/admin/js/collapse.js create mode 100644 staticfiles/admin/js/collapse.js.gz create mode 100644 staticfiles/admin/js/collapse.min.js create mode 100644 staticfiles/admin/js/collapse.min.js.gz create mode 100644 staticfiles/admin/js/core.js create mode 100644 staticfiles/admin/js/core.js.gz create mode 100644 staticfiles/admin/js/filterbucks.min.js create mode 100644 staticfiles/admin/js/filterbucks.min.js.gz create mode 100644 staticfiles/admin/js/filters.js create mode 100644 staticfiles/admin/js/filters.js.gz create mode 100644 staticfiles/admin/js/inlines.js create mode 100644 staticfiles/admin/js/inlines.js.gz create mode 100644 staticfiles/admin/js/inlines.min.js create mode 100644 staticfiles/admin/js/inlines.min.js.gz create mode 100644 staticfiles/admin/js/jquery.init.js create mode 100644 staticfiles/admin/js/jquery.init.js.gz create mode 100644 staticfiles/admin/js/nav_sidebar.js create mode 100644 staticfiles/admin/js/nav_sidebar.js.gz create mode 100644 staticfiles/admin/js/popup_response.js create mode 100644 staticfiles/admin/js/popup_response.js.gz create mode 100644 staticfiles/admin/js/prepopulate.js create mode 100644 staticfiles/admin/js/prepopulate.js.gz create mode 100644 staticfiles/admin/js/prepopulate.min.js create mode 100644 staticfiles/admin/js/prepopulate.min.js.gz create mode 100644 staticfiles/admin/js/prepopulate_init.js create mode 100644 staticfiles/admin/js/prepopulate_init.js.gz create mode 100644 staticfiles/admin/js/query_filter.js create mode 100644 staticfiles/admin/js/query_filter.js.gz create mode 100644 staticfiles/admin/js/tailwind-3.4.5.js create mode 100644 staticfiles/admin/js/tailwind-3.4.5.js.gz create mode 100644 staticfiles/admin/js/theme-change.js create mode 100644 staticfiles/admin/js/theme-change.js.gz create mode 100644 staticfiles/admin/js/theme.js create mode 100644 staticfiles/admin/js/theme.js.gz create mode 100644 staticfiles/admin/js/tom-select.complete.min.js.js create mode 100644 staticfiles/admin/js/tom-select.complete.min.js.js.gz create mode 100644 staticfiles/admin/js/unusable_password_field.js create mode 100644 staticfiles/admin/js/unusable_password_field.js.gz create mode 100644 staticfiles/admin/js/urlify.js create mode 100644 staticfiles/admin/js/urlify.js.gz create mode 100644 staticfiles/admin/js/vendor/jquery/LICENSE.txt create mode 100644 staticfiles/admin/js/vendor/jquery/LICENSE.txt.gz create mode 100644 staticfiles/admin/js/vendor/jquery/jquery.js create mode 100644 staticfiles/admin/js/vendor/jquery/jquery.js.gz create mode 100644 staticfiles/admin/js/vendor/jquery/jquery.min.js create mode 100644 staticfiles/admin/js/vendor/jquery/jquery.min.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/LICENSE.md create mode 100644 staticfiles/admin/js/vendor/select2/LICENSE.md.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/af.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/af.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ar.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ar.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/az.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/az.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bg.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bg.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bn.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bn.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bs.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bs.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ca.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ca.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/cs.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/cs.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/da.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/da.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/de.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/de.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/dsb.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/dsb.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/el.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/el.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/en.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/en.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/es.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/es.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/et.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/et.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/eu.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/eu.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fa.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fa.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fi.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fi.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fr.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/gl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/gl.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/he.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/he.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hi.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hi.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hr.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hsb.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hsb.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hu.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hu.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hy.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hy.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/id.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/id.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/is.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/is.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/it.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/it.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ja.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ja.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ka.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ka.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/km.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/km.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ko.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ko.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/lt.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/lt.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/lv.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/lv.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/mk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/mk.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ms.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ms.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/nb.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/nb.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ne.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ne.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/nl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/nl.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pl.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ps.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ps.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pt-BR.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pt-BR.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pt.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pt.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ro.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ro.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ru.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ru.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sk.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sl.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sq.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sq.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sr-Cyrl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sr-Cyrl.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sr.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sv.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sv.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/th.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/th.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/tk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/tk.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/tr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/tr.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/uk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/uk.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/vi.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/vi.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/zh-CN.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/zh-CN.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/i18n/zh-TW.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/zh-TW.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/select2.full.js create mode 100644 staticfiles/admin/js/vendor/select2/select2.full.js.gz create mode 100644 staticfiles/admin/js/vendor/select2/select2.full.min.js create mode 100644 staticfiles/admin/js/vendor/select2/select2.full.min.js.gz create mode 100644 staticfiles/admin/js/vendor/xregexp/LICENSE.txt create mode 100644 staticfiles/admin/js/vendor/xregexp/LICENSE.txt.gz create mode 100644 staticfiles/admin/js/vendor/xregexp/xregexp.js create mode 100644 staticfiles/admin/js/vendor/xregexp/xregexp.js.gz create mode 100644 staticfiles/admin/js/vendor/xregexp/xregexp.min.js create mode 100644 staticfiles/admin/js/vendor/xregexp/xregexp.min.js.gz create mode 100644 staticfiles/css/base.css create mode 100644 staticfiles/css/base.css.gz create mode 100644 staticfiles/css/choices.min.css create mode 100644 staticfiles/css/choices.min.css.gz create mode 100644 staticfiles/css/hovercards.min.css create mode 100644 staticfiles/css/hovercards.min.css.gz create mode 100644 staticfiles/css/hovercards.style.css create mode 100644 staticfiles/css/hovercards.style.css.gz create mode 100644 staticfiles/el-pagination/js/el-pagination.js create mode 100644 staticfiles/el-pagination/js/el-pagination.js.gz create mode 100644 staticfiles/images/favicon.ico create mode 100644 staticfiles/images/favicon.ico.gz create mode 100644 staticfiles/images/logo.png create mode 100644 staticfiles/js/alpinejs.collapse@3.14.8.min.js create mode 100644 staticfiles/js/alpinejs.collapse@3.14.8.min.js.gz create mode 100644 staticfiles/js/alpinejs@3.14.8.min.js create mode 100644 staticfiles/js/alpinejs@3.14.8.min.js.gz create mode 100644 staticfiles/js/base.js create mode 100644 staticfiles/js/base.js.gz create mode 100644 staticfiles/js/choices.min.js create mode 100644 staticfiles/js/choices.min.js.gz create mode 100644 staticfiles/js/choices.min.js.js create mode 100644 staticfiles/js/choices.min.js.js.gz create mode 100644 staticfiles/js/floating-ui_core@1.6.9.9.js create mode 100644 staticfiles/js/floating-ui_core@1.6.9.9.js.gz create mode 100644 staticfiles/js/floating-ui_core@1.6.9.9.min.js create mode 100644 staticfiles/js/floating-ui_core@1.6.9.9.min.js.gz create mode 100644 staticfiles/js/floating-ui_dom@1.6.13.13.js create mode 100644 staticfiles/js/floating-ui_dom@1.6.13.13.js.gz create mode 100644 staticfiles/js/floating-ui_dom@1.6.13.13.min.js create mode 100644 staticfiles/js/floating-ui_dom@1.6.13.13.min.js.gz create mode 100644 staticfiles/js/hovercards.min.js create mode 100644 staticfiles/js/hovercards.min.js.gz create mode 100644 staticfiles/js/tooltip.js create mode 100644 staticfiles/js/tooltip.js.gz delete mode 100644 theme/templates/cards/_trade_offer_want_list.html diff --git a/accounts/views.py b/accounts/views.py index 3fb1fc5..b37162a 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,7 +1,7 @@ from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse_lazy -from django.shortcuts import redirect, get_object_or_404 +from django.shortcuts import redirect, get_object_or_404, render from django.views.generic import ListView, CreateView, DeleteView, View, TemplateView, UpdateView from accounts.models import FriendCode, CustomUser from accounts.forms import FriendCodeForm, UserSettingsForm @@ -9,6 +9,7 @@ from django.db.models import Case, When, Value, BooleanField from trades.models import TradeOffer, TradeAcceptance from django.core.exceptions import PermissionDenied from trades.mixins import FriendCodeRequiredMixin +from common.mixins import ReusablePaginationMixin class ListFriendCodesView(LoginRequiredMixin, ListView): """ @@ -150,7 +151,7 @@ class EditFriendCodeView(LoginRequiredMixin, UpdateView): messages.success(self.request, "Friend code updated successfully.") return super().form_valid(form) -class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): +class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, ReusablePaginationMixin, TemplateView): template_name = "account/dashboard.html" def post(self, request, *args, **kwargs): @@ -179,10 +180,10 @@ class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): return selected_friend_code def get_dashboard_offers_paginated(self, page_param): - from django.core.paginator import Paginator selected_friend_code = self.get_selected_friend_code() queryset = TradeOffer.objects.filter(initiated_by=selected_friend_code, is_closed=False) - return Paginator(queryset, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(queryset, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_involved_acceptances(self, selected_friend_code): from django.db.models import Q @@ -209,8 +210,8 @@ class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): ]) | Q(accepted_by=selected_friend_code, state__in=[TradeAcceptance.AcceptanceState.SENT]) ) - from django.core.paginator import Paginator - return Paginator(waiting, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(waiting, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_other_party_trade_acceptances_paginated(self, page_param): selected_friend_code = self.get_selected_friend_code() @@ -224,18 +225,17 @@ class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): Q(accepted_by=selected_friend_code, state__in=[TradeAcceptance.AcceptanceState.SENT]) ) others = involved.exclude(pk__in=waiting.values("pk")) - from django.core.paginator import Paginator - return Paginator(others, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(others, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_closed_offers_paginated(self, page_param): - from django.core.paginator import Paginator selected_friend_code = self.get_selected_friend_code() queryset = TradeOffer.objects.filter(initiated_by=selected_friend_code, is_closed=True) - return Paginator(queryset, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(queryset, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_closed_acceptances_paginated(self, page_param): from django.db.models import Q - from django.core.paginator import Paginator selected_friend_code = self.get_selected_friend_code() terminal_success_states = [ TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR, @@ -246,41 +246,83 @@ class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): Q(trade_offer__initiated_by=selected_friend_code) | Q(accepted_by=selected_friend_code), state__in=terminal_success_states ).order_by("-updated_at") - return Paginator(acceptance_qs, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(acceptance_qs, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_rejected_by_me_paginated(self, page_param): from django.db.models import Q - from django.core.paginator import Paginator selected_friend_code = self.get_selected_friend_code() rejection = TradeAcceptance.objects.filter( Q(trade_offer__initiated_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR) | Q(accepted_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR) ).order_by("-updated_at") - return Paginator(rejection, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(rejection, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_rejected_by_them_paginated(self, page_param): from django.db.models import Q - from django.core.paginator import Paginator selected_friend_code = self.get_selected_friend_code() rejection = TradeAcceptance.objects.filter( Q(trade_offer__initiated_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR) | Q(accepted_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR) ).order_by("-updated_at") - return Paginator(rejection, 10).get_page(page_param) + object_list, pagination_context = self.paginate_data(rejection, int(page_param)) + return {"object_list": object_list, "page_obj": pagination_context} def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) request = self.request selected_friend_code = self.get_selected_friend_code() context["selected_friend_code"] = selected_friend_code - context["friend_codes"] = request.user.friend_codes.all() - offers_page = request.GET.get("offers_page", 1) - waiting_page = request.GET.get("waiting_page", 1) - other_page = request.GET.get("other_page", 1) - closed_offers_page = request.GET.get("closed_offers_page", 1) - closed_acceptances_page = request.GET.get("closed_acceptances_page", 1) - rejected_by_me_page = request.GET.get("rejected_by_me_page", 1) - rejected_by_them_page = request.GET.get("rejected_by_them_page", 1) + + # Get the default friend code's primary key if it exists + default_pk = getattr(request.user.default_friend_code, "pk", None) + + # Annotate friend codes with is_default flag + context["friend_codes"] = request.user.friend_codes.all().annotate( + is_default=Case( + When(pk=default_pk, then=Value(True)), + default=Value(False), + output_field=BooleanField() + ) + ) + + ajax_section = request.GET.get("ajax_section") + if ajax_section == "dashboard_offers": + offers_page = request.GET.get("page", 1) + else: + offers_page = request.GET.get("offers_page", 1) + + if ajax_section == "waiting_acceptances": + waiting_page = request.GET.get("page", 1) + else: + waiting_page = request.GET.get("waiting_page", 1) + + if ajax_section == "other_party_acceptances": + other_page = request.GET.get("page", 1) + else: + other_page = request.GET.get("other_page", 1) + + if ajax_section == "closed_offers": + closed_offers_page = request.GET.get("page", 1) + else: + closed_offers_page = request.GET.get("closed_offers_page", 1) + + if ajax_section == "closed_acceptances": + closed_acceptances_page = request.GET.get("page", 1) + else: + closed_acceptances_page = request.GET.get("closed_acceptances_page", 1) + + if ajax_section == "rejected_by_me": + rejected_by_me_page = request.GET.get("page", 1) + else: + rejected_by_me_page = request.GET.get("rejected_by_me_page", 1) + + if ajax_section == "rejected_by_them": + rejected_by_them_page = request.GET.get("page", 1) + else: + rejected_by_them_page = request.GET.get("rejected_by_them_page", 1) + context["dashboard_offers_paginated"] = self.get_dashboard_offers_paginated(offers_page) context["trade_acceptances_waiting_paginated"] = self.get_trade_acceptances_waiting_paginated(waiting_page) context["other_party_trade_acceptances_paginated"] = self.get_other_party_trade_acceptances_paginated(other_page) @@ -291,4 +333,33 @@ class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): from accounts.forms import UserSettingsForm context["settings_form"] = UserSettingsForm(instance=request.user) context["active_tab"] = request.GET.get("tab", "dash") - return context \ No newline at end of file + return context + + # Handle AJAX requests to return only the trade offer list fragment + def get(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + ajax_section = request.GET.get("ajax_section") + if request.headers.get("X-Requested-With") == "XMLHttpRequest" and ajax_section: + if ajax_section == "dashboard_offers": + fragment_context = context.get("dashboard_offers_paginated", {}) + elif ajax_section == "waiting_acceptances": + fragment_context = context.get("trade_acceptances_waiting_paginated", {}) + elif ajax_section == "other_party_acceptances": + fragment_context = context.get("other_party_trade_acceptances_paginated", {}) + elif ajax_section == "closed_offers": + fragment_context = context.get("closed_offers_paginated", {}) + elif ajax_section == "closed_acceptances": + fragment_context = context.get("closed_acceptances_paginated", {}) + elif ajax_section == "rejected_by_me": + fragment_context = context.get("rejected_by_me_paginated", {}) + elif ajax_section == "rejected_by_them": + fragment_context = context.get("rejected_by_them_paginated", {}) + else: + fragment_context = {} + + if fragment_context: + return render(request, "trades/_trade_offer_list.html", { + "offers": fragment_context.get("object_list", []), + "page_obj": fragment_context.get("page_obj") + }) + return super().get(request, *args, **kwargs) \ No newline at end of file diff --git a/cards/views.py b/cards/views.py index 46e49fe..e98652f 100644 --- a/cards/views.py +++ b/cards/views.py @@ -3,7 +3,7 @@ from django.urls import reverse_lazy from django.views.generic import UpdateView, DeleteView, CreateView, ListView, DetailView from cards.models import Card from trades.models import TradeOffer -from cards.mixins import ReusablePaginationMixin +from common.mixins import ReusablePaginationMixin class CardDetailView(DetailView): model = Card @@ -65,7 +65,7 @@ class TradeOfferWantCardListView(ListView): class CardListView(ReusablePaginationMixin, ListView): model = Card - paginate_by = 36 # For non-grouped mode; grouping mode will override default pagination. + # Removed built-in pagination; using custom mixin instead context_object_name = "cards" def get_template_names(self): @@ -88,13 +88,6 @@ class CardListView(ReusablePaginationMixin, ListView): qs = qs.order_by(ordering) return qs.prefetch_related("decks").distinct() - def get_paginate_by(self, queryset): - group_by = self.request.GET.get("group_by") - if group_by in ("deck", "cardset", "rarity"): - # When grouping is enabled, we want to paginate manually so disable default pagination. - return None - return self.paginate_by - def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) order = self.request.GET.get("order", "absolute") @@ -106,7 +99,6 @@ class CardListView(ReusablePaginationMixin, ListView): full_qs = self.get_queryset() all_cards = list(full_qs) flat_cards = [] - if group_by == "deck": for card in all_cards: for deck in card.decks.all(): @@ -121,16 +113,10 @@ class CardListView(ReusablePaginationMixin, ListView): flat_cards.append({"group": card.rarity_icon, "sort_group": card.rarity_level, "card": card}) flat_cards.sort(key=lambda x: x["sort_group"], reverse=True) - try: - page_number = int(self.request.GET.get("page", 1)) - except ValueError: - page_number = 1 - - # Use our custom mixin logic here + page_number = self.get_page_number() self.per_page = 36 page_flat_cards, pagination_context = self.paginate_data(flat_cards, page_number) - # Reassemble the flat list into groups for the current page. page_groups = [] for item in page_flat_cards: group_value = item["group"] @@ -143,19 +129,11 @@ class CardListView(ReusablePaginationMixin, ListView): context["page_obj"] = pagination_context context["total_cards"] = len(flat_cards) context["object_list"] = full_qs - return context else: - # For non-grouped mode, transform the built-in paginator page - if "page_obj" in context: - page = context["page_obj"] - # Create a unified pagination context dict with updated keys - custom_page_obj = { - "number": page.number, - "has_previous": page.has_previous(), - "has_next": page.has_next(), - "previous_page_number": page.previous_page_number() if page.has_previous() else 1, - "next_page_number": page.next_page_number() if page.has_next() else page.paginator.num_pages, - "paginator": {"num_pages": page.paginator.num_pages}, - } - context["page_obj"] = custom_page_obj - return context \ No newline at end of file + page_number = self.get_page_number() + self.per_page = 36 + paginated_cards, pagination_context = self.paginate_data(self.get_queryset(), page_number) + context["cards"] = paginated_cards + context["page_obj"] = pagination_context + context["object_list"] = self.get_queryset() + return context \ No newline at end of file diff --git a/common/mixins.py b/common/mixins.py new file mode 100644 index 0000000..6290fdc --- /dev/null +++ b/common/mixins.py @@ -0,0 +1,34 @@ +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger + + +class ReusablePaginationMixin: + per_page = 10 + + def get_page_number(self): + try: + return int(self.request.GET.get("page", 1)) + except (ValueError, TypeError): + return 1 + + def paginate_data(self, data, page_number): + """ + Paginates data (a QuerySet or list) and returns a tuple: (page_data, pagination_context). + """ + paginator = Paginator(data, self.per_page) + try: + page = paginator.page(page_number) + except PageNotAnInteger: + page = paginator.page(1) + except EmptyPage: + page = paginator.page(paginator.num_pages) + + pagination_context = { + "number": page.number, + "has_previous": page.has_previous(), + "has_next": page.has_next(), + "previous_page_number": page.previous_page_number() if page.has_previous() else 1, + "next_page_number": page.next_page_number() if page.has_next() else paginator.num_pages, + "paginator": {"num_pages": paginator.num_pages}, + "count": paginator.count + } + return page.object_list, pagination_context \ No newline at end of file diff --git a/static/css/base.css b/static/css/base.css index 6511b28..ce81a47 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -54,4 +54,40 @@ select.card-multiselect { } .choices__input { background-color: var(--color-base-100); +} + +.gravatar-hovercard .gravatar-hovercard__inner { + background-color: var(--color-base-100) !important; + border-color: var(--color-base-300) !important; + color: var(--color-base-content) !important; +} + +.gravatar-hovercard .gravatar-hovercard__inner, +.gravatar-hovercard .gravatar-hovercard__header-image, +.gravatar-hovercard .gravatar-hovercard__header, +.gravatar-hovercard .gravatar-hovercard__avatar-link, +.gravatar-hovercard .gravatar-hovercard__avatar, +.gravatar-hovercard .gravatar-hovercard__personal-info-plink, +.gravatar-hovercard .gravatar-hovercard__name, +.gravatar-hovercard .gravatar-hovercard__job, +.gravatar-hovercard .gravatar-hovercard__location, +.gravatar-hovercard .gravatar-hovercard__body, +.gravatar-hovercard .gravatar-hovercard__description, +.gravatar-hovercard .gravatar-hovercard__social-links, +.gravatar-hovercard .gravatar-hovercard__buttons, +.gravatar-hovercard .gravatar-hovercard__button, +.gravatar-hovercard .gravatar-hovercard__button:hover, +.gravatar-hovercard .gravatar-hovercard__footer, +.gravatar-hovercard .gravatar-hovercard__profile-url, +.gravatar-hovercard .gravatar-hovercard__profile-link, +.gravatar-hovercard .gravatar-hovercard__profile-color { + color: var(--color-base-content) !important; +} + +.gravatar-hovercard .gravatar-hovercard__location { + color: var(--color-base-content) !important; +} + +.dark .gravatar-hovercard .gravatar-hovercard__social-icon { + filter: invert(1) !important; } \ No newline at end of file diff --git a/static/css/choices.min.css b/static/css/choices.min.css new file mode 100644 index 0000000..cf79ea9 --- /dev/null +++ b/static/css/choices.min.css @@ -0,0 +1 @@ +.choices{position:relative;overflow:hidden;margin-bottom:24px;font-size:16px}.choices:focus{outline:0}.choices:last-child{margin-bottom:0}.choices.is-open{overflow:visible}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices [hidden]{display:none!important}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.25}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #005f75}.choices[data-type*=select-one] .choices__item[data-placeholder] .choices__button{display:none}.choices[data-type*=select-one]::after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open::after{border-color:transparent transparent #333;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]::after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0-4px 0 8px;padding-left:16px;border-left:1px solid #003642;background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);background-size:8px;width:8px;line-height:1;opacity:.75;border-radius:0}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#005f75;border:1px solid #004a5c;color:#fff;word-break:break-all;box-sizing:border-box}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#004a5c;border:1px solid #003642}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown,.choices__list[aria-expanded]{display:none;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all}.is-active.choices__list--dropdown,.is-active.choices__list[aria-expanded]{display:block}.is-open .choices__list--dropdown,.is-open .choices__list[aria-expanded]{border-color:#b7b7b7}.is-flipped .choices__list--dropdown,.is-flipped .choices__list[aria-expanded]{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list,.choices__list[aria-expanded] .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item,.choices__list[aria-expanded] .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item,[dir=rtl] .choices__list[aria-expanded] .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable[data-select-text],.choices__list[aria-expanded] .choices__item--selectable[data-select-text]{padding-right:100px}.choices__list--dropdown .choices__item--selectable[data-select-text]::after,.choices__list[aria-expanded] .choices__item--selectable[data-select-text]::after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable[data-select-text],[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable[data-select-text]{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable[data-select-text]::after,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable[data-select-text]::after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted::after,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus,.choices__input:focus{outline:0}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input::-webkit-search-cancel-button,.choices__input::-webkit-search-decoration,.choices__input::-webkit-search-results-button,.choices__input::-webkit-search-results-decoration{display:none}.choices__input::-ms-clear,.choices__input::-ms-reveal{display:none;width:0;height:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5} \ No newline at end of file diff --git a/static/css/hovercards.min.css b/static/css/hovercards.min.css new file mode 100644 index 0000000..37bc2a4 --- /dev/null +++ b/static/css/hovercards.min.css @@ -0,0 +1,3 @@ +.gravatar-hovercard{display:inline-block;z-index:10000000}.gravatar-hovercard h4,.gravatar-hovercard p{margin:0}.gravatar-hovercard p,.gravatar-hovercard i,.gravatar-hovercard a,.gravatar-hovercard span{font-family:"SF Pro Text",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;line-height:1.5;color:#000}.gravatar-hovercard .gravatar-hovercard__inner{display:flex;flex-direction:column;justify-content:space-between;width:336px;min-height:273px;position:relative;padding:24px 24px 16px;background-color:#fff;border:1px solid #d8dbdd;border-radius:4px;box-shadow:0 2px 6px rgba(0,0,0,.08);box-sizing:border-box;overflow:hidden}.gravatar-hovercard .gravatar-hovercard__header-image{position:absolute;top:0;left:50%;height:75px;width:100%;transform:translateX(-50%)}.gravatar-hovercard .gravatar-hovercard__header{z-index:1;display:flex;flex-direction:column;gap:8px}.gravatar-hovercard .gravatar-hovercard__avatar-link,.gravatar-hovercard .gravatar-hovercard__social-link{display:inline-flex}.gravatar-hovercard .gravatar-hovercard__avatar{border-radius:50%;background-color:#eee}.gravatar-hovercard .gravatar-hovercard__personal-info-link{text-decoration:none}.gravatar-hovercard .gravatar-hovercard__name{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;word-break:break-word;font-family:"SF Pro Text",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:32px;font-weight:700;line-height:38px;color:#000}.gravatar-hovercard .gravatar-hovercard__job,.gravatar-hovercard .gravatar-hovercard__location{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;word-break:break-word;color:#707070}.gravatar-hovercard .gravatar-hovercard__body{min-height:42px;margin-top:8px}.gravatar-hovercard .gravatar-hovercard__description{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;word-break:break-word}.gravatar-hovercard .gravatar-hovercard__social-links{display:flex;align-items:center;gap:4px;margin-top:16px}.gravatar-hovercard .gravatar-hovercard__buttons{display:flex;gap:16px;margin-top:16px}.gravatar-hovercard .gravatar-hovercard__button{width:100%;min-height:42px;padding:8px;background:unset;border-radius:4px;border:1px solid rgba(29,79,196,.3);font-family:"SF Pro Text",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:15px;font-weight:600;line-height:21px;color:#1d4fc4;cursor:pointer}.gravatar-hovercard .gravatar-hovercard__button:hover{border:1px solid rgba(29,79,196,.6)}.gravatar-hovercard .gravatar-hovercard__footer{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-top:12px}.gravatar-hovercard .gravatar-hovercard__profile-url{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;word-break:break-word;word-break:break-all;color:#707070;text-decoration:none}.gravatar-hovercard .gravatar-hovercard__profile-link{text-decoration:none;flex-shrink:0;color:#707070}.gravatar-hovercard .gravatar-hovercard__profile-color{position:absolute;bottom:0;left:0;width:100%;height:4px}.gravatar-hovercard--skeleton .gravatar-hovercard__avatar-link,.gravatar-hovercard--skeleton .gravatar-hovercard__personal-info-link,.gravatar-hovercard--skeleton .gravatar-hovercard__social-link,.gravatar-hovercard--skeleton .gravatar-hovercard__profile-link,.gravatar-hovercard--skeleton .gravatar-hovercard__profile-url{background-color:#eee}.gravatar-hovercard--skeleton .gravatar-hovercard__avatar-link{width:104px;height:104px;border-radius:50%}.gravatar-hovercard--skeleton .gravatar-hovercard__personal-info-link{height:38px;width:70%}.gravatar-hovercard--skeleton .gravatar-hovercard__social-link{width:32px;height:32px;border-radius:50%}.gravatar-hovercard--skeleton .gravatar-hovercard__profile-url{width:50%;height:21px}.gravatar-hovercard--skeleton .gravatar-hovercard__profile-link{height:21px;width:96px}.gravatar-hovercard--error .gravatar-hovercard__inner{align-items:center;justify-content:center;gap:34px}.gravatar-hovercard--error .gravatar-hovercard__error-message{color:#707070}.gravatar-hovercard__drawer{position:absolute;top:0;left:0;bottom:0;right:0;visibility:hidden;overflow:hidden;z-index:1}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-backdrop{position:absolute;top:0;left:0;bottom:0;right:0;background-color:rgba(0,0,0,.4);opacity:0;transition:opacity .3s ease-in-out}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-card{position:absolute;left:0;bottom:0;width:100%;max-height:100%;display:flex;flex-direction:column;background-color:#fff;border-top-left-radius:4px;border-top-right-radius:4px;padding:20px 0;box-sizing:border-box;transform:translate3d(0, 100%, 0);transition:transform .3s ease-in-out}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-header{display:flex;align-items:center;justify-content:space-between;padding:0 20px;margin-bottom:16px}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-title{font-family:"SF Pro Text",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:18px;font-weight:700;line-height:27px;margin:0}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-close{display:flex;align-items:center;justify-content:center;border:none;background:none;padding:0;cursor:pointer}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-items{display:flex;flex-direction:column;list-style:none;margin:0;padding:0 20px;gap:12px;overflow:auto}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-item{display:flex;gap:8px}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-item-info{display:flex;flex-direction:column}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-item-label{font-weight:600;line-height:24px;text-transform:capitalize}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-item-link{color:#1d4fc4;text-decoration:none}.gravatar-hovercard__drawer .gravatar-hovercard__drawer-item-link:hover{text-decoration:underline}.gravatar-hovercard__drawer--open{visibility:visible}.gravatar-hovercard__drawer--open .gravatar-hovercard__drawer-backdrop{opacity:1}.gravatar-hovercard__drawer--open .gravatar-hovercard__drawer-card{transform:translate3d(0, 0, 0)}.gravatar-hovercard__drawer--closing{visibility:visible}.gravatar-hovercard__drawer--closing .gravatar-hovercard__drawer-backdrop{opacity:0}.gravatar-hovercard__drawer--closing .gravatar-hovercard__drawer-card{transform:translate3d(0, 100%, 0)} + +/*# sourceMappingURL=style.css.map*/ \ No newline at end of file diff --git a/static/js/alpinejs.collapse@3.14.8.min.js b/static/js/alpinejs.collapse@3.14.8.min.js new file mode 100644 index 0000000..a865343 --- /dev/null +++ b/static/js/alpinejs.collapse@3.14.8.min.js @@ -0,0 +1 @@ +(()=>{function g(n){n.directive("collapse",e),e.inline=(t,{modifiers:i})=>{i.includes("min")&&(t._x_doShow=()=>{},t._x_doHide=()=>{})};function e(t,{modifiers:i}){let r=l(i,"duration",250)/1e3,h=l(i,"min",0),u=!i.includes("min");t._x_isShown||(t.style.height=`${h}px`),!t._x_isShown&&u&&(t.hidden=!0),t._x_isShown||(t.style.overflow="hidden");let c=(d,s)=>{let o=n.setStyles(d,s);return s.height?()=>{}:o},f={transitionProperty:"height",transitionDuration:`${r}s`,transitionTimingFunction:"cubic-bezier(0.4, 0.0, 0.2, 1)"};t._x_transition={in(d=()=>{},s=()=>{}){u&&(t.hidden=!1),u&&(t.style.display=null);let o=t.getBoundingClientRect().height;t.style.height="auto";let a=t.getBoundingClientRect().height;o===a&&(o=h),n.transition(t,n.setStyles,{during:f,start:{height:o+"px"},end:{height:a+"px"}},()=>t._x_isShown=!0,()=>{Math.abs(t.getBoundingClientRect().height-a)<1&&(t.style.overflow=null)})},out(d=()=>{},s=()=>{}){let o=t.getBoundingClientRect().height;n.transition(t,c,{during:f,start:{height:o+"px"},end:{height:h+"px"}},()=>t.style.overflow="hidden",()=>{t._x_isShown=!1,t.style.height==`${h}px`&&u&&(t.style.display="none",t.hidden=!0)})}}}}function l(n,e,t){if(n.indexOf(e)===-1)return t;let i=n[n.indexOf(e)+1];if(!i)return t;if(e==="duration"){let r=i.match(/([0-9]+)ms/);if(r)return r[1]}if(e==="min"){let r=i.match(/([0-9]+)px/);if(r)return r[1]}return i}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(g)});})(); diff --git a/static/js/alpinejs@3.14.8.min.js b/static/js/alpinejs@3.14.8.min.js new file mode 100644 index 0000000..a3be81c --- /dev/null +++ b/static/js/alpinejs@3.14.8.min.js @@ -0,0 +1,5 @@ +(()=>{var nt=!1,it=!1,W=[],ot=-1;function Ut(e){Rn(e)}function Rn(e){W.includes(e)||W.push(e),Mn()}function Wt(e){let t=W.indexOf(e);t!==-1&&t>ot&&W.splice(t,1)}function Mn(){!it&&!nt&&(nt=!0,queueMicrotask(Nn))}function Nn(){nt=!1,it=!0;for(let e=0;ee.effect(t,{scheduler:r=>{st?Ut(r):r()}}),at=e.raw}function ct(e){N=e}function Yt(e){let t=()=>{};return[n=>{let i=N(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}function ve(e,t){let r=!0,n,i=N(()=>{let o=e();JSON.stringify(o),r?n=o:queueMicrotask(()=>{t(o,n),n=o}),r=!1});return()=>$(i)}var Xt=[],Zt=[],Qt=[];function er(e){Qt.push(e)}function te(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Zt.push(t))}function Ae(e){Xt.push(e)}function Oe(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function lt(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}function tr(e){for(e._x_effects?.forEach(Wt);e._x_cleanups?.length;)e._x_cleanups.pop()()}var ut=new MutationObserver(mt),ft=!1;function ue(){ut.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),ft=!0}function dt(){kn(),ut.disconnect(),ft=!1}var le=[];function kn(){let e=ut.takeRecords();le.push(()=>e.length>0&&mt(e));let t=le.length;queueMicrotask(()=>{if(le.length===t)for(;le.length>0;)le.shift()()})}function m(e){if(!ft)return e();dt();let t=e();return ue(),t}var pt=!1,Se=[];function rr(){pt=!0}function nr(){pt=!1,mt(Se),Se=[]}function mt(e){if(pt){Se=Se.concat(e);return}let t=[],r=new Set,n=new Map,i=new Map;for(let o=0;o{s.nodeType===1&&s._x_marker&&r.add(s)}),e[o].addedNodes.forEach(s=>{if(s.nodeType===1){if(r.has(s)){r.delete(s);return}s._x_marker||t.push(s)}})),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{lt(s,o)}),n.forEach((o,s)=>{Xt.forEach(a=>a(s,o))});for(let o of r)t.some(s=>s.contains(o))||Zt.forEach(s=>s(o));for(let o of t)o.isConnected&&Qt.forEach(s=>s(o));t=null,r=null,n=null,i=null}function Ce(e){return z(B(e))}function k(e,t,r){return e._x_dataStack=[t,...B(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function B(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?B(e.host):e.parentNode?B(e.parentNode):[]}function z(e){return new Proxy({objects:e},Dn)}var Dn={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(r=>Object.prototype.hasOwnProperty.call(r,t)||Reflect.has(r,t))},get({objects:e},t,r){return t=="toJSON"?Pn:Reflect.get(e.find(n=>Reflect.has(n,t))||{},t,r)},set({objects:e},t,r,n){let i=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?o.set.call(n,r)||!0:Reflect.set(i,t,r)}};function Pn(){return Reflect.ownKeys(this).reduce((t,r)=>(t[r]=Reflect.get(this,r),t),{})}function Te(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Re(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>In(n,i),s=>ht(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function In(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function ht(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),ht(e[t[0]],t.slice(1),r)}}var ir={};function y(e,t){ir[e]=t}function fe(e,t){let r=Ln(t);return Object.entries(ir).forEach(([n,i])=>{Object.defineProperty(e,`$${n}`,{get(){return i(t,r)},enumerable:!1})}),e}function Ln(e){let[t,r]=_t(e),n={interceptor:Re,...t};return te(e,r),n}function or(e,t,r,...n){try{return r(...n)}catch(i){re(i,e,t)}}function re(e,t,r=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message} + +${r?'Expression: "'+r+`" + +`:""}`,t),setTimeout(()=>{throw e},0)}var Me=!0;function ke(e){let t=Me;Me=!1;let r=e();return Me=t,r}function R(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return sr(...e)}var sr=xt;function ar(e){sr=e}function xt(e,t){let r={};fe(r,e);let n=[r,...B(e)],i=typeof t=="function"?$n(n,t):Fn(n,t,e);return or.bind(null,e,t,i)}function $n(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(z([n,...e]),i);Ne(r,o)}}var gt={};function jn(e,t){if(gt[e])return gt[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let s=new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return re(s,t,e),Promise.resolve()}})();return gt[e]=o,o}function Fn(e,t,r){let n=jn(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=z([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>re(l,r,t));n.finished?(Ne(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Ne(i,l,a,s,r)}).catch(l=>re(l,r,t)).finally(()=>n.result=void 0)}}}function Ne(e,t,r,n,i){if(Me&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Ne(e,s,r,n)).catch(s=>re(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var wt="x-";function C(e=""){return wt+e}function cr(e){wt=e}var De={};function d(e,t){return De[e]=t,{before(r){if(!De[r]){console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`);return}let n=G.indexOf(r);G.splice(n>=0?n:G.indexOf("DEFAULT"),0,e)}}}function lr(e){return Object.keys(De).includes(e)}function pe(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=Et(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(dr((o,s)=>n[o]=s)).filter(mr).map(zn(n,r)).sort(Kn).map(o=>Bn(e,o))}function Et(e){return Array.from(e).map(dr()).filter(t=>!mr(t))}var yt=!1,de=new Map,ur=Symbol();function fr(e){yt=!0;let t=Symbol();ur=t,de.set(t,[]);let r=()=>{for(;de.get(t).length;)de.get(t).shift()();de.delete(t)},n=()=>{yt=!1,r()};e(r),n()}function _t(e){let t=[],r=a=>t.push(a),[n,i]=Yt(e);return t.push(i),[{Alpine:K,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:R.bind(R,e)},()=>t.forEach(a=>a())]}function Bn(e,t){let r=()=>{},n=De[t.type]||r,[i,o]=_t(e);Oe(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),yt?de.get(ur).push(n):n())};return s.runCleanups=o,s}var Pe=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),Ie=e=>e;function dr(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=pr.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var pr=[];function ne(e){pr.push(e)}function mr({name:e}){return hr().test(e)}var hr=()=>new RegExp(`^${wt}([^:^.]+)\\b`);function zn(e,t){return({name:r,value:n})=>{let i=r.match(hr()),o=r.match(/:([a-zA-Z0-9\-_:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var bt="DEFAULT",G=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",bt,"teleport"];function Kn(e,t){let r=G.indexOf(e.type)===-1?bt:e.type,n=G.indexOf(t.type)===-1?bt:t.type;return G.indexOf(r)-G.indexOf(n)}function J(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function D(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>D(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)D(n,t,!1),n=n.nextElementSibling}function E(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var _r=!1;function gr(){_r&&E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),_r=!0,document.body||E("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` + */ + +const { computePosition, offset, flip, shift, arrow } = FloatingUIDOM; + +document.addEventListener('DOMContentLoaded', () => { + document.querySelectorAll('[data-tooltip-html]').forEach((el) => { + let tooltipContainer = null; + let arrowElement = null; + let fadeOutTimeout; + + const showTooltip = () => { + if (tooltipContainer) return; // Tooltip already visible + + // Retrieve the custom HTML content from the data attribute + const tooltipContent = el.getAttribute('data-tooltip-html'); + + // Create a container for the tooltip (with modern styling) + tooltipContainer = document.createElement('div'); + tooltipContainer.classList.add( + 'bg-black', 'text-white', + 'shadow-lg', 'rounded-lg', 'p-2', + // Transition classes for simple fade in/out + 'transition-opacity', 'duration-200', 'opacity-0' + ); + tooltipContainer.style.position = 'absolute'; + tooltipContainer.style.zIndex = '9999'; + + // Set the HTML content for the tooltip + tooltipContainer.innerHTML = '
' + tooltipContent + '
'; + + // Create the arrow element. The arrow is styled as a small rotated square. + arrowElement = document.createElement('div'); + arrowElement.classList.add( + 'w-3', 'h-3', + 'bg-black', + 'transform', 'rotate-45' + ); + arrowElement.style.position = 'absolute'; + + // Append the arrow into the tooltip container + tooltipContainer.appendChild(arrowElement); + + // Append the tooltip container to the document body + document.body.appendChild(tooltipContainer); + + // Use Floating UI to position the tooltip, including the arrow middleware + computePosition(el, tooltipContainer, { + middleware: [ + offset(8), + flip(), + shift({ padding: 5 }), + arrow({ element: arrowElement }) + ] + }).then(({ x, y, placement, middlewareData }) => { + Object.assign(tooltipContainer.style, { + left: `${x}px`, + top: `${y}px` + }); + + // Position the arrow using the arrow middleware data + const { x: arrowX, y: arrowY } = middlewareData.arrow || {}; + + // Reset any previous inline values + arrowElement.style.left = ''; + arrowElement.style.top = ''; + arrowElement.style.right = ''; + arrowElement.style.bottom = ''; + + // Adjust the arrow's position according to the placement + if (placement.startsWith('top')) { + arrowElement.style.bottom = '-4px'; + arrowElement.style.left = arrowX !== undefined ? `${arrowX}px` : '50%'; + } else if (placement.startsWith('bottom')) { + arrowElement.style.top = '-4px'; + arrowElement.style.left = arrowX !== undefined ? `${arrowX}px` : '50%'; + } else if (placement.startsWith('left')) { + arrowElement.style.right = '-4px'; + arrowElement.style.top = arrowY !== undefined ? `${arrowY}px` : '50%'; + } else if (placement.startsWith('right')) { + arrowElement.style.left = '-4px'; + arrowElement.style.top = arrowY !== undefined ? `${arrowY}px` : '50%'; + } + }); + + // Trigger a fade-in by moving from opacity-0 to opacity-100 + requestAnimationFrame(() => { + tooltipContainer.classList.remove('opacity-0'); + tooltipContainer.classList.add('opacity-100'); + }); + }; + + const hideTooltip = () => { + if (tooltipContainer) { + tooltipContainer.classList.remove('opacity-100'); + tooltipContainer.classList.add('opacity-0'); + // Remove the tooltip from the DOM after the transition duration + fadeOutTimeout = setTimeout(() => { + if (tooltipContainer && tooltipContainer.parentNode) { + tooltipContainer.parentNode.removeChild(tooltipContainer); + } + tooltipContainer = null; + arrowElement = null; + }, 200); // Matches the duration-200 class (200ms) + } + }; + + // Attach event listeners to show/hide the tooltip + el.addEventListener('mouseenter', showTooltip); + el.addEventListener('mouseleave', hideTooltip); + el.addEventListener('focus', showTooltip); + el.addEventListener('blur', hideTooltip); + }); +}); \ No newline at end of file diff --git a/staticfiles/js/tooltip.js.gz b/staticfiles/js/tooltip.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..d135c596053ee3b1a18d5eee0686d4f6d5a9f6a3 GIT binary patch literal 1574 zcmV+>2HE)^iwFP!00002|HW8&Z`4K<|9?NlYt&*dcz06@Qk#&pQ2-T*6hwkrgpioE zXV*i<9y2rEqru<3Zw_DC-LOTf8i~XnzvDOWeoXG)AHaRkLU7HB@h63E=HM!23REmv zp`lb%!E-K%X8HW>3nYeEQlcqjBB3&mK`zx&c&2)KwFUo!Xi^n4I4Z+23V1-;52H~u^iT-lrSXGTbFZOm&!Abuz$Adv4;?g*6Op@f;0v}#7rD;xO7$Cv9$hA2WB%w(V zK{$kyQ@9xbG<=^+D%Y3j`dABj#(5aL-*E7UU^vDAc}C(i3@LAf0C1{nzZ+eIp=Lyx zmz*Cr1Qz`>7hD@3X9(?8sjpZ@MXB*d=7Y12O2zWZ2}icDqZWY8AiS-32$Jej=EpFZ zK+Q-v27a=J1yc-tJ@EWU@rGhZ(}fEXALhSbUA#2fjn!Z#@x**I25B?EOK=!F7~JhB zAJ3^itBJ#4!NcSuwE5^V*CrPFf9NG_F@ED(m-6252!t(+ z>g{}Eo0cYcOeBS#3Lu!yM^jGXYmA;i)0G}AQ>JMUwG=8PiCB*K+>l67<``@Ktr$J* zP#UjDlDkN+>wvwjVkYlwEMU$}Fc4btx?vfKg2YU(jpU@1#GXAoJT&kA)95hpoV#yo zOVhZn9cZwCOqJm1tl%p=f4{)HtqjL6_!D&9cxe&=ebx`4z9V-DXF2}1j41gDy2;t* zE}n^}6cbsXQ)~eWGqQIU1AJ0C9c<;u=Gb8W{8@S3>^VS@V+?(-_ydMhf(LyG`O&LVw+uf z0|mwlB0GY|Q1u8bOeFAuebkT9YS@r|@K4onjMvLUl-E^}VLi+gq&8jL^AG^4>)oUUwbkJmSWu zZ62~cso}1tiIq3WUwB#n_|A?E{mYJ>v@c)ars`vOLzQXRInGv+E|@44Mhf;D3JbzZ zGqu6aoHO?~`PNj?tyE}>uMDF+8YSVzkv5+qEUHUaK z(MT}EMRSU}h}7y&?7?Ax=!4;~=k_~sjUE-N&K>Tun{ym|cX9%zh0d56YkxIi&*EnDTEs@~d=vV{J-wc(rK_mJ#=fgY2sxJGPm zKxp<3v#*kwrhbZSXXkqQZ3OkVD7-e7asRU6qU~0Id-pc)P+-Hz^&hx-^n)AjBK4Nq zIjyups=;o7y+v{BMB4vcMS!co(C&U0M8_$v7IwJmEDCO}h7cb!RZk05FN93<&>9zA zpk|u}?$>E$|FZ%Y(uoPLjj0~DzRd+Z!}dw>gvuc5F0
- - - + + + +
@@ -21,42 +22,44 @@
-
-
-

{{ _('Account Summary') }}

-

{{ _('Username:') }} {{ request.user.username }}

-

{{ _('Default Friend Code:') }} {{ selected_friend_code.friend_code }}

-

{{ _('Reputation Score:') }} {{ request.user.reputation_score }}

-
-

{{ _('Trade Summary') }}

-
-
-
{{ _('Your Trade Offers') }}
-
{{ dashboard_offers_paginated.paginator.count }}
-
{{ _('Active Offers') }}
+
+
+
+
{{ _('Your Reputation') }}
+
{{ request.user.reputation_score }}
+
{{ _('Current Score') }}
+
+
+
{{ _('Your Trade Offers') }}
+
{{ dashboard_offers_paginated.page_obj.count }}
+
{{ _('Active Offers') }}
+
-
-
{{ _('Waiting on You') }}
-
{{ trade_acceptances_waiting_paginated.paginator.count }}
-
{{ _('Pending Requests') }}
-
-
-
{{ _('Waiting on Them') }}
-
{{ other_party_trade_acceptances_paginated.paginator.count }}
-
{{ _('Pending Responses') }}
+
+
+
{{ _('Waiting on You') }}
+
{{ trade_acceptances_waiting_paginated.page_obj.count }}
+
{{ _('Pending Requests') }}
+
+
+
{{ _('Waiting on Them') }}
+
{{ other_party_trade_acceptances_paginated.page_obj.count }}
+
{{ _('Pending Responses') }}
+
-
+
@@ -64,36 +67,36 @@
- {% include 'trades/_trade_offer_list.html' with offers=dashboard_offers_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=dashboard_offers_paginated.object_list page_obj=dashboard_offers_paginated.page_obj %}
- {% include 'trades/_trade_offer_list.html' with offers=trade_acceptances_waiting_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=trade_acceptances_waiting_paginated.object_list page_obj=trade_acceptances_waiting_paginated.page_obj %}
- {% include 'trades/_trade_offer_list.html' with offers=other_party_trade_acceptances_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=other_party_trade_acceptances_paginated.object_list page_obj=other_party_trade_acceptances_paginated.page_obj %}
-
{{ _('Closed Offers') }} ({{ closed_offers_paginated.paginator.count }})
+
{{ _('Closed Offers') }} ({{ closed_offers_paginated.page_obj.count }})
- {% include 'trades/_trade_offer_list.html' with offers=closed_offers_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=closed_offers_paginated.object_list page_obj=closed_offers_paginated.page_obj %}
-
{{ _('Closed Acceptances') }} ({{ closed_acceptances_paginated.paginator.count }})
+
{{ _('Closed Acceptances') }} ({{ closed_acceptances_paginated.page_obj.count }})
- {% include 'trades/_trade_offer_list.html' with offers=closed_acceptances_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=closed_acceptances_paginated.object_list page_obj=closed_acceptances_paginated.page_obj %}
-
{{ _('Rejected by Them') }} ({{ rejected_by_them_paginated.paginator.count }})
+
{{ _('Rejected by Them') }} ({{ rejected_by_them_paginated.page_obj.count }})
- {% include 'trades/_trade_offer_list.html' with offers=rejected_by_them_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=rejected_by_them_paginated.object_list page_obj=rejected_by_them_paginated.page_obj %}
-
{{ _('Rejected by Me') }} ({{ rejected_by_me_paginated.paginator.count }})
+
{{ _('Rejected by Me') }} ({{ rejected_by_me_paginated.page_obj.count }})
- {% include 'trades/_trade_offer_list.html' with offers=rejected_by_me_paginated %} + {% include 'trades/_trade_offer_list.html' with offers=rejected_by_me_paginated.object_list page_obj=rejected_by_me_paginated.page_obj %}
@@ -105,21 +108,69 @@
{{ gravatar_profile.displayName|default:request.user.username }}

{{ gravatar_profile.displayName|default:request.user.username }}

- {{ _('View Gravatar Profile') }} + + Edit Profile on Gravatar + + + + +
{% else %}

{{ _('No Gravatar profile data available.') }}

{% endif %} {% endwith %}
-

{{ _('What is Gravatar?') }}

-

{{ _('Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture and, optionally, a profile. Many websites use Gravatar to display your avatar automatically.') }}

-

{{ _('How does it work?') }}

-

{{ _('If you have set up a Gravatar, your profile picture will appear whenever you use your email on supported sites. Updates made on Gravatar will reflect here.') }}

-

{{ _('Is it safe? What about privacy?') }}

-

{{ _('Gravatar is optional, and your email is hashed to maintain privacy. Your personal data remains secure.') }}

-

{{ _('Want to update or add a Gravatar?') }}

-

{{ _('Go to Gravatar.com to set up or change your avatar or profile. Your changes will appear here once saved!') }}

+

What is Gravatar?

+

Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture and, optionally, a profile. Many websites, including this one, use Gravatar to display your avatar and profile automatically.

+ +

How does it work?

+

If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. When someone hovers over or clicks on your avatar, your Gravatar profile will also appear if you have one. If you don't have a Gravatar yet, you'll see a default image instead.

+ +

Is it safe? What about privacy?

+

Gravatar is completely optional, opt-in, and prioritizes your security and privacy. Your email is never shared and only a hashed version is sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers. Your personal data remains secure, and you maintain full control over your public profile.

+ +

Want to update or add a Gravatar?

+

Go to Gravatar.com to set up or change your avatar or profile. Your updates will appear here once saved!

+ +
+
+ + +
+
+ {% if friend_codes %} +
    + {% for code in friend_codes %} +
  • +
    + {{ code.in_game_name }} + {% if code.is_default %} + Default + {% endif %} +
    +
    + {{ code.friend_code }} +
    +
    + {% if not code.is_default %} +
    + {% csrf_token %} + +
    + {% endif %} + Delete +
    +
  • + {% endfor %} +
+ {% else %} +

You do not have any friend codes added yet.

+ {% endif %} + +
@@ -129,13 +180,11 @@
{% csrf_token %} {{ settings_form|crispy }} - {{ _('Edit Friend Codes') }} - +
+ +
- diff --git a/theme/templates/base.html b/theme/templates/base.html index cc4d325..ce033f6 100644 --- a/theme/templates/base.html +++ b/theme/templates/base.html @@ -31,22 +31,22 @@ - - + + {% tailwind_css %} - - + + - + - + @@ -58,18 +58,6 @@