From 47876e3971cfafc42cb7770c23b7f195bc3828e3 Mon Sep 17 00:00:00 2001 From: imperosol Date: Tue, 17 Dec 2024 00:53:47 +0100 Subject: [PATCH] Make product types dynamically orderable. --- core/static/bundled/alpine-index.js | 2 + core/static/core/forms.scss | 18 ++ core/static/core/style.scss | 11 + .../bundled/counter/product-type-index.ts | 64 ++++++ counter/static/counter/css/product_type.scss | 15 ++ .../templates/counter/producttype_list.jinja | 40 +++- locale/fr/LC_MESSAGES/django.po | 2 +- locale/fr/LC_MESSAGES/djangojs.po | 211 +++++++++--------- package-lock.json | 16 +- package.json | 3 +- 10 files changed, 269 insertions(+), 113 deletions(-) create mode 100644 counter/static/bundled/counter/product-type-index.ts create mode 100644 counter/static/counter/css/product_type.scss diff --git a/core/static/bundled/alpine-index.js b/core/static/bundled/alpine-index.js index d07e0bf2..211600a5 100644 --- a/core/static/bundled/alpine-index.js +++ b/core/static/bundled/alpine-index.js @@ -1,5 +1,7 @@ +import sort from "@alpinejs/sort"; import Alpine from "alpinejs"; +Alpine.plugin(sort); window.Alpine = Alpine; window.addEventListener("DOMContentLoaded", () => { diff --git a/core/static/core/forms.scss b/core/static/core/forms.scss index 7dab0484..e439bd8d 100644 --- a/core/static/core/forms.scss +++ b/core/static/core/forms.scss @@ -87,3 +87,21 @@ a:not(.button) { color: $primary-color; } } + + +form { + .row { + label { + margin: unset; + } + } + + fieldset { + margin-bottom: 1rem; + } + + .helptext { + margin-top: .25rem; + font-size: 80%; + } +} diff --git a/core/static/core/style.scss b/core/static/core/style.scss index cbe8d326..dd44fda0 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -314,6 +314,17 @@ body { } } + .snackbar { + width: 250px; + margin-left: -125px; + box-sizing: border-box; + position: fixed; + z-index: 1; + left: 50%; + top: 60px; + text-align: center; + } + .tabs { border-radius: 5px; diff --git a/counter/static/bundled/counter/product-type-index.ts b/counter/static/bundled/counter/product-type-index.ts new file mode 100644 index 00000000..4d1bdda4 --- /dev/null +++ b/counter/static/bundled/counter/product-type-index.ts @@ -0,0 +1,64 @@ +import Alpine from "alpinejs"; +import { producttypeReorder } from "#openapi"; + +document.addEventListener("alpine:init", () => { + Alpine.data("productTypesList", () => ({ + loading: false, + alertMessage: { + open: false, + success: true, + content: "", + timeout: null, + }, + + async reorder(itemId: number, newPosition: number) { + // The sort plugin of Alpine doesn't manage dynamic lists with x-sort + // (cf. https://github.com/alpinejs/alpine/discussions/4157). + // There is an open PR that fixes this issue + // (cf. https://github.com/alpinejs/alpine/pull/4361). + // However, it hasn't been merged yet. + // To overcome this, I get the list of DOM elements + // And fetch the `x-sort:item` attribute, which value is + // the id of the object in database. + // Please make this a little bit cleaner when the fix has been merged + // into the main Alpine repo. + this.loading = true; + const productTypes = this.$refs.productTypes + .childNodes as NodeListOf; + const getId = (elem: HTMLLIElement) => + Number.parseInt(elem.getAttribute("x-sort:item")); + const query = + newPosition === 0 + ? { above: getId(productTypes.item(1)) } + : { below: getId(productTypes.item(newPosition - 1)) }; + const response = await producttypeReorder({ + // biome-ignore lint/style/useNamingConvention: api is snake_case + path: { type_id: itemId }, + query: query, + }); + this.openAlertMessage(response.response); + this.loading = false; + }, + + openAlertMessage(response: Response) { + if (response.ok) { + this.alertMessage.success = true; + this.alertMessage.content = gettext("Products types successfully reordered"); + } else { + this.alertMessage.success = false; + this.alertMessage.content = interpolate( + gettext("Product type reorganisation failed with status code : %d"), + [response.status], + ); + } + this.alertMessage.open = true; + if (this.alertMessage.timeout !== null) { + clearTimeout(this.alertMessage.timeout); + } + this.alertMessage.timeout = setTimeout(() => { + this.alertMessage.open = false; + }, 2000); + this.loading = false; + }, + })); +}); diff --git a/counter/static/counter/css/product_type.scss b/counter/static/counter/css/product_type.scss new file mode 100644 index 00000000..16bd43a9 --- /dev/null +++ b/counter/static/counter/css/product_type.scss @@ -0,0 +1,15 @@ +.product-type-list { + li { + list-style: none; + margin-bottom: 10px; + + i { + cursor: grab; + visibility: hidden; + } + } +} + +body:not(.sorting) .product-type-list li:hover i { + visibility: visible; +} \ No newline at end of file diff --git a/counter/templates/counter/producttype_list.jinja b/counter/templates/counter/producttype_list.jinja index 0c4ff0c5..5d7ddc26 100644 --- a/counter/templates/counter/producttype_list.jinja +++ b/counter/templates/counter/producttype_list.jinja @@ -4,21 +4,49 @@ {% trans %}Product type list{% endtrans %} {% endblock %} +{% block additional_css %} + +{% endblock %} + +{% block additional_js %} + +{% endblock %} + {% block content %}

{% trans %}New product type{% endtrans %}

{% if producttype_list %} -

{% trans %}Product type list{% endtrans %}

- +
+

+

{% trans %}Product type list{% endtrans %}

+
    + {%- for t in producttype_list -%} +
  • + + {{ t }} +
  • + {%- endfor -%} +
+
{% else %} {% trans %}There is no product types in this website.{% endtrans %} {% endif %} {% endblock %} +{% block script %} + +{% endblock %} diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 20ddf28a..fdfef6b9 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-17 13:09+0100\n" +"POT-Creation-Date: 2024-12-17 13:10+0100\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" diff --git a/locale/fr/LC_MESSAGES/djangojs.po b/locale/fr/LC_MESSAGES/djangojs.po index e907e571..c89f7eb1 100644 --- a/locale/fr/LC_MESSAGES/djangojs.po +++ b/locale/fr/LC_MESSAGES/djangojs.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-14 10:24+0100\n" +"POT-Creation-Date: 2024-12-17 00:46+0100\n" "PO-Revision-Date: 2024-09-17 11:54+0200\n" "Last-Translator: Sli \n" "Language-Team: AE info \n" @@ -17,119 +17,128 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +#: core/static/bundled/core/components/ajax-select-base.ts:68 +msgid "Remove" +msgstr "Retirer" + +#: core/static/bundled/core/components/ajax-select-base.ts:90 +msgid "You need to type %(number)s more characters" +msgstr "Vous devez taper %(number)s caractères de plus" + +#: core/static/bundled/core/components/ajax-select-base.ts:94 +msgid "No results found" +msgstr "Aucun résultat trouvé" + +#: core/static/bundled/core/components/easymde-index.ts:38 +msgid "Heading" +msgstr "Titre" + +#: core/static/bundled/core/components/easymde-index.ts:44 +msgid "Italic" +msgstr "Italique" + +#: core/static/bundled/core/components/easymde-index.ts:50 +msgid "Bold" +msgstr "Gras" + +#: core/static/bundled/core/components/easymde-index.ts:56 +msgid "Strikethrough" +msgstr "Barré" + +#: core/static/bundled/core/components/easymde-index.ts:65 +msgid "Underline" +msgstr "Souligné" + +#: core/static/bundled/core/components/easymde-index.ts:74 +msgid "Superscript" +msgstr "Exposant" + +#: core/static/bundled/core/components/easymde-index.ts:83 +msgid "Subscript" +msgstr "Indice" + +#: core/static/bundled/core/components/easymde-index.ts:89 +msgid "Code" +msgstr "Code" + +#: core/static/bundled/core/components/easymde-index.ts:96 +msgid "Quote" +msgstr "Citation" + +#: core/static/bundled/core/components/easymde-index.ts:102 +msgid "Unordered list" +msgstr "Liste non ordonnée" + +#: core/static/bundled/core/components/easymde-index.ts:108 +msgid "Ordered list" +msgstr "Liste ordonnée" + +#: core/static/bundled/core/components/easymde-index.ts:115 +msgid "Insert link" +msgstr "Insérer lien" + +#: core/static/bundled/core/components/easymde-index.ts:121 +msgid "Insert image" +msgstr "Insérer image" + +#: core/static/bundled/core/components/easymde-index.ts:127 +msgid "Insert table" +msgstr "Insérer tableau" + +#: core/static/bundled/core/components/easymde-index.ts:134 +msgid "Clean block" +msgstr "Nettoyer bloc" + +#: core/static/bundled/core/components/easymde-index.ts:141 +msgid "Toggle preview" +msgstr "Activer la prévisualisation" + +#: core/static/bundled/core/components/easymde-index.ts:147 +msgid "Toggle side by side" +msgstr "Activer la vue côte à côte" + +#: core/static/bundled/core/components/easymde-index.ts:153 +msgid "Toggle fullscreen" +msgstr "Activer le plein écran" + +#: core/static/bundled/core/components/easymde-index.ts:160 +msgid "Markdown guide" +msgstr "Guide markdown" + +#: core/static/bundled/core/components/nfc-input-index.ts:26 +msgid "Unsupported NFC card" +msgstr "Carte NFC non supportée" + +#: core/static/bundled/user/family-graph-index.js:233 +msgid "family_tree.%(extension)s" +msgstr "arbre_genealogique.%(extension)s" + +#: core/static/bundled/user/pictures-index.js:76 +msgid "pictures.%(extension)s" +msgstr "photos.%(extension)s" + #: core/static/user/js/user_edit.js:91 #, javascript-format msgid "captured.%s" msgstr "capture.%s" -#: core/static/webpack/core/components/ajax-select-base.ts:68 -msgid "Remove" -msgstr "Retirer" +#: counter/static/bundled/counter/product-type-index.ts:36 +msgid "Products types successfully reordered" +msgstr "Types de produits réordonnés." -#: core/static/webpack/core/components/ajax-select-base.ts:88 -msgid "You need to type %(number)s more characters" -msgstr "Vous devez taper %(number)s caractères de plus" - -#: core/static/webpack/core/components/ajax-select-base.ts:92 -msgid "No results found" -msgstr "Aucun résultat trouvé" - -#: core/static/webpack/core/components/easymde-index.ts:38 -msgid "Heading" -msgstr "Titre" - -#: core/static/webpack/core/components/easymde-index.ts:44 -msgid "Italic" -msgstr "Italique" - -#: core/static/webpack/core/components/easymde-index.ts:50 -msgid "Bold" -msgstr "Gras" - -#: core/static/webpack/core/components/easymde-index.ts:56 -msgid "Strikethrough" -msgstr "Barré" - -#: core/static/webpack/core/components/easymde-index.ts:65 -msgid "Underline" -msgstr "Souligné" - -#: core/static/webpack/core/components/easymde-index.ts:74 -msgid "Superscript" -msgstr "Exposant" - -#: core/static/webpack/core/components/easymde-index.ts:83 -msgid "Subscript" -msgstr "Indice" - -#: core/static/webpack/core/components/easymde-index.ts:89 -msgid "Code" -msgstr "Code" - -#: core/static/webpack/core/components/easymde-index.ts:96 -msgid "Quote" -msgstr "Citation" - -#: core/static/webpack/core/components/easymde-index.ts:102 -msgid "Unordered list" -msgstr "Liste non ordonnée" - -#: core/static/webpack/core/components/easymde-index.ts:108 -msgid "Ordered list" -msgstr "Liste ordonnée" - -#: core/static/webpack/core/components/easymde-index.ts:115 -msgid "Insert link" -msgstr "Insérer lien" - -#: core/static/webpack/core/components/easymde-index.ts:121 -msgid "Insert image" -msgstr "Insérer image" - -#: core/static/webpack/core/components/easymde-index.ts:127 -msgid "Insert table" -msgstr "Insérer tableau" - -#: core/static/webpack/core/components/easymde-index.ts:134 -msgid "Clean block" -msgstr "Nettoyer bloc" - -#: core/static/webpack/core/components/easymde-index.ts:141 -msgid "Toggle preview" -msgstr "Activer la prévisualisation" - -#: core/static/webpack/core/components/easymde-index.ts:147 -msgid "Toggle side by side" -msgstr "Activer la vue côte à côte" - -#: core/static/webpack/core/components/easymde-index.ts:153 -msgid "Toggle fullscreen" -msgstr "Activer le plein écran" - -#: core/static/webpack/core/components/easymde-index.ts:160 -msgid "Markdown guide" -msgstr "Guide markdown" - -#: core/static/webpack/core/components/nfc-input-index.ts:24 -msgid "Unsupported NFC card" -msgstr "Carte NFC non supportée" - -#: core/static/webpack/user/family-graph-index.js:233 -msgid "family_tree.%(extension)s" -msgstr "arbre_genealogique.%(extension)s" - -#: core/static/webpack/user/pictures-index.js:76 -msgid "pictures.%(extension)s" -msgstr "photos.%(extension)s" +#: counter/static/bundled/counter/product-type-index.ts:40 +#, javascript-format +msgid "Product type reorganisation failed with status code : %d" +msgstr "La réorganisation des types de produit a échoué avec le code : %d" #: eboutic/static/eboutic/js/makecommand.js:56 msgid "Incorrect value" msgstr "Valeur incorrecte" -#: sas/static/webpack/sas/viewer-index.ts:271 +#: sas/static/bundled/sas/viewer-index.ts:271 msgid "Couldn't moderate picture" msgstr "Il n'a pas été possible de modérer l'image" -#: sas/static/webpack/sas/viewer-index.ts:284 +#: sas/static/bundled/sas/viewer-index.ts:284 msgid "Couldn't delete picture" msgstr "Il n'a pas été possible de supprimer l'image" diff --git a/package-lock.json b/package-lock.json index 05418a69..c46ef180 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,13 @@ "version": "3", "license": "GPL-3.0-only", "dependencies": { + "@alpinejs/sort": "^3.14.7", "@fortawesome/fontawesome-free": "^6.6.0", "@hey-api/client-fetch": "^0.4.0", "@sentry/browser": "^8.34.0", "@zip.js/zip.js": "^2.7.52", "3d-force-graph": "^1.73.4", - "alpinejs": "^3.14.1", + "alpinejs": "^3.14.7", "chart.js": "^4.4.4", "cytoscape": "^3.30.2", "cytoscape-cxtmenu": "^3.5.0", @@ -44,6 +45,12 @@ "vite-plugin-static-copy": "^2.1.0" } }, + "node_modules/@alpinejs/sort": { + "version": "3.14.7", + "resolved": "https://registry.npmjs.org/@alpinejs/sort/-/sort-3.14.7.tgz", + "integrity": "sha512-EJzxTBSoKvOxKHAUFeTSgxJR4rJQQPm10b4dB38kGcsxjUtOeNkbBF3xV4nlc0ZyTv7DarTWdppdoR/iP8jfdQ==", + "license": "MIT" + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -3064,9 +3071,10 @@ } }, "node_modules/alpinejs": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.1.tgz", - "integrity": "sha512-ICar8UsnRZAYvv/fCNfNeKMXNoXGUfwHrjx7LqXd08zIP95G2d9bAOuaL97re+1mgt/HojqHsfdOLo/A5LuWgQ==", + "version": "3.14.7", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.7.tgz", + "integrity": "sha512-ScnbydNBcWVnCiVupD3wWUvoMPm8244xkvDNMxVCspgmap9m4QuJ7pjc+77UtByU+1+Ejg0wzYkP4mQaOMcvng==", + "license": "MIT", "dependencies": { "@vue/reactivity": "~3.1.1" } diff --git a/package.json b/package.json index 2ca46967..77572a6f 100644 --- a/package.json +++ b/package.json @@ -33,12 +33,13 @@ "vite-plugin-static-copy": "^2.1.0" }, "dependencies": { + "@alpinejs/sort": "^3.14.7", "@fortawesome/fontawesome-free": "^6.6.0", "@hey-api/client-fetch": "^0.4.0", "@sentry/browser": "^8.34.0", "@zip.js/zip.js": "^2.7.52", "3d-force-graph": "^1.73.4", - "alpinejs": "^3.14.1", + "alpinejs": "^3.14.7", "chart.js": "^4.4.4", "cytoscape": "^3.30.2", "cytoscape-cxtmenu": "^3.5.0",