2025-01-08
This commit is contained in:
parent
6189bb32a8
commit
caf87cf1da
38 changed files with 1541 additions and 1401 deletions
|
|
@ -29,16 +29,22 @@
|
|||
"no-async-await": "off",
|
||||
"no-console": "off",
|
||||
"no-magic-numbers": "warn",
|
||||
"no-map-spread": "off",
|
||||
"no-misused-promises": "off",
|
||||
"no-optional-chaining": "off",
|
||||
"no-rest-spread-properties": "off",
|
||||
"no-ternary": "off",
|
||||
"no-undefined": "off",
|
||||
"no-unused-expressions": "off",
|
||||
"no-void": "off",
|
||||
"prefer-await-to-then": "off",
|
||||
"promise/prefer-await-to-callbacks": "off",
|
||||
"sort-imports": "off",
|
||||
"typescript/array-type": ["error", { "default": "generic", "readonly": "generic" }],
|
||||
"typescript/consistent-indexed-object-style": ["error", "record"],
|
||||
"typescript/consistent-type-imports": "error",
|
||||
"prefer-await-to-then": "off",
|
||||
"no-void": "off",
|
||||
"no-optional-chaining": "off",
|
||||
"no-unused-expressions": "off",
|
||||
"no-misused-promises": "off",
|
||||
"typescript/explicit-function-return-type": "warn",
|
||||
"unicorn/prefer-dom-node-dataset": "off",
|
||||
"yoda": ["error", "never"]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
79
STUFF.md
Normal file
79
STUFF.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
I'll see you soup
|
||||
|
||||
- Thèmes
|
||||
- LS_COLORS (Vivid ?)
|
||||
- bat
|
||||
- eza (?)
|
||||
- fdfind
|
||||
- fish
|
||||
- gitui
|
||||
- helix
|
||||
- wezterm
|
||||
- yazi
|
||||
- zed
|
||||
- zellij
|
||||
|
||||
Google API
|
||||
AIzaSyDGe62r-bDxvNuDCP6HIfWIJAMvelFxU1s
|
||||
402628219773-hl8niqniiiklf15f9biou8g06pbm9sac.apps.googleusercontent.com
|
||||
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
|
||||
|
||||
- Code promo ?
|
||||
- La commande peut être associée au panier.
|
||||
- Vu que l'on passe par l'API REST, le panier est dissocié de la commande.
|
||||
- Il est possible de fixer le hash du panier dans la commande avec la fonction set_cart_hash de WC_Order.
|
||||
- Cela permet par la suite d'utiliser la fonction cancel_order WC_Order à l'annulation de cette dernière lors du retour au Panier depuis Stripe.
|
||||
- Ce ne sera possible qu'en utilisant un endpoint personnalisé réalisant ces opérations plutôt que l'API REST.
|
||||
- Dans l'idéal,
|
||||
- Ajouter un bouton "Reset cart" quelque part pour tout réinitialiser (et appeler cancel_order si implémenté)
|
||||
|
||||
---
|
||||
|
||||
- BadRequestError
|
||||
- reponse.status === 400
|
||||
- reponse.body = {
|
||||
code: string,
|
||||
message: string (différenciation sur le message ?),
|
||||
data: {
|
||||
status: number (400),
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
Stripe
|
||||
pk_live_51D0BbTIKBol0AhpghF9b6lJ4ZjPXWaNRzBgxtcUTdbV8OC2OpHxSbkMoEEgCHEPSs6E6NISfdMv92t9OnKqKh0sH00N6tgi6HW
|
||||
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
|
||||
|
||||
---
|
||||
|
||||
- Chargement de la page
|
||||
- Récupération des informations à la génération de la page
|
||||
- Panier
|
||||
- Code promo
|
||||
- Mode de livraison
|
||||
- Sous-totaux
|
||||
- Total
|
||||
- Adresses
|
||||
- Récupération des informations dans le LocalStorage
|
||||
- Code promo
|
||||
- Mode de livraison
|
||||
- Adresses
|
||||
|
||||
- À l'injection de données du LocalStorage
|
||||
- Mettre à jour les sous-totaux
|
||||
|
||||
- À l'appui sur le bouton de calcul de la livraison et au succès de la requête
|
||||
- Mettre à jour les méthodes de livraison
|
||||
- Mettre à jour les sous-totaux et le total
|
||||
- Sauvegarder les nouvelles données dans le LocalStorage
|
||||
|
||||
- Événements à créer
|
||||
- MiseAJourCodePromo
|
||||
- Se déclenche quand le champ du Code promo est modifié
|
||||
- MiseAJourProduits
|
||||
- Se déclenche quand une des lignes du Panier est modifiée (addition/soustraction/suppression)
|
||||
- MiseAJourMethodeLivraison
|
||||
- Se déclence quand le choix de la Méthode de livraison est modifié
|
||||
- MiseAJourAdresses
|
||||
- Se déclenche quand un des champs du formulaire des adresses est modifié
|
||||
153
composer.lock
generated
153
composer.lock
generated
|
|
@ -646,16 +646,16 @@
|
|||
},
|
||||
{
|
||||
"name": "illuminate/collections",
|
||||
"version": "v11.36.1",
|
||||
"version": "v11.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/collections.git",
|
||||
"reference": "21868f9ac221a42d4346dc56495d11ab7e0d339a"
|
||||
"reference": "9100b083eeb85d38d78fc1de28f7326596ab2eda"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/collections/zipball/21868f9ac221a42d4346dc56495d11ab7e0d339a",
|
||||
"reference": "21868f9ac221a42d4346dc56495d11ab7e0d339a",
|
||||
"url": "https://api.github.com/repos/illuminate/collections/zipball/9100b083eeb85d38d78fc1de28f7326596ab2eda",
|
||||
"reference": "9100b083eeb85d38d78fc1de28f7326596ab2eda",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -698,11 +698,11 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-12-13T13:58:10+00:00"
|
||||
"time": "2024-12-18T14:14:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/conditionable",
|
||||
"version": "v11.36.1",
|
||||
"version": "v11.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/conditionable.git",
|
||||
|
|
@ -748,7 +748,7 @@
|
|||
},
|
||||
{
|
||||
"name": "illuminate/contracts",
|
||||
"version": "v11.36.1",
|
||||
"version": "v11.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/contracts.git",
|
||||
|
|
@ -796,7 +796,7 @@
|
|||
},
|
||||
{
|
||||
"name": "illuminate/macroable",
|
||||
"version": "v11.36.1",
|
||||
"version": "v11.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/macroable.git",
|
||||
|
|
@ -842,16 +842,16 @@
|
|||
},
|
||||
{
|
||||
"name": "illuminate/support",
|
||||
"version": "v11.36.1",
|
||||
"version": "v11.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/support.git",
|
||||
"reference": "fba1ff58e30fa280248ce3db9b18d6341c6ac339"
|
||||
"reference": "388c916b143a104e732cbaf7e6b19cd7a4e21a1e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/support/zipball/fba1ff58e30fa280248ce3db9b18d6341c6ac339",
|
||||
"reference": "fba1ff58e30fa280248ce3db9b18d6341c6ac339",
|
||||
"url": "https://api.github.com/repos/illuminate/support/zipball/388c916b143a104e732cbaf7e6b19cd7a4e21a1e",
|
||||
"reference": "388c916b143a104e732cbaf7e6b19cd7a4e21a1e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -875,7 +875,7 @@
|
|||
},
|
||||
"suggest": {
|
||||
"illuminate/filesystem": "Required to use the Composer class (^11.0).",
|
||||
"laravel/serializable-closure": "Required to use the once function (^1.3).",
|
||||
"laravel/serializable-closure": "Required to use the once function (^1.3|^2.0).",
|
||||
"league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).",
|
||||
"league/uri": "Required to use the Uri class (^7.5.1).",
|
||||
"ramsey/uuid": "Required to use Str::uuid() (^4.7).",
|
||||
|
|
@ -915,7 +915,7 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-12-17T20:20:09+00:00"
|
||||
"time": "2024-12-20T14:43:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/helpers",
|
||||
|
|
@ -1223,16 +1223,16 @@
|
|||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "3.8.3",
|
||||
"version": "3.8.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "f01cfa96468f4c38325f507ab81a4f1d2cd93cfe"
|
||||
"url": "https://github.com/CarbonPHP/carbon.git",
|
||||
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/f01cfa96468f4c38325f507ab81a4f1d2cd93cfe",
|
||||
"reference": "f01cfa96468f4c38325f507ab81a4f1d2cd93cfe",
|
||||
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58",
|
||||
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1325,7 +1325,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-21T18:03:19+00:00"
|
||||
"time": "2024-12-27T09:25:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "oscarotero/env",
|
||||
|
|
@ -2381,12 +2381,12 @@
|
|||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/contracts",
|
||||
"name": "symfony/contracts"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -2901,16 +2901,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v7.2.0",
|
||||
"version": "v7.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5"
|
||||
"reference": "e2674a30132b7cc4d74540d6c2573aa363f05923"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/dc89e16b44048ceecc879054e5b7f38326ab6cc5",
|
||||
"reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923",
|
||||
"reference": "e2674a30132b7cc4d74540d6c2573aa363f05923",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2976,7 +2976,7 @@
|
|||
"description": "Provides tools to internationalize your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation/tree/v7.2.0"
|
||||
"source": "https://github.com/symfony/translation/tree/v7.2.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2992,7 +2992,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-12T20:47:56+00:00"
|
||||
"time": "2024-12-07T08:18:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
|
|
@ -3013,12 +3013,12 @@
|
|||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/contracts",
|
||||
"name": "symfony/contracts"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -3148,21 +3148,21 @@
|
|||
},
|
||||
{
|
||||
"name": "timber/timber",
|
||||
"version": "v2.3.0",
|
||||
"version": "v2.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/timber/timber.git",
|
||||
"reference": "55acea4414eac6ea9d0a11a102af37cf13f219b2"
|
||||
"reference": "3f6e73feadf5d547dff4992f645805da7fbc4d3a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/timber/timber/zipball/55acea4414eac6ea9d0a11a102af37cf13f219b2",
|
||||
"reference": "55acea4414eac6ea9d0a11a102af37cf13f219b2",
|
||||
"url": "https://api.github.com/repos/timber/timber/zipball/3f6e73feadf5d547dff4992f645805da7fbc4d3a",
|
||||
"reference": "3f6e73feadf5d547dff4992f645805da7fbc4d3a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"twig/twig": "^3.5"
|
||||
"twig/twig": "^3.17"
|
||||
},
|
||||
"require-dev": {
|
||||
"ergebnis/composer-normalize": "^2.28",
|
||||
|
|
@ -3177,7 +3177,7 @@
|
|||
"squizlabs/php_codesniffer": "^3.0",
|
||||
"symplify/easy-coding-standard": "^12.2",
|
||||
"szepeviktor/phpstan-wordpress": "^1.1",
|
||||
"twig/cache-extra": "^3.3",
|
||||
"twig/cache-extra": "^3.17",
|
||||
"wpackagist-plugin/advanced-custom-fields": "^6.0",
|
||||
"wpackagist-plugin/co-authors-plus": "^3.6",
|
||||
"yoast/wp-test-utils": "^1.2"
|
||||
|
|
@ -3246,20 +3246,20 @@
|
|||
"type": "open_collective"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-08T09:38:16+00:00"
|
||||
"time": "2024-12-23T13:04:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.17.1",
|
||||
"version": "v3.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "677ef8da6497a03048192aeeb5aa3018e379ac71"
|
||||
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/677ef8da6497a03048192aeeb5aa3018e379ac71",
|
||||
"reference": "677ef8da6497a03048192aeeb5aa3018e379ac71",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
||||
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3314,7 +3314,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.17.1"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.18.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3326,7 +3326,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-12T09:58:10+00:00"
|
||||
"time": "2024-12-29T10:51:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
|
|
@ -3596,15 +3596,15 @@
|
|||
},
|
||||
{
|
||||
"name": "wpackagist-plugin/woocommerce",
|
||||
"version": "9.5.1",
|
||||
"version": "9.5.2",
|
||||
"source": {
|
||||
"type": "svn",
|
||||
"url": "https://plugins.svn.wordpress.org/woocommerce/",
|
||||
"reference": "tags/9.5.1"
|
||||
"reference": "tags/9.5.2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://downloads.wordpress.org/plugin/woocommerce.9.5.1.zip"
|
||||
"url": "https://downloads.wordpress.org/plugin/woocommerce.9.5.2.zip"
|
||||
},
|
||||
"require": {
|
||||
"composer/installers": "^1.0 || ^2.0"
|
||||
|
|
@ -3614,15 +3614,15 @@
|
|||
},
|
||||
{
|
||||
"name": "wpackagist-plugin/wp-mail-logging",
|
||||
"version": "1.13.1",
|
||||
"version": "1.14.0",
|
||||
"source": {
|
||||
"type": "svn",
|
||||
"url": "https://plugins.svn.wordpress.org/wp-mail-logging/",
|
||||
"reference": "tags/1.13.1"
|
||||
"reference": "tags/1.14.0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://downloads.wordpress.org/plugin/wp-mail-logging.1.13.1.zip"
|
||||
"url": "https://downloads.wordpress.org/plugin/wp-mail-logging.1.14.0.zip"
|
||||
},
|
||||
"require": {
|
||||
"composer/installers": "^1.0 || ^2.0"
|
||||
|
|
@ -3650,15 +3650,15 @@
|
|||
},
|
||||
{
|
||||
"name": "wpackagist-plugin/wp-openapi",
|
||||
"version": "1.0.17",
|
||||
"version": "1.0.18",
|
||||
"source": {
|
||||
"type": "svn",
|
||||
"url": "https://plugins.svn.wordpress.org/wp-openapi/",
|
||||
"reference": "tags/1.0.17"
|
||||
"reference": "tags/1.0.18"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://downloads.wordpress.org/plugin/wp-openapi.1.0.17.zip"
|
||||
"url": "https://downloads.wordpress.org/plugin/wp-openapi.1.0.18.zip"
|
||||
},
|
||||
"require": {
|
||||
"composer/installers": "^1.0 || ^2.0"
|
||||
|
|
@ -3784,16 +3784,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.0.4",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "50d276fc3bf1430ec315f2f109bbde2769821524"
|
||||
"reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d276fc3bf1430ec315f2f109bbde2769821524",
|
||||
"reference": "50d276fc3bf1430ec315f2f109bbde2769821524",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
|
||||
"reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3838,7 +3838,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-17T17:14:01+00:00"
|
||||
"time": "2025-01-05T16:43:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "roave/security-advisories",
|
||||
|
|
@ -3846,12 +3846,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "abbccc97f36a9c78f033525c019d310433f22b57"
|
||||
"reference": "19bf84017a308ac32893551b899bac74d2aba856"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/abbccc97f36a9c78f033525c019d310433f22b57",
|
||||
"reference": "abbccc97f36a9c78f033525c019d310433f22b57",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/19bf84017a308ac32893551b899bac74d2aba856",
|
||||
"reference": "19bf84017a308ac32893551b899bac74d2aba856",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
|
|
@ -3973,7 +3973,7 @@
|
|||
"datatables/datatables": "<1.10.10",
|
||||
"david-garcia/phpwhois": "<=4.3.1",
|
||||
"dbrisinajumi/d2files": "<1",
|
||||
"dcat/laravel-admin": "<=2.1.3",
|
||||
"dcat/laravel-admin": "<=2.1.3|==2.2.0.0-beta|==2.2.2.0-beta",
|
||||
"derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3",
|
||||
"derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4",
|
||||
"desperado/xml-bundle": "<=0.1.7",
|
||||
|
|
@ -4095,6 +4095,7 @@
|
|||
"grumpydictator/firefly-iii": "<6.1.17",
|
||||
"gugoan/economizzer": "<=0.9.0.0-beta1",
|
||||
"guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5",
|
||||
"guzzlehttp/oauth-subscriber": "<0.8.1",
|
||||
"guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5",
|
||||
"haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2",
|
||||
"harvesthq/chosen": "<1.8.7",
|
||||
|
|
@ -4140,7 +4141,7 @@
|
|||
"james-heinrich/phpthumb": "<1.7.12",
|
||||
"jasig/phpcas": "<1.3.3",
|
||||
"jcbrand/converse.js": "<3.3.3",
|
||||
"joelbutcher/socialstream": "<6.2",
|
||||
"joelbutcher/socialstream": "<5.6|>=6,<6.2",
|
||||
"johnbillion/wp-crontrol": "<1.16.2",
|
||||
"joomla/application": "<1.0.13",
|
||||
"joomla/archive": "<1.1.12|>=2,<2.0.1",
|
||||
|
|
@ -4255,10 +4256,11 @@
|
|||
"neos/media-browser": "<7.3.19|>=8,<8.0.16|>=8.1,<8.1.11|>=8.2,<8.2.11|>=8.3,<8.3.9",
|
||||
"neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2",
|
||||
"neos/swiftmailer": "<5.4.5",
|
||||
"netcarver/textile": "<=4.1.2",
|
||||
"netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15",
|
||||
"nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6",
|
||||
"nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13",
|
||||
"nilsteampassnet/teampass": "<3.0.10",
|
||||
"nilsteampassnet/teampass": "<3.1.3.1-dev",
|
||||
"nonfiction/nterchange": "<4.1.1",
|
||||
"notrinos/notrinos-erp": "<=0.7",
|
||||
"noumo/easyii": "<=0.9",
|
||||
|
|
@ -4319,10 +4321,10 @@
|
|||
"phpmailer/phpmailer": "<6.5",
|
||||
"phpmussel/phpmussel": ">=1,<1.6",
|
||||
"phpmyadmin/phpmyadmin": "<5.2.1",
|
||||
"phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5",
|
||||
"phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5|>=3.2.10,<=4.0.1",
|
||||
"phpoffice/common": "<0.2.9",
|
||||
"phpoffice/phpexcel": "<1.8.1",
|
||||
"phpoffice/phpspreadsheet": "<1.29.4|>=2,<2.1.3|>=2.2,<2.3.2|>=3.3,<3.4",
|
||||
"phpoffice/phpspreadsheet": "<=1.29.6|>=2,<=2.1.5|>=2.2,<=2.3.4|>=3,<3.7",
|
||||
"phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36",
|
||||
"phpservermon/phpservermon": "<3.6",
|
||||
"phpsysinfo/phpsysinfo": "<3.4.3",
|
||||
|
|
@ -4401,7 +4403,7 @@
|
|||
"shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev",
|
||||
"shopxo/shopxo": "<=6.1",
|
||||
"showdoc/showdoc": "<2.10.4",
|
||||
"shuchkin/simplexlsx": ">=1.0.12,<1.1.12",
|
||||
"shuchkin/simplexlsx": ">=1.0.12,<1.1.13",
|
||||
"silverstripe-australia/advancedreports": ">=1,<=2",
|
||||
"silverstripe/admin": "<1.13.19|>=2,<2.1.8",
|
||||
"silverstripe/assets": ">=1,<1.11.1",
|
||||
|
|
@ -4448,6 +4450,7 @@
|
|||
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
|
||||
"ssddanbrown/bookstack": "<24.05.1",
|
||||
"starcitizentools/citizen-skin": ">=2.6.3,<2.31",
|
||||
"starcitizentools/tabber-neue": ">=1.9.1,<2.7.2",
|
||||
"statamic/cms": "<=5.16",
|
||||
"stormpath/sdk": "<9.9.99",
|
||||
"studio-42/elfinder": "<=2.1.64",
|
||||
|
|
@ -4512,18 +4515,20 @@
|
|||
"t3s/content-consent": "<1.0.3|>=2,<2.0.2",
|
||||
"tastyigniter/tastyigniter": "<3.3",
|
||||
"tcg/voyager": "<=1.4",
|
||||
"tecnickcom/tcpdf": "<=6.7.5",
|
||||
"tecnickcom/tc-lib-pdf-font": "<2.6.4",
|
||||
"tecnickcom/tcpdf": "<6.8",
|
||||
"terminal42/contao-tablelookupwizard": "<3.3.5",
|
||||
"thelia/backoffice-default-template": ">=2.1,<2.1.2",
|
||||
"thelia/thelia": ">=2.1,<2.1.3",
|
||||
"theonedemon/phpwhois": "<=4.2.5",
|
||||
"thinkcmf/thinkcmf": "<6.0.8",
|
||||
"thorsten/phpmyfaq": "<4",
|
||||
"thorsten/phpmyfaq": "<=4.0.1",
|
||||
"tikiwiki/tiki-manager": "<=17.1",
|
||||
"timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1",
|
||||
"tinymce/tinymce": "<7.2",
|
||||
"tinymighty/wiki-seo": "<1.2.2",
|
||||
"titon/framework": "<9.9.99",
|
||||
"tltneon/lgsl": "<7",
|
||||
"tobiasbg/tablepress": "<=2.0.0.0-RC1",
|
||||
"topthink/framework": "<6.0.17|>=6.1,<=8.0.4",
|
||||
"topthink/think": "<=6.1.1",
|
||||
|
|
@ -4601,7 +4606,7 @@
|
|||
"xpressengine/xpressengine": "<3.0.15",
|
||||
"yab/quarx": "<2.4.5",
|
||||
"yeswiki/yeswiki": "<=4.4.4",
|
||||
"yetiforce/yetiforce-crm": "<=6.4",
|
||||
"yetiforce/yetiforce-crm": "<6.5",
|
||||
"yidashi/yii2cmf": "<=2",
|
||||
"yii2mod/yii2-cms": "<1.9.2",
|
||||
"yiisoft/yii": "<1.1.29",
|
||||
|
|
@ -4691,7 +4696,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-20T16:05:39+00:00"
|
||||
"time": "2025-01-07T18:06:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "squizlabs/php_codesniffer",
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@
|
|||
"https://plugins.dprint.dev/typescript-0.93.3.wasm",
|
||||
"https://plugins.dprint.dev/json-0.19.4.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.17.8.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.3.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.4.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/malva-v0.11.1.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.18.0.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm",
|
||||
|
|
|
|||
47
package.json
47
package.json
|
|
@ -8,57 +8,57 @@
|
|||
"packageManager": "pnpm@9.14.4",
|
||||
"main": "index.js",
|
||||
"keywords": [],
|
||||
"scripts": {
|
||||
"knip": "knip",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"scripts": { "knip": "knip", "test": "echo \"Error: no test specified\" && exit 1" },
|
||||
"dependencies": {
|
||||
"@mobily/ts-belt": "4.0.0-rc.5",
|
||||
"@sentry/browser": "8.47.0",
|
||||
"@sentry/browser": "8.48.0",
|
||||
"@swan-io/boxed": "^3.2.0",
|
||||
"a11y-dialog": "^8.1.1",
|
||||
"chalk": "^5.4.1",
|
||||
"lit-html": "^3.2.1",
|
||||
"loglevel": "^1.9.2",
|
||||
"loglevel-plugin-prefix": "^0.8.4",
|
||||
"optics-ts": "^2.4.1",
|
||||
"purify-ts": "^2.1.0",
|
||||
"ts-pattern": "^5.6.0",
|
||||
"valibot": "1.0.0-beta.9"
|
||||
"valibot": "1.0.0-beta.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@prettier/plugin-php": "^0.22.2",
|
||||
"@prettier/plugin-xml": "^3.4.1",
|
||||
"@sentry/core": "^8.47.0",
|
||||
"@sentry/core": "^8.48.0",
|
||||
"@swc/cli": "0.5.2",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/node": "^22.10.5",
|
||||
"@vitejs/plugin-legacy": "^6.0.0",
|
||||
"better-typescript-lib": "^2.10.0",
|
||||
"browserslist": "^4.24.3",
|
||||
"browserslist": "^4.24.4",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-plugin-oxlint": "^0.15.2",
|
||||
"eslint-plugin-perfectionist": "^4.4.0",
|
||||
"eslint-plugin-oxlint": "^0.15.5",
|
||||
"eslint-plugin-perfectionist": "^4.6.0",
|
||||
"fdir": "^6.4.2",
|
||||
"globals": "^15.14.0",
|
||||
"knip": "^5.41.1",
|
||||
"oxlint": "^0.15.3",
|
||||
"oxlint": "^0.15.5",
|
||||
"picomatch": "^4.0.2",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-pkg": "^0.18.1",
|
||||
"prettier-plugin-sh": "^0.14.0",
|
||||
"sass-embedded": "^1.83.0",
|
||||
"sass-embedded": "^1.83.1",
|
||||
"stylelint": "^16.12.0",
|
||||
"stylelint-config-clean-order": "^6.1.0",
|
||||
"stylelint-config-clean-order": "^7.0.0",
|
||||
"stylelint-config-sass-guidelines": "^12.1.0",
|
||||
"stylelint-config-standard-scss": "^14.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "^2.8.0",
|
||||
"stylelint-plugin-logical-css": "^1.2.1",
|
||||
"typescript": "5.8.0-dev.20241122",
|
||||
"typescript-eslint": "^8.18.1",
|
||||
"vite": "^6.0.5",
|
||||
"typescript-eslint": "^8.19.1",
|
||||
"vite": "^6.0.7",
|
||||
"vite-plugin-manifest-sri": "^0.2.0",
|
||||
"vite-plugin-node-polyfills": "^0.22.0",
|
||||
"vite-plugin-valibot-env": "^0.9.2",
|
||||
"vite-plugin-valibot-env": "^0.9.3",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"wp-types": "^4.67.0"
|
||||
},
|
||||
|
|
@ -72,15 +72,8 @@
|
|||
"ios >0 and last 3 years"
|
||||
],
|
||||
"knip": {
|
||||
"entry": [
|
||||
"web/app/themes/haiku-atelier-2024/src/scripts/*.ts"
|
||||
],
|
||||
"project": [
|
||||
"web/app/themes/haiku-atelier-2024/src/scripts/**/*.{js,ts,d.ts}"
|
||||
]
|
||||
"entry": ["web/app/themes/haiku-atelier-2024/src/scripts/*.ts"],
|
||||
"project": ["web/app/themes/haiku-atelier-2024/src/scripts/**/*.{js,ts,d.ts}"]
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@biomejs/biome",
|
||||
"@parcel/watcher"
|
||||
]
|
||||
"trustedDependencies": ["@biomejs/biome", "@parcel/watcher"]
|
||||
}
|
||||
|
|
|
|||
1127
pnpm-lock.yaml
generated
1127
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
|||
export const ADRESSES_MAJ = "adressesMaj";
|
||||
export const CODE_PROMO_MAJ = "codePromoMaj";
|
||||
export const METHODES_LIVRAISON_MAJ = "methodesLivraisonMaj";
|
||||
export const SHIPPING_RATES_UPDATED = "shippingRatesUpdated";
|
||||
export const PRODUITS_MAJ = "produitsMaj";
|
||||
export const TOTAUX_MAJ = "totauxMaj";
|
||||
export const TOTALS_UPDATED = "totalsUpdated";
|
||||
|
|
|
|||
|
|
@ -73,4 +73,4 @@ const client = new BrowserClient({
|
|||
|
||||
/* Initialise la configuration */
|
||||
getCurrentScope().setClient(client);
|
||||
client.init();
|
||||
// client.init();
|
||||
|
|
|
|||
12
web/app/themes/haiku-atelier-2024/src/scripts/lib/arrays.ts
Normal file
12
web/app/themes/haiku-atelier-2024/src/scripts/lib/arrays.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export const forEach = <T>(fn: (_1: T) => void) => (xs: Array<T>): void => {
|
||||
xs.forEach(fn);
|
||||
};
|
||||
|
||||
export const forEachWithIndex = <T>(fn: (_1: T, _2: number) => void) => (xs: Array<T>): void => {
|
||||
xs.forEach(fn);
|
||||
};
|
||||
|
||||
export const map = <T>(fn: (_1: T) => void) => (xs: Array<T>): Array<T> => {
|
||||
xs.map(fn);
|
||||
return xs;
|
||||
};
|
||||
|
|
@ -1,19 +1,25 @@
|
|||
import { A, G, pipe } from "@mobily/ts-belt";
|
||||
import { Either, identity, Left, Maybe, Right } from "purify-ts";
|
||||
import { Either, identity, Left, Right } from "purify-ts";
|
||||
|
||||
import type { ElementParent } from "./types/dom.d.ts";
|
||||
import type { ParentElement } from "./types/dom.d.ts";
|
||||
|
||||
import { ATTRIBUT_CHARGEMENT, ATTRIBUT_DESACTIVE } from "../constantes/dom.ts";
|
||||
import { logger } from "../logging.ts";
|
||||
import { lanceAnimationCycleLoading } from "./animations.ts";
|
||||
import {
|
||||
BadRequestError,
|
||||
creeSyntaxError,
|
||||
ERREUR_SELECTEUR_INEXISTANT,
|
||||
ERREUR_SYNTAXE_INVALIDE,
|
||||
ErreurEntreeInexistante,
|
||||
type NonExistingKeyError,
|
||||
ForbiddenError,
|
||||
NotFoundError,
|
||||
reporteEtLeveErreur,
|
||||
ServerError,
|
||||
UnauthorizedError,
|
||||
} from "./erreurs";
|
||||
|
||||
export const recupereElementAvecSelecteur =
|
||||
(parent: ElementParent) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, E> =>
|
||||
(parent: ParentElement) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, E> =>
|
||||
Either
|
||||
// Retourne une SyntaxError dans un Left si le sélecteur est invalide
|
||||
.encase(() => parent.querySelector<E>(selecteur))
|
||||
|
|
@ -24,8 +30,8 @@ export const recupereElementAvecSelecteur =
|
|||
G.isNotNullable(e) ? Right(e) : Left(creeSyntaxError(ERREUR_SELECTEUR_INEXISTANT(selecteur)))
|
||||
);
|
||||
|
||||
export const recupereElementsAvecSelecteur =
|
||||
(parent: ElementParent) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, Array<E>> =>
|
||||
export const getDOMElementsWithSelector =
|
||||
(parent: ParentElement) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, Array<E>> =>
|
||||
Either
|
||||
// Retourne une SyntaxError dans un Left si le sélecteur est invalide
|
||||
.encase(() => pipe(parent.querySelectorAll<E>(selecteur), Array.from<E>))
|
||||
|
|
@ -62,29 +68,86 @@ export const html = (strings: TemplateStringsArray, ...args: Array<string>) =>
|
|||
template => template.content,
|
||||
);
|
||||
|
||||
/**
|
||||
* Récupère une entrée dans le Stockage de Session (`storageSession`) sous forme d'`Either`.
|
||||
*
|
||||
* @param cle La clé de l'entrée.
|
||||
* @returns Un `Either` avec une `NonExistingKeyError` si la clé est absente (`Left`), la
|
||||
* valeur de l'entrée sinon (`Right`).
|
||||
*/
|
||||
export const eitherSessionStorageGet = (cle: string): Either<NonExistingKeyError, string> =>
|
||||
Maybe
|
||||
.fromNullable(sessionStorage.getItem(cle))
|
||||
.toEither(ErreurEntreeInexistante(`Clé ${cle} absente dans le stockage de session.`));
|
||||
|
||||
/**
|
||||
* Convertis une chaîne JSON en un objet JavaScript sous forme d'`Either`.
|
||||
* @param chaine La chaîne à convertir.
|
||||
* @returns Un `Either` avec une `SyntaxError` si la chaîne est invalide, un objet JS sinon
|
||||
* (`Right`).
|
||||
*/
|
||||
export const eitherJsonParse = (chaine: string): Either<SyntaxError, JSONValue> =>
|
||||
export const safeJsonParse = (chaine: string): Either<SyntaxError, JSONValue> =>
|
||||
Either.encase(() => JSON.parse(chaine));
|
||||
|
||||
/** TODO */
|
||||
export const accorderCibleASelecteur = <E extends HTMLElement = HTMLElement>(
|
||||
/**
|
||||
* Vérifie qu'un sélecteur s'applique à l'élément DOM d'une cible d'événement (un `EventTarget`) donnée.
|
||||
* La fonction agit comme un garde de type.
|
||||
*
|
||||
* @returns Un booléen
|
||||
*/
|
||||
export const targetMatchesSelector = <E extends HTMLElement = HTMLElement>(
|
||||
cible: EventTarget | null,
|
||||
selecteur: string,
|
||||
): cible is E => cible !== null && (cible as HTMLElement).matches(selecteur);
|
||||
|
||||
export const recupereElementsDocumentEither: <E extends Element = Element>(
|
||||
selecteur: string,
|
||||
) => Either<SyntaxError, Array<E>> = getDOMElementsWithSelector(document);
|
||||
|
||||
export const recupereElementDocumentEither: <E extends Element = Element>(
|
||||
selecteur: string,
|
||||
) => Either<SyntaxError, E> = recupereElementAvecSelecteur(document);
|
||||
|
||||
/**
|
||||
* Fonction utilitaire pour récupérer un Élément selon un sélecteur au sein du Document.
|
||||
*
|
||||
* @param selecteur Le sélecteur de l'Élément recherché.
|
||||
* @throws Une SyntaxError si l'Élément n'est pas trouvé.
|
||||
* @returns Un Élément.
|
||||
*/
|
||||
export const mustGetEleInDocument = <E extends Element = Element>(selecteur: string): E =>
|
||||
pipe(
|
||||
recupereElementDocumentEither<E>(selecteur),
|
||||
recupereElementOuLeve,
|
||||
);
|
||||
|
||||
export const mustGetEleInParent = (parent: ParentElement) => <E extends HTMLElement>(selector: string) =>
|
||||
pipe(recupereElementAvecSelecteur(parent)<E>(selector), recupereElementOuLeve);
|
||||
|
||||
/**
|
||||
* Fonction utilitaire pour récupérer des Éléments selon un sélecteur au sein du Document.
|
||||
*
|
||||
* @param selecteur Le sélecteur des Éléments recherchés.
|
||||
* @throws Une SyntaxError si aucun Élément n'est trouvé.
|
||||
* @returns Un tableau d'Éléments.
|
||||
*/
|
||||
export const mustGetElesInDocument = <E extends Element = Element>(selecteur: string): Array<E> =>
|
||||
pipe(
|
||||
recupereElementsDocumentEither<E>(selecteur),
|
||||
recupereElementsOuLeve,
|
||||
);
|
||||
|
||||
export const setButtonLoadingState = (button: HTMLButtonElement, isLoading: boolean): void => {
|
||||
logger.debug("majEtatChargementBouton", button, isLoading);
|
||||
if (isLoading) {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
button.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
button.setAttribute(ATTRIBUT_CHARGEMENT, "");
|
||||
// Lance un cycle d'animation sur le texte de chargement
|
||||
lanceAnimationCycleLoading(button, 500);
|
||||
} else {
|
||||
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
||||
button.removeAttribute(ATTRIBUT_CHARGEMENT);
|
||||
button.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
}
|
||||
};
|
||||
|
||||
export const estErreurHttp = (erreur: unknown) =>
|
||||
erreur instanceof BadRequestError
|
||||
|| erreur instanceof ForbiddenError
|
||||
|| erreur instanceof NotFoundError
|
||||
|| erreur instanceof ServerError
|
||||
|| erreur instanceof UnauthorizedError;
|
||||
|
||||
export const estErreurFetch = (erreur: unknown) =>
|
||||
erreur instanceof DOMException
|
||||
|| erreur instanceof TypeError
|
||||
|| erreur instanceof Error;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
import type { WCStoreCartTotals, WCStoreShippingRateShippingRate } from "../types/api/cart";
|
||||
|
||||
import { ADRESSES_MAJ, CODE_PROMO_MAJ, METHODES_LIVRAISON_MAJ, TOTAUX_MAJ } from "../../constantes/evenements";
|
||||
import { ADRESSES_MAJ, CODE_PROMO_MAJ, SHIPPING_RATES_UPDATED, TOTALS_UPDATED } from "../../constantes/evenements";
|
||||
|
||||
export const ADRESSES_MAJ_EVENT = new CustomEvent(ADRESSES_MAJ, {});
|
||||
export const CODE_PROMO_MAJ_EVENT = new CustomEvent(CODE_PROMO_MAJ, {});
|
||||
|
||||
export const MethodesLivraisonMajEvent = (methodes: ReadonlyArray<WCStoreShippingRateShippingRate>) =>
|
||||
new CustomEvent(METHODES_LIVRAISON_MAJ, { detail: { methodes } });
|
||||
// Interfaces
|
||||
|
||||
export interface MethodesLivraisonMajEvent extends Event {
|
||||
detail: { methodes: ReadonlyArray<WCStoreShippingRateShippingRate> };
|
||||
export interface UpdatedShippingRatesEvent extends Event {
|
||||
detail: { refresh_methods: boolean; shipping_rates: ReadonlyArray<WCStoreShippingRateShippingRate> };
|
||||
}
|
||||
export interface UpdatedTotalsEvent extends Event {
|
||||
detail: { totals: WCStoreCartTotals };
|
||||
}
|
||||
|
||||
export const TotauxMajEvent = (totaux: WCStoreCartTotals) => new CustomEvent(TOTAUX_MAJ, { detail: { totaux } });
|
||||
// Méthodes
|
||||
|
||||
export interface TotauxMajEvent extends Event {
|
||||
detail: { totaux: WCStoreCartTotals };
|
||||
}
|
||||
export const createUpdatedShippingRatesEvent = (
|
||||
shipping_rates: ReadonlyArray<WCStoreShippingRateShippingRate>,
|
||||
refresh_methods: boolean,
|
||||
): UpdatedShippingRatesEvent =>
|
||||
new CustomEvent(SHIPPING_RATES_UPDATED, { detail: { refresh_methods, shipping_rates } });
|
||||
|
||||
export const createUpdatedTotalsEvent = (totals: WCStoreCartTotals): UpdatedTotalsEvent =>
|
||||
new CustomEvent(TOTALS_UPDATED, { detail: { totals } });
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import type { GenericSchema, InferOutput, ValiError } from "valibot";
|
|||
|
||||
import { Either, Maybe } from "purify-ts";
|
||||
|
||||
import { eitherJsonParse } from "./dom.ts";
|
||||
import { safeJsonParse } from "./dom.ts";
|
||||
import { ErreurEntreeInexistante, type NonExistingKeyError } from "./erreurs.ts";
|
||||
import { eitherValiParseCurried } from "./validation.ts";
|
||||
import { safeSchemaParse, safeSchemaParseCurried } from "./validation.ts";
|
||||
|
||||
export type GetLocalStorage<S extends GenericSchema> = Either<ErreursGetLocalStorage<S>, InferOutput<S>>;
|
||||
type ErreursGetLocalStorage<S extends GenericSchema> =
|
||||
|
|
@ -24,13 +24,18 @@ export const eitherGetLocalStorage = (cle: string): Either<NonExistingKeyError,
|
|||
.fromNullable(localStorage.getItem(cle))
|
||||
.toEither(ErreurEntreeInexistante(`Clé ${cle} absente dans le stockage de session.`));
|
||||
|
||||
export const setLocalStorage = <V>(cle: string, valeur: V): Either<DOMException, V> =>
|
||||
export const eitherSetLocalStorage = <V>(cle: string, valeur: V): Either<DOMException, V> =>
|
||||
Either.encase<DOMException, V>(() => {
|
||||
localStorage.setItem(cle, JSON.stringify(valeur));
|
||||
return valeur;
|
||||
});
|
||||
|
||||
export const getAndParseLocalStorage = <S extends GenericSchema>(cle: string, schema: S): GetLocalStorage<S> =>
|
||||
eitherGetLocalStorage(cle)
|
||||
.chain(eitherJsonParse)
|
||||
.chain(eitherValiParseCurried(schema));
|
||||
export const getLocalStorageByKey = <S extends GenericSchema>(key: string, schema: S): GetLocalStorage<S> =>
|
||||
eitherGetLocalStorage(key)
|
||||
.chain(safeJsonParse)
|
||||
.chain(safeSchemaParseCurried(schema));
|
||||
|
||||
export const setLocalStorageByKey =
|
||||
<S extends GenericSchema>(key: string, schema: S) =>
|
||||
(value: unknown): Either<DOMException | ValiError<S>, InferOutput<S>> =>
|
||||
safeSchemaParse(value, schema).chain(v => eitherSetLocalStorage(key, v));
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import type {
|
|||
MessageMajContenuPanier,
|
||||
MessageMajContenuPanierDonnees,
|
||||
} from "./types/messages";
|
||||
import type { ReponseSimplifiee } from "./types/reseau";
|
||||
import type { SimplifiedResponse } from "./types/reseau";
|
||||
|
||||
import { NOM_CANAL_BOUTON_PANIER, NOM_CANAL_CONTENU_PANIER, TYPES_MESSAGES } from "../constantes/messages.ts";
|
||||
import { reporteErreur } from "./erreurs.ts";
|
||||
import { WCErrorSchema } from "./schemas/api/erreurs.ts";
|
||||
import { MessageMajBoutonPanierSchema, MessageMajContenuPanierSchema } from "./schemas/messages.ts";
|
||||
import { eitherValiParse } from "./validation.ts";
|
||||
import { safeSchemaParse } from "./validation.ts";
|
||||
|
||||
const canalPostMessage = (canal: BroadcastChannel, message: unknown): BroadcastChannel => {
|
||||
canal.postMessage(message);
|
||||
|
|
@ -77,7 +77,7 @@ export const valideMessageMajContenuPanier = (
|
|||
.ifLeft(erreur => reporteErreur(erreur));
|
||||
|
||||
// Correspondances
|
||||
export const reponseEstCodeErreurWC = (reponse: ReponseSimplifiee, codeErreurWC: string): boolean =>
|
||||
eitherValiParse(reponse, WCErrorSchema)
|
||||
export const reponseEstCodeErreurWC = (reponse: SimplifiedResponse, codeErreurWC: string): boolean =>
|
||||
safeSchemaParse(reponse, WCErrorSchema)
|
||||
.map(v => v.body.code === codeErreurWC)
|
||||
.orDefault(false);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { EitherAsync } from "purify-ts";
|
|||
import { match, P } from "ts-pattern";
|
||||
import { type GenericSchema, parse } from "valibot";
|
||||
|
||||
import type { HttpCodeErrors, ReponseSimplifiee } from "./types/reseau";
|
||||
import type { HttpCodeErrors, SimplifiedResponse } from "./types/reseau";
|
||||
|
||||
import { ENTETE_WC_NONCE } from "../constantes/api.ts";
|
||||
import {
|
||||
|
|
@ -126,7 +126,27 @@ export const postBackend = (args: ArgumentsPostBackendWC): Promise<Response> =>
|
|||
},
|
||||
);
|
||||
|
||||
export const eitherAsyncFetch = (f: Promise<Response>): EitherAsync<DOMException | TypeError, Response> =>
|
||||
export const prefilledPostBackend =
|
||||
(nonce: string, authString?: string) =>
|
||||
(route: string, body: BodyInit, needsAuthString: boolean): Promise<Response> =>
|
||||
fetch(
|
||||
route,
|
||||
{
|
||||
body: body,
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
[ENTETE_WC_NONCE]: nonce,
|
||||
...(authString && needsAuthString && { Authorization: `Basic ${authString}` }),
|
||||
},
|
||||
method: "POST",
|
||||
mode: "same-origin",
|
||||
signal: AbortSignal.timeout(5000),
|
||||
},
|
||||
);
|
||||
|
||||
export const safeFetch = (f: Promise<Response>): EitherAsync<DOMException | TypeError, Response> =>
|
||||
EitherAsync<DOMException | TypeError, Response>(async () => await f);
|
||||
|
||||
// TODO: Ne traite pas du tout les Erreurs
|
||||
|
|
@ -146,14 +166,14 @@ export const traiteReponseBackendWCSelonCodesHTTP = <R, S extends GenericSchema<
|
|||
.otherwise(e => pipe(e, ErreurInconnue, leveErreur<UnknownError>));
|
||||
|
||||
// Réponses Simplifiées
|
||||
export const creeReponseSimplifiee = async (reponse: Response): Promise<ReponseSimplifiee> => {
|
||||
export const newPartialResponse = async (reponse: Response): Promise<SimplifiedResponse> => {
|
||||
return {
|
||||
body: await reponse.json(),
|
||||
status: reponse.status,
|
||||
};
|
||||
};
|
||||
|
||||
export const traiteErreursBackendWooCommerce = (rs: ReponseSimplifiee): HttpCodeErrors => {
|
||||
export const traiteErreursBackendWooCommerce = (rs: SimplifiedResponse): HttpCodeErrors => {
|
||||
return match(rs)
|
||||
.with({ status: 400 }, () => new BadRequestError())
|
||||
.with({ status: 401 }, () => new UnauthorizedError())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { Maybe } from "purify-ts";
|
||||
|
||||
/**
|
||||
* Retourne sous forme d'un Maybe le premier élément d'un tableau.
|
||||
* @param xs Le tableau dont on souhaite le premier élément.
|
||||
* @returns Un Just avec le premier élément du tableau, un None sinon.
|
||||
*/
|
||||
export const first = <T>(xs: Array<T>): Maybe<T> => Maybe.fromNullable(xs.at(0));
|
||||
|
||||
export const find = <T>(predicateFn: (_1: T) => boolean) => (xs: Array<T>): Maybe<T> =>
|
||||
Maybe.fromNullable(xs.find(predicateFn));
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
import type { InferOutput } from "valibot";
|
||||
|
||||
import type { WCStoreCartItemSchema, WCStoreCartSchema, WCStoreCartTotalsSchema } from "../../schemas/api/cart.ts";
|
||||
import type { WCStoreShippingRateShippingRateSchema } from "../../schemas/api/couts-livraison.ts";
|
||||
import type {
|
||||
WCStoreShippingRateSchema,
|
||||
WCStoreShippingRateShippingRateSchema,
|
||||
} from "../../schemas/api/couts-livraison.ts";
|
||||
|
||||
export type WCStoreCart = InferOutput<typeof WCStoreCartSchema>;
|
||||
export type WCStoreCartItem = InferOutput<typeof WCStoreCartItemSchema>;
|
||||
export type WCStoreCartTotals = InferOutput<typeof WCStoreCartTotalsSchema>;
|
||||
export type WCStoreShippingRate = InferOutput<typeof WCStoreShippingRateSchema>;
|
||||
export type WCStoreShippingRateShippingRate = InferOutput<typeof WCStoreShippingRateShippingRateSchema>;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
/** Type union des parents possibles pour un `querySelector`. */
|
||||
export type ElementParent = Document | Element;
|
||||
export type ParentElement = Document | Element;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export type EtatsPageGenerique = {
|
||||
export type GenericPageState = {
|
||||
/** Une chaîne pour l'authentification de requêtes vers la nouvelle API (v3) du backend WooCommerce. */
|
||||
authString: string;
|
||||
/** Un nonce pour l'authentification de requêtes API vers le backend WooCommerce. */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export type HttpCodeErrors =
|
|||
| ServerError
|
||||
| UnauthorizedError;
|
||||
|
||||
export interface ReponseSimplifiee {
|
||||
export interface SimplifiedResponse {
|
||||
body: unknown;
|
||||
status: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,7 @@
|
|||
import { D, pipe } from "@mobily/ts-belt";
|
||||
import { D } from "@mobily/ts-belt";
|
||||
import { type Either, Maybe } from "purify-ts";
|
||||
|
||||
import { ATTRIBUT_CHARGEMENT, ATTRIBUT_DESACTIVE } from "../constantes/dom.ts";
|
||||
import { lanceAnimationCycleLoading } from "./animations.ts";
|
||||
import {
|
||||
recupereElementAvecSelecteur,
|
||||
recupereElementOuLeve,
|
||||
recupereElementsAvecSelecteur,
|
||||
recupereElementsOuLeve,
|
||||
} from "./dom.ts";
|
||||
import {
|
||||
BadRequestError,
|
||||
CleNonTrouveError,
|
||||
ForbiddenError,
|
||||
NotFoundError,
|
||||
ServerError,
|
||||
UnauthorizedError,
|
||||
} from "./erreurs.ts";
|
||||
|
||||
export const recupereElementsDocumentEither: <E extends Element = Element>(
|
||||
selecteur: string,
|
||||
) => Either<SyntaxError, Array<E>> = recupereElementsAvecSelecteur(document);
|
||||
|
||||
export const recupereElementDocumentEither: <E extends Element = Element>(
|
||||
selecteur: string,
|
||||
) => Either<SyntaxError, E> = recupereElementAvecSelecteur(document);
|
||||
import { CleNonTrouveError } from "./erreurs";
|
||||
|
||||
/**
|
||||
* TODO
|
||||
|
|
@ -35,55 +12,3 @@ export const propEither = <T, K extends keyof T>(cle: K) => (donnees: T): Either
|
|||
.toEither(
|
||||
new CleNonTrouveError(`La clé « ${String(cle)} » n'a pas été trouvé dans l'objet.`),
|
||||
);
|
||||
|
||||
/**
|
||||
* Fonction utilitaire pour récupérer un Élément selon un sélecteur au sein du Document.
|
||||
*
|
||||
* @param selecteur Le sélecteur de l'Élément recherché.
|
||||
* @throws Une SyntaxError si l'Élément n'est pas trouvé.
|
||||
* @returns Un Élément.
|
||||
*/
|
||||
export const recupereEleOuLeve = <E extends Element = Element>(selecteur: string): E =>
|
||||
pipe(
|
||||
recupereElementDocumentEither<E>(selecteur),
|
||||
recupereElementOuLeve,
|
||||
);
|
||||
|
||||
/**
|
||||
* Fonction utilitaire pour récupérer des Éléments selon un sélecteur au sein du Document.
|
||||
*
|
||||
* @param selecteur Le sélecteur des Éléments recherchés.
|
||||
* @throws Une SyntaxError si aucun Élément n'est trouvé.
|
||||
* @returns Un tableau d'Éléments.
|
||||
*/
|
||||
export const recupereElesOuLeve = <E extends Element = Element>(selecteur: string): Array<E> =>
|
||||
pipe(
|
||||
recupereElementsDocumentEither<E>(selecteur),
|
||||
recupereElementsOuLeve,
|
||||
);
|
||||
|
||||
export const majEtatChargementBouton = (bouton: HTMLButtonElement, activation: boolean): void => {
|
||||
if (activation) {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
bouton.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
bouton.setAttribute(ATTRIBUT_CHARGEMENT, "");
|
||||
// Lance un cycle d'animation sur le texte de chargement
|
||||
lanceAnimationCycleLoading(bouton, 500);
|
||||
} else {
|
||||
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
||||
bouton.removeAttribute(ATTRIBUT_CHARGEMENT);
|
||||
bouton.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
}
|
||||
};
|
||||
|
||||
export const estErreurHttp = (erreur: unknown) =>
|
||||
erreur instanceof BadRequestError
|
||||
|| erreur instanceof ForbiddenError
|
||||
|| erreur instanceof NotFoundError
|
||||
|| erreur instanceof ServerError
|
||||
|| erreur instanceof UnauthorizedError;
|
||||
|
||||
export const estErreurFetch = (erreur: unknown) =>
|
||||
erreur instanceof DOMException
|
||||
|| erreur instanceof TypeError
|
||||
|| erreur instanceof Error;
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
import { Either } from "purify-ts";
|
||||
import { type GenericSchema, type InferOutput, parse, type ValiError } from "valibot";
|
||||
|
||||
export const eitherValiParse = <Schema extends GenericSchema>(
|
||||
export const safeSchemaParse = <Schema extends GenericSchema>(
|
||||
valeur: unknown,
|
||||
schema: Schema,
|
||||
): Either<ValiError<Schema>, InferOutput<Schema>> => Either.encase(() => parse(schema, valeur));
|
||||
|
||||
export const eitherValiParseCurried =
|
||||
export const safeSchemaParseCurried =
|
||||
<S extends GenericSchema>(schema: S) => (valeur: unknown): Either<ValiError<S>, InferOutput<S>> =>
|
||||
Either.encase(() => parse(schema, valeur));
|
||||
|
|
|
|||
21
web/app/themes/haiku-atelier-2024/src/scripts/logging.ts
Normal file
21
web/app/themes/haiku-atelier-2024/src/scripts/logging.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import chalk from "chalk";
|
||||
import log, { type Logger } from "loglevel";
|
||||
import prefix from "loglevel-plugin-prefix";
|
||||
|
||||
const colors = {
|
||||
DEBUG: chalk.cyan,
|
||||
ERROR: chalk.red,
|
||||
INFO: chalk.blue,
|
||||
TRACE: chalk.magenta,
|
||||
WARN: chalk.yellow,
|
||||
};
|
||||
|
||||
export const logger: Logger = log.noConflict() as Logger;
|
||||
logger.enableAll(true);
|
||||
logger.setDefaultLevel("DEBUG");
|
||||
prefix.reg(logger);
|
||||
prefix.apply(logger, {
|
||||
format(level, _, timestamp) {
|
||||
return `${chalk.gray(`[${timestamp}]`)} ${colors[level.toUpperCase()](level)}`;
|
||||
},
|
||||
});
|
||||
|
|
@ -1,30 +1,20 @@
|
|||
import { pipe } from "@mobily/ts-belt";
|
||||
import { find as arrayFind, map as arrayMap } from "@mobily/ts-belt/Array";
|
||||
import { find as arrayFind } from "@mobily/ts-belt/Array";
|
||||
import { map as dictMap, values as dictValues } from "@mobily/ts-belt/Dict";
|
||||
import { trim as stringTrim } from "@mobily/ts-belt/String";
|
||||
import { EitherAsync, Maybe } from "purify-ts";
|
||||
import { match, P } from "ts-pattern";
|
||||
import { type AnySchema, ValiError } from "valibot";
|
||||
|
||||
import type { WCStoreCart, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
||||
import type { WCStoreBillingAddress, WCStoreShippingAddress } from "../lib/types/api/adresses";
|
||||
import type { WCStoreCart, WCStoreShippingRate, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
||||
import type { WCStoreCartUpdateCustomerArgs } from "../lib/types/api/cart-update-customer";
|
||||
import type { WCStoreShippingRateShippingRates } from "../lib/types/api/couts-livraison";
|
||||
import type { WCV3Order, WCV3OrdersArgs } from "../lib/types/api/v3/orders";
|
||||
import type { EtatsPageGenerique } from "../lib/types/pages";
|
||||
import type { GenericPageState } from "../lib/types/pages";
|
||||
import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau";
|
||||
|
||||
import { ROUTE_API_MAJ_CLIENT, ROUTE_API_NOUVELLE_COMMANDES } from "../constantes/api";
|
||||
import {
|
||||
ATTRIBUT_CHARGEMENT,
|
||||
ATTRIBUT_LIVRAISON_VALIDEE,
|
||||
SELECTEUR_BOUTON_ACTIONS_FORMULAIRE,
|
||||
SELECTEUR_BOUTON_SEPARATION_ADRESSES,
|
||||
SELECTEUR_CONTENEUR_METHODES_LIVRAISON,
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
SELECTEUR_FORMULAIRE_PANIER,
|
||||
SELECTEUR_INSTRUCTIONS_CLIENT,
|
||||
SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES,
|
||||
} from "../constantes/dom";
|
||||
import { ATTRIBUT_CHARGEMENT, ATTRIBUT_LIVRAISON_VALIDEE } from "../constantes/dom";
|
||||
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages";
|
||||
import {
|
||||
ERREUR_ADRESSE_MAUVAIS_CODE_POSTAL,
|
||||
|
|
@ -32,42 +22,36 @@ import {
|
|||
ERREUR_GENERIQUE_RESEAU,
|
||||
ERREUR_GENERIQUE_SOUMISSION_ADRESSES,
|
||||
} from "../constantes/messages-utilisateur";
|
||||
import { leveErreur, type NonExistingKeyError, reporteErreur, reporteEtJournaliseErreur } from "../lib/erreurs";
|
||||
import { estErreurFetch, estErreurHttp, setButtonLoadingState } from "../lib/dom";
|
||||
import { reporteEtJournaliseErreur } from "../lib/erreurs";
|
||||
import { ErreurAdresseInvalide } from "../lib/erreurs/adresses";
|
||||
import { ADRESSES_MAJ_EVENT, MethodesLivraisonMajEvent, TotauxMajEvent } from "../lib/evenements/panier";
|
||||
import { getAndParseLocalStorage } from "../lib/local-storage";
|
||||
import {
|
||||
ADRESSES_MAJ_EVENT,
|
||||
createUpdatedShippingRatesEvent,
|
||||
createUpdatedTotalsEvent,
|
||||
} from "../lib/evenements/panier";
|
||||
import { emetUniqueMessageBroadcastChannel } from "../lib/messages";
|
||||
import { diviseParCent } from "../lib/nombres";
|
||||
import { creeReponseSimplifiee, eitherAsyncFetch, postBackend, traiteErreursBackendWooCommerce } from "../lib/reseau";
|
||||
import { newPartialResponse, prefilledPostBackend, safeFetch, traiteErreursBackendWooCommerce } from "../lib/reseau";
|
||||
import { find, first } from "../lib/safe-arrays";
|
||||
import { WCStoreCartSchema } from "../lib/schemas/api/cart";
|
||||
import { WCStoreCartUpdateCustomerArgsSchema } from "../lib/schemas/api/cart-update-customer";
|
||||
import { WCStoreShippingRateShippingRatesSchema } from "../lib/schemas/api/couts-livraison";
|
||||
import { estWCAddressError } from "../lib/schemas/api/erreurs";
|
||||
import { WCV3OrdersArgsSchema, WCV3OrderSchema } from "../lib/schemas/api/v3/orders";
|
||||
import {
|
||||
estErreurFetch,
|
||||
estErreurHttp,
|
||||
majEtatChargementBouton,
|
||||
recupereElementsDocumentEither,
|
||||
recupereEleOuLeve,
|
||||
} from "../lib/utils";
|
||||
import { eitherValiParse } from "../lib/validation";
|
||||
import { genereHtmlMethodesLivraison } from "./scripts-page-panier-methodes-livraison";
|
||||
import { safeSchemaParse } from "../lib/validation";
|
||||
import { logger } from "../logging";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
|
||||
|
||||
interface Addresses {
|
||||
billing_address: WCStoreBillingAddress;
|
||||
shipping_address: WCStoreShippingAddress;
|
||||
}
|
||||
|
||||
// @ts-expect-error -- États injectés par le modèle PHP
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
|
||||
const ETATS_PAGE: EtatsPageGenerique = _etats;
|
||||
|
||||
const E = {
|
||||
BOUTON_ACTIONS_FORMULAIRE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_ACTIONS_FORMULAIRE),
|
||||
BOUTON_SEPARATION_ADRESSES: recupereEleOuLeve<HTMLInputElement>(SELECTEUR_BOUTON_SEPARATION_ADRESSES),
|
||||
CONTENEUR_METHODES_LIVRAISON: recupereEleOuLeve<HTMLFieldSetElement>(SELECTEUR_CONTENEUR_METHODES_LIVRAISON),
|
||||
ENTREES_PANIER_EITHER: recupereElementsDocumentEither<HTMLElement>(SELECTEUR_ENTREES_PANIER),
|
||||
FORMULAIRE_PANIER: recupereEleOuLeve<HTMLFormElement>(SELECTEUR_FORMULAIRE_PANIER),
|
||||
INSTRUCTIONS_CLIENT: recupereEleOuLeve<HTMLTextAreaElement>(SELECTEUR_INSTRUCTIONS_CLIENT),
|
||||
MESSAGE_ADRESSES: recupereEleOuLeve<HTMLParagraphElement>(SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES),
|
||||
};
|
||||
|
||||
const ETATS_PAGE: GenericPageState = _etats;
|
||||
const postBackend = prefilledPostBackend(ETATS_PAGE.nonce, ETATS_PAGE.authString);
|
||||
/**
|
||||
* Initialise les Émetteurs d'Événements sur divers parties du Panier.
|
||||
*
|
||||
|
|
@ -75,126 +59,138 @@ const E = {
|
|||
*
|
||||
* @returns void
|
||||
*/
|
||||
export const initialiseEmetteursEvenementsFormulairePanier = (): void => {
|
||||
export const initCartFormEventEmiters = (): void => {
|
||||
logger.debug("ADRESSES", "initCartFormEventEmiters");
|
||||
E.FORMULAIRE_PANIER.addEventListener("change", (): void => {
|
||||
logger.info("ADRESSES", "Changement du Formulaire Panier");
|
||||
Maybe
|
||||
.fromFalsy(E.FORMULAIRE_PANIER.checkValidity())
|
||||
.ifJust((): boolean => window.dispatchEvent(ADRESSES_MAJ_EVENT));
|
||||
});
|
||||
};
|
||||
|
||||
export const initialiseBoutonCalculLivraison = (): void => {
|
||||
// Déclenche la requête pour la soumission des adresses
|
||||
export const getAddressesFromForm = (formFields: Record<string, string>, areAddressesMerged: boolean): Addresses => {
|
||||
logger.debug("ADDRESSES", "getAddressesFromForm");
|
||||
return {
|
||||
billing_address: {
|
||||
address_1: formFields["facturation-adresse"] ?? formFields["livraison-adresse"] ?? "",
|
||||
address_2: "",
|
||||
city: formFields["facturation-ville"] ?? formFields["livraison-ville"] ?? "",
|
||||
company: "",
|
||||
country: areAddressesMerged ? formFields["facturation-pays"] ?? "" : formFields["livraison-pays"] ?? "",
|
||||
email: formFields["facturation-email"] ?? formFields["livraison-email"] ?? "",
|
||||
first_name: formFields["facturation-prenom"] ?? formFields["livraison-prenom"] ?? "",
|
||||
last_name: formFields["facturation-nom"] ?? formFields["livraison-nom"] ?? "",
|
||||
phone: formFields["facturation-telephone"] ?? formFields["livraison-telephone"] ?? "",
|
||||
postcode: formFields["facturation-code-postal"] ?? formFields["livraison-code-postal"] ?? "",
|
||||
state: formFields["facturation-region-etat"] ?? formFields["livraison-region-etat"] ?? "",
|
||||
},
|
||||
shipping_address: {
|
||||
address_1: formFields["livraison-adresse"] ?? "",
|
||||
address_2: "",
|
||||
city: formFields["livraison-ville"] ?? "",
|
||||
company: "",
|
||||
country: formFields["livraison-pays"] ?? "",
|
||||
first_name: formFields["livraison-prenom"] ?? "",
|
||||
last_name: formFields["livraison-nom"] ?? "",
|
||||
phone: formFields["livraison-telephone"] ?? "",
|
||||
postcode: formFields["livraison-code-postal"] ?? "",
|
||||
state: formFields["livraison-region-etat"] ?? "",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const initShippingCalculationButton = (): void => {
|
||||
logger.debug("ADRESSES", "initShippingCalculationButton");
|
||||
// Déclenche au clic sur le Bouton de soumission du Formulaire la requête pour le calcul des frais de livraison
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.addEventListener("click", (event: Event): void => {
|
||||
logger.info("ADRESSES", "Demande de calcul des frais de livraison de la commande");
|
||||
Maybe
|
||||
// Ne fais rien si le Formulaire n'est pas valide
|
||||
.fromFalsy(E.FORMULAIRE_PANIER.checkValidity())
|
||||
// Ne fais rien si la livraison a déjà été validée
|
||||
.chainNullable((): boolean | null =>
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE) ? null : true
|
||||
.chainNullable((): boolean | undefined =>
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE) ? undefined : true
|
||||
)
|
||||
.ifJust((): void => {
|
||||
event.preventDefault();
|
||||
|
||||
/** Les données du Formulaire sans caractères vides. */
|
||||
const donneesFormulaire: Record<string, string> = pipe(
|
||||
/** Les données du Formulaire transformées pour la requête vers le Backend. */
|
||||
const formArgs: WCStoreCartUpdateCustomerArgs = pipe(
|
||||
Object.fromEntries(new FormData(E.FORMULAIRE_PANIER)) as Record<string, string>,
|
||||
dictMap(stringTrim),
|
||||
fields => dictMap(fields, stringTrim),
|
||||
fields => getAddressesFromForm(fields, E.BOUTON_SEPARATION_ADRESSES.checked),
|
||||
);
|
||||
/** Les données du Formulaire transformées en arguments pour la requête vers le Backend. */
|
||||
const argumentsFormulaire: WCStoreCartUpdateCustomerArgs = {
|
||||
billing_address: {
|
||||
address_1: donneesFormulaire["facturation-adresse"] ?? donneesFormulaire["livraison-adresse"] ?? "",
|
||||
address_2: "",
|
||||
city: donneesFormulaire["facturation-ville"] ?? donneesFormulaire["livraison-ville"] ?? "",
|
||||
company: "",
|
||||
country: E.BOUTON_SEPARATION_ADRESSES.checked
|
||||
? donneesFormulaire["facturation-pays"] ?? ""
|
||||
: donneesFormulaire["livraison-pays"] ?? "",
|
||||
email: donneesFormulaire["facturation-email"] ?? donneesFormulaire["livraison-email"] ?? "",
|
||||
first_name: donneesFormulaire["facturation-prenom"] ?? donneesFormulaire["livraison-prenom"] ?? "",
|
||||
last_name: donneesFormulaire["facturation-nom"] ?? donneesFormulaire["livraison-nom"] ?? "",
|
||||
phone: donneesFormulaire["facturation-telephone"] ?? donneesFormulaire["livraison-telephone"] ?? "",
|
||||
postcode: donneesFormulaire["facturation-code-postal"] ?? donneesFormulaire["livraison-code-postal"] ?? "",
|
||||
state: donneesFormulaire["facturation-region-etat"] ?? donneesFormulaire["livraison-region-etat"] ?? "",
|
||||
},
|
||||
shipping_address: {
|
||||
address_1: donneesFormulaire["livraison-adresse"] ?? "",
|
||||
address_2: "",
|
||||
city: donneesFormulaire["livraison-ville"] ?? "",
|
||||
company: "",
|
||||
country: donneesFormulaire["livraison-pays"] ?? "",
|
||||
first_name: donneesFormulaire["livraison-prenom"] ?? "",
|
||||
last_name: donneesFormulaire["livraison-nom"] ?? "",
|
||||
phone: donneesFormulaire["livraison-telephone"] ?? "",
|
||||
postcode: donneesFormulaire["livraison-code-postal"] ?? "",
|
||||
state: donneesFormulaire["livraison-region-etat"] ?? "",
|
||||
},
|
||||
};
|
||||
logger.debug("ADRESSES", "initShippingCalculationButton", "formArgs", formArgs);
|
||||
|
||||
// Réalise la requête et traite sa réponse
|
||||
void EitherAsync
|
||||
.liftEither(eitherValiParse(argumentsFormulaire, WCStoreCartUpdateCustomerArgsSchema))
|
||||
.ifRight((): void => {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
majEtatChargementBouton(E.BOUTON_ACTIONS_FORMULAIRE, true);
|
||||
})
|
||||
.liftEither(safeSchemaParse(formArgs, WCStoreCartUpdateCustomerArgsSchema))
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
|
||||
.chain((args: WCStoreCartUpdateCustomerArgs) =>
|
||||
eitherAsyncFetch(
|
||||
postBackend({ corps: JSON.stringify(args), nonce: ETATS_PAGE.nonce, route: ROUTE_API_MAJ_CLIENT }),
|
||||
)
|
||||
safeFetch(postBackend(ROUTE_API_MAJ_CLIENT, JSON.stringify(args), false))
|
||||
)
|
||||
.chain((rs: Response) =>
|
||||
EitherAsync<ErreurAdresseInvalide | HttpCodeErrors, unknown>(async ({ throwE }): Promise<unknown> =>
|
||||
match(await creeReponseSimplifiee(rs))
|
||||
.chain((rs: Response) => {
|
||||
logger.debug("ADRESSES", "initShippingCalculationButton", "rs", rs);
|
||||
return EitherAsync<ErreurAdresseInvalide | HttpCodeErrors, unknown>(async ({ throwE }): Promise<unknown> =>
|
||||
match(await newPartialResponse(rs))
|
||||
.with({ status: 200 }, (rs): unknown => rs.body)
|
||||
.with(
|
||||
{ body: P.when(body => estWCAddressError(body)), status: 400 },
|
||||
(rs): never => throwE(new ErreurAdresseInvalide(rs.body.data.params)),
|
||||
)
|
||||
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs)))
|
||||
)
|
||||
)
|
||||
.chain((body: unknown) => EitherAsync.liftEither(eitherValiParse(body, WCStoreCartSchema)))
|
||||
);
|
||||
})
|
||||
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCStoreCartSchema)))
|
||||
.ifRight((cart: WCStoreCart): void => {
|
||||
const localStorageSelectedMethod = getAndParseLocalStorage(
|
||||
"shipping_rates",
|
||||
WCStoreShippingRateShippingRatesSchema,
|
||||
)
|
||||
.toMaybe()
|
||||
.chainNullable(arrayFind(m => m.selected));
|
||||
logger.debug("ADRESSES", "initShippingCalculationButton", "cart", cart);
|
||||
|
||||
const updatedMethods = Maybe
|
||||
.fromNullable(cart.shipping_rates.at(0))
|
||||
.chainNullable(xs => xs.shipping_rates)
|
||||
.map(arrayMap(m => {
|
||||
// Sélectionne la Méthode précédemment choisie
|
||||
localStorageSelectedMethod.ifJust(mls => {
|
||||
m.selected = m.method_id === mls.method_id;
|
||||
});
|
||||
m.price = diviseParCent(m.price);
|
||||
return m;
|
||||
}))
|
||||
.ifJust(xs => {
|
||||
// Met à jour les Méthodes de livraison
|
||||
window.dispatchEvent(MethodesLivraisonMajEvent(xs));
|
||||
/** La méthode de livraison sélectionnée dans le LocalStorage */
|
||||
const oldSelectedRateLS = getShippingRatesLS()
|
||||
.chain(find(sr => sr.selected))
|
||||
.ifJust(sr => logger.debug("ADRESSES", "initShippingCalculationButton", "oldSelectedRateLS", sr));
|
||||
|
||||
// Met à jour les Totaux
|
||||
const selectedMethod = xs.find(m => m.selected)!;
|
||||
const newTotals = {
|
||||
...cart.totals,
|
||||
total_discount: diviseParCent(cart.totals.total_discount),
|
||||
total_items: diviseParCent(cart.totals.total_items),
|
||||
total_price: diviseParCent(cart.totals.total_items) - diviseParCent(cart.totals.total_discount)
|
||||
+ selectedMethod.price,
|
||||
total_shipping: selectedMethod.price,
|
||||
};
|
||||
window.dispatchEvent(TotauxMajEvent(newTotals));
|
||||
})
|
||||
/* Les méthodes de livraison mises à jour avec le nouveau choix de l'Utilisateur. */
|
||||
const updatedRates = first(cart.shipping_rates)
|
||||
.chainNullable((sr: WCStoreShippingRate) => sr.shipping_rates)
|
||||
.map((srs: Array<WCStoreShippingRateShippingRate>) =>
|
||||
srs.map((sr: WCStoreShippingRateShippingRate, index: number) => {
|
||||
// Sélectionne la nouvelle méthode demandée OU la première si le LocalStorage n'a pas été défini
|
||||
oldSelectedRateLS.caseOf({
|
||||
Just: sm => {
|
||||
sr.selected = sr.method_id === sm.method_id;
|
||||
},
|
||||
Nothing: () => {
|
||||
sr.selected = index === 0;
|
||||
},
|
||||
});
|
||||
|
||||
// Formate le prix correctement
|
||||
sr.price = diviseParCent(sr.price);
|
||||
|
||||
return sr;
|
||||
})
|
||||
)
|
||||
.orDefault([]);
|
||||
logger.debug("ADRESSES", "initShippingCalculationButton", "updatedRates", updatedRates);
|
||||
// Met à jour les Méthodes de livraison dans le LocalStorage et le DOM
|
||||
window.dispatchEvent(createUpdatedShippingRatesEvent(updatedRates, true));
|
||||
|
||||
// Met à jour les Totaux
|
||||
const newShippingPrice = updatedRates.find(m => m.selected)?.price ?? 0;
|
||||
const newTotals = {
|
||||
...cart.totals,
|
||||
total_discount: diviseParCent(cart.totals.total_discount),
|
||||
total_items: diviseParCent(cart.totals.total_items),
|
||||
total_price: diviseParCent(cart.totals.total_items) - diviseParCent(cart.totals.total_discount)
|
||||
+ newShippingPrice,
|
||||
total_shipping: newShippingPrice,
|
||||
};
|
||||
logger.debug("ADRESSES", "initShippingCalculationButton", "newTotals", newTotals);
|
||||
window.dispatchEvent(createUpdatedTotalsEvent(newTotals));
|
||||
|
||||
// Affiche les nouvelles Méthodes à l'Utilisateur
|
||||
genereHtmlMethodesLivraison(E.CONTENEUR_METHODES_LIVRAISON, updatedMethods);
|
||||
// Réinitialise le Message affiché à l'Utiisateur
|
||||
E.MESSAGE_ADRESSES.textContent = " ";
|
||||
// Active le Bouton pour la création de la Commande
|
||||
|
|
@ -206,10 +202,12 @@ export const initialiseBoutonCalculLivraison = (): void => {
|
|||
match(err)
|
||||
.with(P.instanceOf(ValiError), (e: ValiError<AnySchema>): void => {
|
||||
reporteEtJournaliseErreur(e);
|
||||
console.error(e.issues);
|
||||
E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||
})
|
||||
.with(P.instanceOf(ErreurAdresseInvalide), (e: ErreurAdresseInvalide): void => {
|
||||
reporteEtJournaliseErreur(e);
|
||||
// TODO: Créer une fonction pour traiter les cas d'erreurs spécifiques
|
||||
match(e.problemes)
|
||||
.when(
|
||||
// TODO: Créer une fonction utilitaire
|
||||
|
|
@ -236,158 +234,103 @@ export const initialiseBoutonCalculLivraison = (): void => {
|
|||
E.BOUTON_ACTIONS_FORMULAIRE.textContent = "Submit your addresses";
|
||||
},
|
||||
)
|
||||
.finally((): void => {
|
||||
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
||||
majEtatChargementBouton(E.BOUTON_ACTIONS_FORMULAIRE, false);
|
||||
})
|
||||
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
||||
.finally((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, false))
|
||||
.run();
|
||||
})
|
||||
.ifNothing((): void => event.preventDefault());
|
||||
});
|
||||
};
|
||||
|
||||
export const initialiseBoutonCreationCommande = (): void => {
|
||||
// Créé la Commande au clic sur le Bouton
|
||||
export const initOrderCreationButton = (): void => {
|
||||
logger.debug("ADRESSES", "initOrderCreationButton");
|
||||
// Créé la Commande au clic sur le Bouton de soumission du Formulaire
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.addEventListener("click", (event: Event): void => {
|
||||
logger.info("ADRESSES", "Demande de création de commande");
|
||||
Maybe
|
||||
// Ne fais rien si le Formulaire n'est pas valide
|
||||
.fromFalsy(
|
||||
E.FORMULAIRE_PANIER.checkValidity() && E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE),
|
||||
)
|
||||
// Active l'état de chargement
|
||||
.ifJust((): void => {
|
||||
event.preventDefault();
|
||||
|
||||
/** Les données du Formulaire sans caractères vides. */
|
||||
const donneesFormulaire: Record<string, string> = pipe(
|
||||
const formFields: Record<string, string> = pipe(
|
||||
Object.fromEntries(new FormData(E.FORMULAIRE_PANIER)) as Record<string, string>,
|
||||
dictMap(stringTrim),
|
||||
);
|
||||
|
||||
/** La méthode de livraison sélectionnée ; interrompt la requête si incorrect. */
|
||||
const methodeLivraison: WCStoreShippingRateShippingRate = getAndParseLocalStorage(
|
||||
"shipping_rates",
|
||||
WCStoreShippingRateShippingRatesSchema,
|
||||
)
|
||||
.ifLeft((erreur: NonExistingKeyError | SyntaxError | ValiError<AnySchema>): void => {
|
||||
match(erreur)
|
||||
.with(P.instanceOf(ValiError), (e): void => {
|
||||
reporteErreur(e);
|
||||
console.error(e.issues);
|
||||
})
|
||||
.otherwise(reporteEtJournaliseErreur);
|
||||
|
||||
localStorage.removeItem("shipping_rates");
|
||||
|
||||
const selectedRateLS: WCStoreShippingRateShippingRate = getShippingRatesLS()
|
||||
.ifNothing((): void => {
|
||||
// Rétablis le Formulaire à son état d'origine
|
||||
E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||
emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
|
||||
|
||||
// Interrompt la requête
|
||||
leveErreur(erreur);
|
||||
throw new Error("LocalStorage indisponible.");
|
||||
})
|
||||
.toMaybe()
|
||||
.chainNullable((methodes: WCStoreShippingRateShippingRates) => methodes.at(0))
|
||||
.chain(first)
|
||||
.orDefault({});
|
||||
logger.debug("ADRESSES", "initOrderCreationButton", "selectedRateLS", selectedRateLS);
|
||||
|
||||
/** Les Produits du Panier. */
|
||||
const produitsPanier = E.ENTREES_PANIER_EITHER
|
||||
const cartProducts = E.ENTREES_PANIER
|
||||
.orDefault([])
|
||||
.map((entree: HTMLElement) => ({
|
||||
product_id: Number(entree.getAttribute("data-id-produit")),
|
||||
quantity: Number(entree.getAttribute("data-quantite")),
|
||||
...(entree.getAttribute("data-id-variation") && {
|
||||
variation_id: Number(entree.getAttribute("data-id-variation")),
|
||||
.map((entry: HTMLElement) => ({
|
||||
product_id: Number(entry.getAttribute("data-id-produit")),
|
||||
quantity: Number(entry.getAttribute("data-quantite")),
|
||||
...(entry.getAttribute("data-id-variation") && {
|
||||
variation_id: Number(entry.getAttribute("data-id-variation")),
|
||||
}),
|
||||
}));
|
||||
logger.debug("ADRESSES", "initOrderCreationButton", "cartProducts", cartProducts);
|
||||
|
||||
/** Les données du Formulaire transformées en arguments pour la requête vers le Backend. */
|
||||
const argumentsFormulaire: WCV3OrdersArgs = {
|
||||
billing: {
|
||||
address_1: donneesFormulaire["facturation-adresse"] ?? donneesFormulaire["livraison-adresse"] ?? "",
|
||||
address_2: "",
|
||||
city: donneesFormulaire["facturation-ville"] ?? donneesFormulaire["livraison-ville"] ?? "",
|
||||
company: "",
|
||||
country: E.BOUTON_SEPARATION_ADRESSES.checked
|
||||
? donneesFormulaire["facturation-pays"] ?? ""
|
||||
: donneesFormulaire["livraison-pays"] ?? "",
|
||||
email: donneesFormulaire["facturation-email"] ?? donneesFormulaire["livraison-email"] ?? "",
|
||||
first_name: donneesFormulaire["facturation-prenom"] ?? donneesFormulaire["livraison-prenom"] ?? "",
|
||||
last_name: donneesFormulaire["facturation-nom"] ?? donneesFormulaire["livraison-nom"] ?? "",
|
||||
phone: donneesFormulaire["facturation-telephone"] ?? donneesFormulaire["livraison-telephone"] ?? "",
|
||||
postcode: donneesFormulaire["facturation-code-postal"] ?? donneesFormulaire["livraison-code-postal"] ?? "",
|
||||
state: donneesFormulaire["facturation-region-etat"] ?? donneesFormulaire["livraison-region-etat"] ?? "",
|
||||
},
|
||||
currency: methodeLivraison.currency_code,
|
||||
/** Les données du Formulaire transformées pour la requête vers le Backend. */
|
||||
const formArgs: WCV3OrdersArgs = {
|
||||
...getAddressesFromForm(formFields, E.BOUTON_SEPARATION_ADRESSES.checked),
|
||||
currency: selectedRateLS.currency_code,
|
||||
customer_note: E.INSTRUCTIONS_CLIENT.value,
|
||||
line_items: produitsPanier,
|
||||
shipping: {
|
||||
address_1: donneesFormulaire["livraison-adresse"] ?? "",
|
||||
address_2: "",
|
||||
city: donneesFormulaire["livraison-ville"] ?? "",
|
||||
company: "",
|
||||
country: donneesFormulaire["livraison-pays"] ?? "",
|
||||
first_name: donneesFormulaire["livraison-prenom"] ?? "",
|
||||
last_name: donneesFormulaire["livraison-nom"] ?? "",
|
||||
phone: donneesFormulaire["livraison-telephone"] ?? "",
|
||||
postcode: donneesFormulaire["livraison-code-postal"] ?? "",
|
||||
state: donneesFormulaire["livraison-region-etat"] ?? "",
|
||||
},
|
||||
line_items: cartProducts,
|
||||
shipping_lines: [
|
||||
{
|
||||
method_id: methodeLivraison.method_id,
|
||||
method_title: methodeLivraison.name,
|
||||
total: pipe(diviseParCent(methodeLivraison.price), String),
|
||||
method_id: selectedRateLS.method_id,
|
||||
method_title: selectedRateLS.name,
|
||||
total: pipe(diviseParCent(selectedRateLS.price), String),
|
||||
},
|
||||
],
|
||||
};
|
||||
logger.debug("ADRESSES", "initOrderCreationButton", "formArgs", formArgs);
|
||||
|
||||
// Réalise la requête et traite sa réponse
|
||||
void EitherAsync
|
||||
// 1. Valide les Arguments de la Requête
|
||||
.liftEither(eitherValiParse(argumentsFormulaire, WCV3OrdersArgsSchema))
|
||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
|
||||
.ifRight((): void => {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
majEtatChargementBouton(E.BOUTON_ACTIONS_FORMULAIRE, true);
|
||||
})
|
||||
// 3. Exécute la requête via fetch sous form d'EitherAsync
|
||||
.liftEither(safeSchemaParse(formArgs, WCV3OrdersArgsSchema))
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
|
||||
.chain((args: WCV3OrdersArgs) =>
|
||||
eitherAsyncFetch(
|
||||
postBackend({
|
||||
authString: ETATS_PAGE.authString,
|
||||
corps: JSON.stringify(args),
|
||||
nonce: ETATS_PAGE.nonce,
|
||||
route: ROUTE_API_NOUVELLE_COMMANDES,
|
||||
}),
|
||||
)
|
||||
safeFetch(postBackend(ROUTE_API_NOUVELLE_COMMANDES, JSON.stringify(args), true))
|
||||
)
|
||||
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
|
||||
.chain((reponse: Response) =>
|
||||
.chain((rs: Response) =>
|
||||
EitherAsync<HttpCodeErrors, unknown>(async ({ throwE }): Promise<unknown> =>
|
||||
match(await creeReponseSimplifiee(reponse))
|
||||
match(await newPartialResponse(rs))
|
||||
.with({ status: 201 }, (rs): unknown => rs.body)
|
||||
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs)))
|
||||
)
|
||||
)
|
||||
// 5. Vérifie le Schéma de la Réponse
|
||||
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCV3OrderSchema)))
|
||||
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||
.ifRight((commande: WCV3Order): void => {
|
||||
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCV3OrderSchema)))
|
||||
.ifRight((order: WCV3Order): void => {
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.removeAttribute(ATTRIBUT_CHARGEMENT);
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.textContent = "OK!";
|
||||
E.MESSAGE_ADRESSES.textContent = " ";
|
||||
|
||||
// Redirige vers Stripe
|
||||
// Maybe
|
||||
// .fromNullable(new URL(`https://${window.location.host}/checkout`))
|
||||
// .ifJust(url => url.searchParams.append("order_key", commande.order_key))
|
||||
// .ifJust(url => url.searchParams.append("order_id", String(commande.id)))
|
||||
// .ifJust(url => location.assign(url));
|
||||
Maybe
|
||||
.fromNullable(new URL(`https://${window.location.host}/checkout`))
|
||||
.ifJust(url => url.searchParams.append("order_key", order.order_key))
|
||||
.ifJust(url => url.searchParams.append("order_id", String(order.id)))
|
||||
.ifJust(url => location.assign(url));
|
||||
})
|
||||
// 7. Traite les Erreurs et affiche un message à l'Utilisateur
|
||||
.ifLeft((erreur: FetchErrors | HttpCodeErrors | ValiError<AnySchema>): void => {
|
||||
match(erreur)
|
||||
.ifLeft((err: FetchErrors | HttpCodeErrors | ValiError<AnySchema>): void => {
|
||||
match(err)
|
||||
.with(P.instanceOf(ValiError), (e: ValiError<AnySchema>): void => {
|
||||
reporteEtJournaliseErreur(e);
|
||||
console.error(e.issues);
|
||||
|
|
@ -404,7 +347,7 @@ export const initialiseBoutonCreationCommande = (): void => {
|
|||
.exhaustive();
|
||||
|
||||
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
||||
majEtatChargementBouton(E.BOUTON_ACTIONS_FORMULAIRE, false);
|
||||
setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, false);
|
||||
E.BOUTON_ACTIONS_FORMULAIRE.textContent = "Checkout";
|
||||
})
|
||||
.run();
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import { ValiError } from "valibot";
|
|||
import type { WCStoreCart } from "../lib/types/api/cart";
|
||||
import type { WCStoreCartApplyCouponArgs } from "../lib/types/api/cart-apply-coupon";
|
||||
import type { WCStoreCartRemoveCouponArgs } from "../lib/types/api/cart-remove-coupon";
|
||||
import type { EtatsPageGenerique } from "../lib/types/pages";
|
||||
import type { ReponseSimplifiee } from "../lib/types/reseau";
|
||||
import type { GenericPageState } from "../lib/types/pages";
|
||||
import type { SimplifiedResponse } from "../lib/types/reseau";
|
||||
|
||||
import { ROUTE_API_APPLIQUE_COUPON, ROUTE_API_RETIRE_COUPON } from "../constantes/api";
|
||||
import { ERREUR_CODE_PROMO_INVALIDE } from "../constantes/api/erreurs";
|
||||
|
|
@ -21,16 +21,10 @@ import {
|
|||
ATTRIBUT_DESACTIVE,
|
||||
ATTRIBUT_HIDDEN,
|
||||
SELECTEUR_BOUTON_CODE_PROMO,
|
||||
SELECTEUR_CHAMP_CODE_PROMO,
|
||||
SELECTEUR_ENSEMBLE_CODE_PROMO,
|
||||
SELECTEUR_MESSAGE_CODE_PROMO,
|
||||
SELECTEUR_TOTAL_PANIER,
|
||||
SELECTEUR_TOTAL_REDUCTION,
|
||||
SELECTEUR_TOTAL_REDUCTION_VALEUR,
|
||||
} from "../constantes/dom";
|
||||
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages";
|
||||
import { lanceAnimationCycleLoading } from "../lib/animations";
|
||||
import { accorderCibleASelecteur } from "../lib/dom";
|
||||
import { targetMatchesSelector } from "../lib/dom";
|
||||
import { reporteErreur, ServerError } from "../lib/erreurs";
|
||||
import { ErreurCodePromoInvalide } from "../lib/erreurs/codes-promo";
|
||||
import { CODE_PROMO_MAJ_EVENT } from "../lib/evenements/panier";
|
||||
|
|
@ -41,22 +35,12 @@ import { postBackend } from "../lib/reseau";
|
|||
import { WCStoreCartSchema } from "../lib/schemas/api/cart";
|
||||
import { WCStoreCartApplyCouponArgsSchema } from "../lib/schemas/api/cart-apply-coupon";
|
||||
import { WCStoreCartRemoveCouponArgsSchema } from "../lib/schemas/api/cart-remove-coupon";
|
||||
import { recupereEleOuLeve } from "../lib/utils";
|
||||
import { eitherValiParse } from "../lib/validation";
|
||||
import { safeSchemaParse } from "../lib/validation";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
|
||||
// @ts-expect-error -- États injectés par le modèle PHP
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
|
||||
const ETATS_PAGE: EtatsPageGenerique = _etats;
|
||||
|
||||
const E = {
|
||||
BOUTON_CODE_PROMO: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_CODE_PROMO),
|
||||
CHAMP_CODE_PROMO: recupereEleOuLeve<HTMLInputElement>(SELECTEUR_CHAMP_CODE_PROMO),
|
||||
ENSEMBLE_CODE_PROMO: recupereEleOuLeve<HTMLFormElement>(SELECTEUR_ENSEMBLE_CODE_PROMO),
|
||||
MESSAGE_CODE_PROMO: recupereEleOuLeve<HTMLParagraphElement>(SELECTEUR_MESSAGE_CODE_PROMO),
|
||||
TOTAL_PANIER: recupereEleOuLeve<HTMLParagraphElement>(SELECTEUR_TOTAL_PANIER),
|
||||
TOTAL_REDUCTION_LIGNE: recupereEleOuLeve<HTMLDivElement>(SELECTEUR_TOTAL_REDUCTION),
|
||||
TOTAL_REDUCTION_VALEUR: recupereEleOuLeve<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
};
|
||||
const ETATS_PAGE: GenericPageState = _etats;
|
||||
|
||||
export const initialiseElementsCodePromo = (): void => {
|
||||
const recupereValeurCodePromo = (): null | string =>
|
||||
|
|
@ -75,7 +59,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
.with(
|
||||
{
|
||||
cible: P.when((cible: EventTarget | null) =>
|
||||
accorderCibleASelecteur<HTMLButtonElement>(cible, SELECTEUR_BOUTON_CODE_PROMO)
|
||||
targetMatchesSelector<HTMLButtonElement>(cible, SELECTEUR_BOUTON_CODE_PROMO)
|
||||
),
|
||||
codePromoPresent: false,
|
||||
valeurCodePromo: P.string,
|
||||
|
|
@ -83,7 +67,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
({ valeurCodePromo }) =>
|
||||
void EitherAsync
|
||||
// Vérifie le Schéma des arguments
|
||||
.liftEither(eitherValiParse({ code: valeurCodePromo }, WCStoreCartApplyCouponArgsSchema))
|
||||
.liftEither(safeSchemaParse({ code: valeurCodePromo }, WCStoreCartApplyCouponArgsSchema))
|
||||
.ifRight(() => {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
|
|
@ -105,7 +89,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
// Traite les cas d'Erreur
|
||||
.chain((reponse: Response) =>
|
||||
EitherAsync<ErreurCodePromoInvalide | ServerError, unknown>(async ({ throwE }) => {
|
||||
const reponseSimplifiee: ReponseSimplifiee = {
|
||||
const reponseSimplifiee: SimplifiedResponse = {
|
||||
body: await reponse.json(),
|
||||
status: reponse.status,
|
||||
};
|
||||
|
|
@ -121,7 +105,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
})
|
||||
)
|
||||
// Vérifie le Schéma de la Réponse du backend
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCStoreCartSchema)))
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCStoreCartSchema)))
|
||||
// Déclenche les mises à jour du DOM avec les données du nouveau Panier
|
||||
.ifRight((panier: WCStoreCart) => {
|
||||
E.ENSEMBLE_CODE_PROMO.toggleAttribute(ATTRIBUT_CODE_PROMO_PRESENT);
|
||||
|
|
@ -186,13 +170,13 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
// Un code promo est présent sous forme de chaîne
|
||||
.with(
|
||||
{
|
||||
cible: P.when(cible => accorderCibleASelecteur<HTMLButtonElement>(cible, SELECTEUR_BOUTON_CODE_PROMO)),
|
||||
cible: P.when(cible => targetMatchesSelector<HTMLButtonElement>(cible, SELECTEUR_BOUTON_CODE_PROMO)),
|
||||
codePromoPresent: true,
|
||||
valeurCodePromo: P.string,
|
||||
},
|
||||
({ valeurCodePromo }) =>
|
||||
void EitherAsync
|
||||
.liftEither(eitherValiParse({ code: valeurCodePromo }, WCStoreCartRemoveCouponArgsSchema))
|
||||
.liftEither(safeSchemaParse({ code: valeurCodePromo }, WCStoreCartRemoveCouponArgsSchema))
|
||||
.ifRight(() => {
|
||||
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_CHARGEMENT, "");
|
||||
|
|
@ -212,7 +196,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
return await reponse.json();
|
||||
})
|
||||
)
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCStoreCartSchema)))
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCStoreCartSchema)))
|
||||
.ifRight((panier: WCStoreCart) => {
|
||||
E.ENSEMBLE_CODE_PROMO.toggleAttribute(ATTRIBUT_CODE_PROMO_PRESENT);
|
||||
E.ENSEMBLE_CODE_PROMO.reset();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
import {
|
||||
SELECTEUR_BOUTON_ACTIONS_FORMULAIRE,
|
||||
SELECTEUR_BOUTON_CODE_PROMO,
|
||||
SELECTEUR_BOUTON_SEPARATION_ADRESSES,
|
||||
SELECTEUR_CHAMP_CODE_PROMO,
|
||||
SELECTEUR_CONTENEUR_METHODES_LIVRAISON,
|
||||
SELECTEUR_CONTENEUR_PANIER,
|
||||
SELECTEUR_ENSEMBLE_CODE_PROMO,
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
SELECTEUR_FORMULAIRE_FACTURATION,
|
||||
SELECTEUR_FORMULAIRE_PANIER,
|
||||
SELECTEUR_INSTRUCTIONS_CLIENT,
|
||||
SELECTEUR_MESSAGE_CODE_PROMO,
|
||||
SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES,
|
||||
SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT,
|
||||
SELECTEUR_SOUS_TOTAL_PRODUITS,
|
||||
SELECTEUR_TOTAL_PANIER,
|
||||
SELECTEUR_TOTAL_REDUCTION,
|
||||
SELECTEUR_TOTAL_REDUCTION_VALEUR,
|
||||
} from "../constantes/dom";
|
||||
import { mustGetEleInDocument, recupereElementsDocumentEither } from "../lib/dom";
|
||||
|
||||
export const E = {
|
||||
BOUTON_ACTIONS_FORMULAIRE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_ACTIONS_FORMULAIRE),
|
||||
BOUTON_CODE_PROMO: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_CODE_PROMO),
|
||||
BOUTON_SEPARATION_ADRESSES: mustGetEleInDocument<HTMLInputElement>(SELECTEUR_BOUTON_SEPARATION_ADRESSES),
|
||||
CHAMP_CODE_PROMO: mustGetEleInDocument<HTMLInputElement>(SELECTEUR_CHAMP_CODE_PROMO),
|
||||
CONTENEUR_METHODES_LIVRAISON: mustGetEleInDocument<HTMLFieldSetElement>(SELECTEUR_CONTENEUR_METHODES_LIVRAISON),
|
||||
CONTENEUR_PANIER: mustGetEleInDocument<HTMLElement>(SELECTEUR_CONTENEUR_PANIER),
|
||||
ENSEMBLE_CODE_PROMO: mustGetEleInDocument<HTMLFormElement>(SELECTEUR_ENSEMBLE_CODE_PROMO),
|
||||
ENTREES_PANIER: recupereElementsDocumentEither<HTMLElement>(
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
),
|
||||
FORMULAIRE_FACTURATION: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_FORMULAIRE_FACTURATION),
|
||||
FORMULAIRE_PANIER: mustGetEleInDocument<HTMLFormElement>(SELECTEUR_FORMULAIRE_PANIER),
|
||||
INSTRUCTIONS_CLIENT: mustGetEleInDocument<HTMLTextAreaElement>(SELECTEUR_INSTRUCTIONS_CLIENT),
|
||||
MESSAGE_ADRESSES: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES),
|
||||
MESSAGE_CODE_PROMO: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_MESSAGE_CODE_PROMO),
|
||||
SOUS_TOTAL_LIVRAISON_VALEUR: mustGetEleInDocument<HTMLElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT),
|
||||
SOUS_TOTAL_PRODUITS: mustGetEleInDocument<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_PRODUITS_VALEUR: mustGetEleInDocument<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_REDUCTION: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
SOUS_TOTAL_REDUCTION_VALEUR: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
TOTAL_PANIER: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_TOTAL_PANIER),
|
||||
TOTAL_PANIER_VALEUR: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_PANIER),
|
||||
TOTAL_REDUCTION_LIGNE: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_TOTAL_REDUCTION),
|
||||
TOTAL_REDUCTION_VALEUR: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
};
|
||||
|
|
@ -1,28 +1,15 @@
|
|||
import { Either } from "purify-ts";
|
||||
|
||||
import type { MethodesLivraisonMajEvent, TotauxMajEvent } from "../lib/evenements/panier";
|
||||
import type { UpdatedShippingRatesEvent, UpdatedTotalsEvent } from "../lib/evenements/panier";
|
||||
|
||||
import {
|
||||
ATTRIBUT_LIVRAISON_VALIDEE,
|
||||
SELECTEUR_BOUTON_ACTIONS_FORMULAIRE,
|
||||
SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT,
|
||||
SELECTEUR_SOUS_TOTAL_PRODUITS,
|
||||
SELECTEUR_TOTAL_PANIER,
|
||||
SELECTEUR_TOTAL_REDUCTION_VALEUR,
|
||||
} from "../constantes/dom";
|
||||
import { ADRESSES_MAJ, CODE_PROMO_MAJ, METHODES_LIVRAISON_MAJ, TOTAUX_MAJ } from "../constantes/evenements";
|
||||
import { ATTRIBUT_LIVRAISON_VALIDEE } from "../constantes/dom";
|
||||
import { ADRESSES_MAJ, CODE_PROMO_MAJ, SHIPPING_RATES_UPDATED, TOTALS_UPDATED } from "../constantes/evenements";
|
||||
import { reporteEtJournaliseErreur } from "../lib/erreurs";
|
||||
import { setLocalStorage } from "../lib/local-storage";
|
||||
import { eitherSetLocalStorage } from "../lib/local-storage";
|
||||
import { formateEnEuros } from "../lib/nombres";
|
||||
import { recupereEleOuLeve } from "../lib/utils";
|
||||
|
||||
const E = {
|
||||
BOUTON_ACTIONS_FORMULAIRE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_ACTIONS_FORMULAIRE),
|
||||
SOUS_TOTAL_LIVRAISON_VALEUR: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT),
|
||||
SOUS_TOTAL_PRODUITS_VALEUR: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_REDUCTION_VALEUR: recupereEleOuLeve<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
TOTAL_PANIER_VALEUR: recupereEleOuLeve<HTMLSpanElement>(SELECTEUR_TOTAL_PANIER),
|
||||
};
|
||||
import { logger } from "../logging";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
import { generateShippingRatesHTML } from "./scripts-page-panier-methodes-livraison";
|
||||
|
||||
/**
|
||||
* Réinitialise le bouton de soumission du Formulaire en revenant à l'étape de validation des adresses.
|
||||
|
|
@ -48,23 +35,29 @@ export const souscrisEvenementsPanier = (): void => {
|
|||
reinitialiseValidationLivraison();
|
||||
});
|
||||
|
||||
window.addEventListener(METHODES_LIVRAISON_MAJ, (event: Event): void => {
|
||||
window.addEventListener(SHIPPING_RATES_UPDATED, (event: Event): void => {
|
||||
Either
|
||||
// La vérification du schéma se fait à l'émission
|
||||
.encase(() => (event as MethodesLivraisonMajEvent).detail.methodes)
|
||||
.chain(ms => setLocalStorage("shipping_rates", ms))
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
.encase(() => (event as UpdatedShippingRatesEvent).detail)
|
||||
// Met à jour le DOM
|
||||
.ifRight(ms => console.debug("methods", ms))
|
||||
.toMaybe()
|
||||
.chainNullable(xs => xs.find(m => m.selected));
|
||||
.ifRight(event => {
|
||||
logger.info("ShippingRatesUpdatedEvent", "shipping_rates", event);
|
||||
// Met à jour les Méthodes à l'Utilisateur si demandé
|
||||
// Il peut y en avoir aucune
|
||||
if (event.refresh_methods) {
|
||||
generateShippingRatesHTML(E.CONTENEUR_METHODES_LIVRAISON, event.shipping_rates);
|
||||
}
|
||||
})
|
||||
// Met à jour le LocalStorage
|
||||
.chain(event => eitherSetLocalStorage("shipping_rates", event.shipping_rates))
|
||||
.ifLeft(reporteEtJournaliseErreur);
|
||||
});
|
||||
|
||||
window.addEventListener(TOTAUX_MAJ, (event: Event): void => {
|
||||
window.addEventListener(TOTALS_UPDATED, (event: Event): void => {
|
||||
Either
|
||||
// La vérification du Schéma se fait à l'émission
|
||||
.encase(() => (event as TotauxMajEvent).detail.totaux)
|
||||
.chain(ts => setLocalStorage("totals", ts))
|
||||
.encase(() => (event as UpdatedTotalsEvent).detail.totals)
|
||||
.chain(ts => eitherSetLocalStorage("totals", ts))
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
// Met à jour le DOM
|
||||
.ifRight(ts => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import type { Maybe } from "purify-ts";
|
||||
|
||||
import type { WCStoreShippingRateShippingRates } from "../lib/types/api/couts-livraison";
|
||||
|
||||
import { reporteEtJournaliseErreur } from "../lib/erreurs";
|
||||
import { getLocalStorageByKey, setLocalStorageByKey } from "../lib/local-storage";
|
||||
import { WCStoreShippingRateShippingRatesSchema } from "../lib/schemas/api/couts-livraison";
|
||||
|
||||
/* LS = LocalStorage */
|
||||
|
||||
export const getShippingRatesLS = (): Maybe<WCStoreShippingRateShippingRates> =>
|
||||
getLocalStorageByKey("shipping_rates", WCStoreShippingRateShippingRatesSchema)
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
.toMaybe();
|
||||
|
||||
export const setShippingRatesLS = (
|
||||
shippingRates: WCStoreShippingRateShippingRates,
|
||||
): Maybe<WCStoreShippingRateShippingRates> =>
|
||||
setLocalStorageByKey("shipping_rates", WCStoreShippingRateShippingRatesSchema)(shippingRates)
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
.toMaybe();
|
||||
|
|
@ -1,81 +1,73 @@
|
|||
import { find as arrayFind, forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
|
||||
import { forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
|
||||
import { html, render, type TemplateResult } from "lit-html";
|
||||
|
||||
import type { WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
||||
import type { WCStoreCartTotals, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
||||
import type { WCStoreShippingRateShippingRates } from "../lib/types/api/couts-livraison";
|
||||
|
||||
import { ATTRIBUT_HIDDEN, SELECTEUR_CONTENEUR_METHODES_LIVRAISON } from "../constantes/dom";
|
||||
import { recupereElementsAvecSelecteur } from "../lib/dom";
|
||||
import { ATTRIBUT_HIDDEN } from "../constantes/dom";
|
||||
import { forEach, map } from "../lib/arrays";
|
||||
import { getDOMElementsWithSelector } from "../lib/dom";
|
||||
import { reporteEtJournaliseErreur } from "../lib/erreurs";
|
||||
import { MethodesLivraisonMajEvent, TotauxMajEvent } from "../lib/evenements/panier";
|
||||
import { getAndParseLocalStorage } from "../lib/local-storage";
|
||||
import { createUpdatedShippingRatesEvent, createUpdatedTotalsEvent } from "../lib/evenements/panier";
|
||||
import { getLocalStorageByKey } from "../lib/local-storage";
|
||||
import { formateEnEuros } from "../lib/nombres";
|
||||
import { find } from "../lib/safe-arrays";
|
||||
import { WCStoreCartTotalsSchema } from "../lib/schemas/api/cart";
|
||||
import { WCStoreShippingRateShippingRatesSchema } from "../lib/schemas/api/couts-livraison";
|
||||
import { recupereEleOuLeve } from "../lib/utils";
|
||||
import { logger } from "../logging";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
|
||||
|
||||
const E = {
|
||||
CONTENEUR_METHODES_LIVRAISON: recupereEleOuLeve<HTMLFieldSetElement>(SELECTEUR_CONTENEUR_METHODES_LIVRAISON),
|
||||
};
|
||||
export const initShippingRatesChoicesActions = (): void => {
|
||||
logger.debug("METHODES_LIVRAISON", "initShippingRatesChoicesActions");
|
||||
getDOMElementsWithSelector(E.CONTENEUR_METHODES_LIVRAISON)<HTMLInputElement>("input")
|
||||
.ifRight(forEach((el: HTMLInputElement): void =>
|
||||
el.addEventListener("click", (event: MouseEvent): void => {
|
||||
logger.info("METHODES_LIVRAISON", "Clic sur un choix de méthode de livraison");
|
||||
|
||||
/**
|
||||
* Créé un Événement MethodesLivraisonMaj déclenché quand l'Utilisateur change son choix avec en corps un object ShippingRates
|
||||
* À l'Émission, sauvegarde les méthodes mises à jour dans le LocalStorage et met à jour le DOM
|
||||
* Le déclenchement de la mise à jour des adresses déclenche aussi cet Événement.
|
||||
* Le LocalStorage est comparé avec la Réponse, et si la méthode jusqe-là sélectionnée est présente dans la Réponse, la mise à jour du DOM doit se faire en conservant ce choix.
|
||||
*/
|
||||
|
||||
export const initialiseBoutonsChoixMethodesLivraison = (): void => {
|
||||
recupereElementsAvecSelecteur(E.CONTENEUR_METHODES_LIVRAISON)<HTMLInputElement>("input").ifRight(
|
||||
arrayForEach((i): void =>
|
||||
i.addEventListener("click", (evenement: MouseEvent): void => {
|
||||
getAndParseLocalStorage("shipping_rates", WCStoreShippingRateShippingRatesSchema)
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
.map(arrayMap(m => {
|
||||
// Met à jour la Méthode sélectionnée
|
||||
m.selected = m.method_id === (evenement.target as HTMLInputElement).value;
|
||||
return m;
|
||||
// Récupère les méthodes du LocalStorage et les met à jour avec le nouveau choix
|
||||
getShippingRatesLS()
|
||||
// Met à jour la sélection de la Méthode
|
||||
.map(map((sr: WCStoreShippingRateShippingRate) => {
|
||||
sr.selected = sr.method_id === (event.target as HTMLInputElement).value;
|
||||
return sr;
|
||||
}))
|
||||
// Émet l'Événement de màj des Méthodes
|
||||
.ifRight(xs => window.dispatchEvent(MethodesLivraisonMajEvent(xs)))
|
||||
.toMaybe()
|
||||
.chainNullable(arrayFind(m => m.selected))
|
||||
.ifJust(m => {
|
||||
// Émet l'Événement de màj des Totaux
|
||||
getAndParseLocalStorage("totals", WCStoreCartTotalsSchema)
|
||||
// Met à jour les Méthodes de livraison dans le LocalStorage et le DOM
|
||||
.ifJust((srs: WCStoreShippingRateShippingRates): void => {
|
||||
window.dispatchEvent(createUpdatedShippingRatesEvent(srs, false));
|
||||
})
|
||||
// Met à jour les totaux dans le LocalStorage et le DOM
|
||||
.chain(find(sr => sr.selected))
|
||||
.ifJust((sr: WCStoreShippingRateShippingRate): void => {
|
||||
getLocalStorageByKey("totals", WCStoreCartTotalsSchema)
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
.map(ts => {
|
||||
ts.total_shipping = m.price;
|
||||
ts.total_price = ts.total_items - ts.total_discount + m.price;
|
||||
.map((ts: WCStoreCartTotals): WCStoreCartTotals => {
|
||||
ts.total_shipping = sr.price;
|
||||
ts.total_price = ts.total_items - ts.total_discount + sr.price;
|
||||
return ts;
|
||||
})
|
||||
.ifRight(t => window.dispatchEvent(TotauxMajEvent(t)));
|
||||
.ifRight((ts: WCStoreCartTotals): void => {
|
||||
window.dispatchEvent(createUpdatedTotalsEvent(ts));
|
||||
});
|
||||
});
|
||||
})
|
||||
),
|
||||
);
|
||||
));
|
||||
};
|
||||
|
||||
export const genereHtmlMethodesLivraison = (
|
||||
conteneur: HTMLElement,
|
||||
methodes: ReadonlyArray<WCStoreShippingRateShippingRate>,
|
||||
export const generateShippingRatesHTML = (
|
||||
container: HTMLElement,
|
||||
shippingRates: ReadonlyArray<WCStoreShippingRateShippingRate>,
|
||||
): void => {
|
||||
// Cache les méthodes s'il n'y en a pas
|
||||
if (methodes.length === 0) {
|
||||
conteneur.setAttribute(ATTRIBUT_HIDDEN, "");
|
||||
// Cache les méthodes s'il n'y en a pas sans continuer
|
||||
if (shippingRates.length === 0) {
|
||||
container.setAttribute(ATTRIBUT_HIDDEN, "");
|
||||
return;
|
||||
}
|
||||
|
||||
// Retire les méthodes de livraison initiales
|
||||
recupereElementsAvecSelecteur(conteneur)("div[data-methode-initiale]").ifRight(arrayForEach(div => div.remove()));
|
||||
getDOMElementsWithSelector(container)("div[data-methode-initiale]").ifRight(arrayForEach(div => div.remove()));
|
||||
|
||||
const methodeSelectionnee: string = getAndParseLocalStorage("shipping_rates", WCStoreShippingRateShippingRatesSchema)
|
||||
.ifLeft(reporteEtJournaliseErreur)
|
||||
.toMaybe()
|
||||
.chainNullable(arrayFind(m => m.selected))
|
||||
.map(m => m.method_id)
|
||||
.orDefault("");
|
||||
|
||||
const methodesHtml: ReadonlyArray<TemplateResult> = arrayMap(methodes, methode => {
|
||||
const selectedShippingRate: string = shippingRates.find(sr => sr.selected)?.method_id ?? "";
|
||||
const shippingRatesHTML: ReadonlyArray<TemplateResult> = arrayMap(shippingRates, methode => {
|
||||
return html`
|
||||
<div>
|
||||
<input
|
||||
|
|
@ -83,15 +75,15 @@ export const genereHtmlMethodesLivraison = (
|
|||
name="choix-methode-livraison"
|
||||
type="radio"
|
||||
value="${methode.method_id}"
|
||||
.checked="${methode.method_id === methodeSelectionnee}"
|
||||
.checked="${methode.method_id === selectedShippingRate}"
|
||||
>
|
||||
<label for="methode-livraison-${methode.method_id}">${methode.name} (${formateEnEuros(methode.price)})</label>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// Ajoute les nouveaux Produits dans le DOM
|
||||
conteneur.removeAttribute(ATTRIBUT_HIDDEN);
|
||||
render(methodesHtml, conteneur);
|
||||
container.removeAttribute(ATTRIBUT_HIDDEN);
|
||||
render(shippingRatesHTML, container);
|
||||
// Recréé les Écouteurs de clic sur les choix de méthodes
|
||||
initialiseBoutonsChoixMethodesLivraison();
|
||||
initShippingRatesChoicesActions();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import { type AnySchema, ValiError } from "valibot";
|
|||
import type { WCStoreCart } from "../lib/types/api/cart";
|
||||
import type { WCStoreCartRemoveItemArgs } from "../lib/types/api/cart-remove-item";
|
||||
import type { WCStoreCartUpdateItemArgs } from "../lib/types/api/cart-update-item";
|
||||
import type { ElementParent } from "../lib/types/dom";
|
||||
import type { EtatsPageGenerique } from "../lib/types/pages";
|
||||
import type { FetchErrors } from "../lib/types/reseau";
|
||||
import type { GenericPageState } from "../lib/types/pages";
|
||||
import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau";
|
||||
|
||||
import { ROUTE_API_MAJ_ARTICLE_PANIER, ROUTE_API_RETIRE_ARTICLE_PANIER } from "../constantes/api";
|
||||
import {
|
||||
|
|
@ -21,10 +20,9 @@ import {
|
|||
SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SUPPRESSION_PANIER,
|
||||
SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
} from "../constantes/dom";
|
||||
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages";
|
||||
import { recupereElementAvecSelecteur, recupereElementOuLeve } from "../lib/dom";
|
||||
import { mustGetEleInParent } from "../lib/dom";
|
||||
import { BadRequestError, reporteErreur, ServerError } from "../lib/erreurs";
|
||||
import {
|
||||
emetMessageMajBoutonPanier,
|
||||
|
|
@ -32,164 +30,146 @@ import {
|
|||
emetUniqueMessageBroadcastChannel,
|
||||
} from "../lib/messages";
|
||||
import { diviseParCent } from "../lib/nombres";
|
||||
import { creeReponseSimplifiee, eitherAsyncFetch, postBackend } from "../lib/reseau";
|
||||
import { newPartialResponse, postBackend, safeFetch, traiteErreursBackendWooCommerce } from "../lib/reseau";
|
||||
import { WCStoreCartSchema } from "../lib/schemas/api/cart";
|
||||
import { WCStoreCartRemoveItemArgsSchema } from "../lib/schemas/api/cart-remove-item";
|
||||
import { WCStoreCartUpdateItemArgsSchema } from "../lib/schemas/api/cart-update-item";
|
||||
import { recupereElementsDocumentEither } from "../lib/utils";
|
||||
import { eitherValiParse } from "../lib/validation";
|
||||
import { safeSchemaParse } from "../lib/validation";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
|
||||
// @ts-expect-error -- États injectés par le modèle PHP
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
|
||||
const ETATS_PAGE: EtatsPageGenerique = _etats;
|
||||
const PAGE_STATE: GenericPageState = _etats;
|
||||
|
||||
type ElementsEntreePanier = {
|
||||
boutonAddition: HTMLButtonElement;
|
||||
boutonSoustraction: HTMLButtonElement;
|
||||
boutonSuppression: HTMLButtonElement;
|
||||
champQuantite: HTMLInputElement;
|
||||
type CartEntryInteractiveElements = {
|
||||
additionButton: HTMLButtonElement;
|
||||
deletionButton: HTMLButtonElement;
|
||||
quantityInput: HTMLInputElement;
|
||||
substractionButton: HTMLButtonElement;
|
||||
};
|
||||
|
||||
const E = {
|
||||
ENTREES_PANIER: recupereElementsDocumentEither<HTMLElement>(SELECTEUR_ENTREES_PANIER),
|
||||
};
|
||||
|
||||
// TODO: Tout ça est bien compliqué
|
||||
const recupereEleDansEntreeOuLeve = (entree: ElementParent) => <E extends HTMLElement>(selecteur: string) =>
|
||||
pipe(recupereElementAvecSelecteur(entree)<E>(selecteur), recupereElementOuLeve);
|
||||
|
||||
const recupereElementsEntreePanier = (entree: HTMLElement): ElementsEntreePanier => {
|
||||
const recupereElementDansEntree = recupereEleDansEntreeOuLeve(entree);
|
||||
const getCartEntryInteractiveEles = (entree: HTMLElement): CartEntryInteractiveElements => {
|
||||
const mustGetEle = mustGetEleInParent(entree);
|
||||
return {
|
||||
boutonAddition: recupereElementDansEntree<HTMLButtonElement>(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
boutonSoustraction: recupereElementDansEntree<HTMLButtonElement>(SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
boutonSuppression: recupereElementDansEntree<HTMLButtonElement>(SELECTEUR_BOUTON_SUPPRESSION_PANIER),
|
||||
champQuantite: recupereElementDansEntree<HTMLInputElement>(SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER),
|
||||
additionButton: mustGetEle<HTMLButtonElement>(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
deletionButton: mustGetEle<HTMLButtonElement>(SELECTEUR_BOUTON_SUPPRESSION_PANIER),
|
||||
quantityInput: mustGetEle<HTMLInputElement>(SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER),
|
||||
substractionButton: mustGetEle<HTMLButtonElement>(SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Met à jour l'état d'activation des Boutons d'action sur chaque Entrée du Panier.
|
||||
|
||||
* @param activation Le nouvel état d'activation (activé/désactivé).
|
||||
* @param activated Le nouvel état d'activation (activé/désactivé).
|
||||
* @returns Rien.
|
||||
*/
|
||||
export const majActivationBoutons = (activation: boolean) => (entrees: ReadonlyArray<ElementsEntreePanier>): void =>
|
||||
arrayForEach(entrees, (entree: ElementsEntreePanier): void => {
|
||||
if (activation) {
|
||||
// Active les Boutons
|
||||
Number(entree.champQuantite.value) === 1
|
||||
? entree.boutonSoustraction.setAttribute(ATTRIBUT_DESACTIVE, "")
|
||||
: entree.boutonSoustraction.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
entree.boutonAddition.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
entree.boutonSuppression.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
entree.boutonSuppression.textContent = "Remove";
|
||||
} else {
|
||||
// Désactive les Boutons
|
||||
entree.boutonSoustraction.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
entree.boutonAddition.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
entree.boutonSuppression.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
entree.boutonSuppression.textContent = "Loading";
|
||||
}
|
||||
});
|
||||
export const toggleCartEntryButtons =
|
||||
(activated: boolean) => (cartEntries: ReadonlyArray<CartEntryInteractiveElements>): void =>
|
||||
arrayForEach(cartEntries, (e: CartEntryInteractiveElements): void => {
|
||||
if (activated) {
|
||||
// Active les Boutons
|
||||
Number(e.quantityInput.value) === 1
|
||||
? e.substractionButton.setAttribute(ATTRIBUT_DESACTIVE, "")
|
||||
: e.substractionButton.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
e.additionButton.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
e.deletionButton.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||
e.deletionButton.textContent = "Remove";
|
||||
} else {
|
||||
// Désactive les Boutons
|
||||
e.substractionButton.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
e.additionButton.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
e.deletionButton.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
e.deletionButton.textContent = "Loading";
|
||||
}
|
||||
});
|
||||
|
||||
export const initialiseActionsEntreesPanier = (): void => {
|
||||
// Initialise des actions uniquement si des Entrées dans le Panier existent
|
||||
E.ENTREES_PANIER.ifRight((entrees: Array<HTMLElement>) =>
|
||||
arrayForEach(entrees, (entree: HTMLElement): void => {
|
||||
/** Retire l'entrée du DOM si la clé Panier n'existe pas et arrête précocement */
|
||||
const clePanier: string = Maybe
|
||||
.fromNullable(entree.getAttribute(ATTRIBUT_CLE_PANIER))
|
||||
.ifNothing(() => {
|
||||
entree.remove();
|
||||
})
|
||||
E.ENTREES_PANIER.ifRight((cartEntries: Array<HTMLElement>) =>
|
||||
arrayForEach(cartEntries, (entry: HTMLElement): void => {
|
||||
// Retire l'entrée du DOM si la clé Panier n'existe pas puis arrête précocement
|
||||
const entryKey: string = Maybe
|
||||
.fromNullable(entry.getAttribute(ATTRIBUT_CLE_PANIER))
|
||||
.ifNothing(() => entry.remove())
|
||||
.orDefault("CLE_PANIER_INEXISTANTE");
|
||||
/** */
|
||||
const E_P: ElementsEntreePanier = recupereElementsEntreePanier(entree);
|
||||
const entryButtons: CartEntryInteractiveElements = getCartEntryInteractiveEles(entry);
|
||||
|
||||
entree.addEventListener("click", (evenement: Event): void => {
|
||||
// Délégation d'Événements
|
||||
match(evenement.target)
|
||||
.with(P.nullish, () => console.error(evenement.target))
|
||||
// Bouton d'addition
|
||||
entry.addEventListener("click", (event: Event): void => {
|
||||
// Discrimine en fonction de l'Élément cliqué (délégation d'Événements)
|
||||
match(event.target)
|
||||
// Cas impossible
|
||||
.with(P.nullish, () => console.error(event.target))
|
||||
// Clic sur le Bouton d'addition
|
||||
.when(
|
||||
(cible: EventTarget) => (cible as HTMLElement).matches(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
(target: EventTarget) => (target as HTMLElement).matches(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
(): void => {
|
||||
Maybe
|
||||
// Nécessaire pour que l'on ait une valeur à incrémenter
|
||||
.fromNullable(E_P.champQuantite.valueAsNumber)
|
||||
.ifJust((valeur: number) => {
|
||||
// Réalise la requête et traite sa réponse
|
||||
void EitherAsync
|
||||
// 1. Valide les Arguments de la Requête
|
||||
.liftEither(
|
||||
eitherValiParse({ key: clePanier, quantity: valeur + 1 }, WCStoreCartUpdateItemArgsSchema),
|
||||
)
|
||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes
|
||||
.ifRight(() => pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(false)))
|
||||
// 3. Exécute la requête via fetch sous forme d'EitherAsync
|
||||
.chain((args: WCStoreCartUpdateItemArgs) =>
|
||||
eitherAsyncFetch(postBackend({
|
||||
corps: JSON.stringify(args),
|
||||
nonce: ETATS_PAGE.nonce,
|
||||
route: ROUTE_API_MAJ_ARTICLE_PANIER,
|
||||
}))
|
||||
)
|
||||
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
|
||||
.chain((reponse: Response) =>
|
||||
EitherAsync<BadRequestError | Error | ServerError, unknown>(async ({ throwE }) =>
|
||||
// Simplifie les données à matcher
|
||||
match(await creeReponseSimplifiee(reponse))
|
||||
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
||||
.with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
|
||||
.with({ status: 200 }, r => r.body)
|
||||
.otherwise(erreur => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`)))
|
||||
)
|
||||
)
|
||||
// 5. Vérifie le Schéma de la Réponse
|
||||
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
|
||||
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||
.ifRight((panier: WCStoreCart): void => {
|
||||
// Émet un Message avec le nouveau nombre de Produits dans le Panier
|
||||
emetMessageMajBoutonPanier({ quantiteProduits: panier.items_count });
|
||||
// Émet un Message avec le nouveau contenu du Panier
|
||||
emetMessageMajContenuPanier({
|
||||
produits: panier.items,
|
||||
sousTotalProduits: diviseParCent(panier.totals.total_items),
|
||||
sousTotalReduction: diviseParCent(panier.totals.total_discount),
|
||||
totalPanier: diviseParCent(panier.totals.total_price),
|
||||
});
|
||||
// Émet un Message pour réinitialiser la validation de la livraison
|
||||
emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
|
||||
})
|
||||
// 7. Traite les Erreurs et affiche un message à l'Utilisateur
|
||||
.ifLeft(
|
||||
(erreur: BadRequestError | FetchErrors | ServerError | ValiError<AnySchema>): void => {
|
||||
match(erreur)
|
||||
.with(P.instanceOf(ValiError), e => {
|
||||
reporteErreur(e);
|
||||
console.error(e.issues);
|
||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||
})
|
||||
.with(P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
|
||||
reporteErreur(e);
|
||||
console.error(e);
|
||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||
})
|
||||
.with(P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
|
||||
reporteErreur(e);
|
||||
console.error(e);
|
||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
|
||||
})
|
||||
.exhaustive();
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
// Réactive les Boutons
|
||||
pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(true));
|
||||
})
|
||||
.run();
|
||||
});
|
||||
void EitherAsync
|
||||
.liftEither(
|
||||
Maybe
|
||||
// Une valeur à incrémenter doit exister
|
||||
.fromNullable(entryButtons.quantityInput.valueAsNumber)
|
||||
.toEither(new Error("Quantité manquante pour cette ligne du Panier !")),
|
||||
)
|
||||
.chain(q =>
|
||||
EitherAsync.liftEither(
|
||||
safeSchemaParse({ key: entryKey, quantity: q + 1 }, WCStoreCartUpdateItemArgsSchema),
|
||||
)
|
||||
)
|
||||
.ifRight(() => pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false)))
|
||||
.chain((args: WCStoreCartUpdateItemArgs) =>
|
||||
safeFetch(postBackend({
|
||||
corps: JSON.stringify(args),
|
||||
nonce: PAGE_STATE.nonce,
|
||||
route: ROUTE_API_MAJ_ARTICLE_PANIER,
|
||||
}))
|
||||
)
|
||||
.chain((r: Response) =>
|
||||
EitherAsync<ServerError, unknown>(async ({ throwE }) =>
|
||||
match(await newPartialResponse(r))
|
||||
.with({ status: 200 }, r => r.body)
|
||||
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs)))
|
||||
)
|
||||
)
|
||||
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCStoreCartSchema)))
|
||||
.ifRight((c: WCStoreCart): void => {
|
||||
// Émet un Message avec le nouveau nombre de Produits dans le Panier
|
||||
emetMessageMajBoutonPanier({ quantiteProduits: c.items_count });
|
||||
// Émet un Message avec le nouveau contenu du Panier
|
||||
emetMessageMajContenuPanier({
|
||||
produits: c.items,
|
||||
sousTotalProduits: diviseParCent(c.totals.total_items),
|
||||
sousTotalReduction: diviseParCent(c.totals.total_discount),
|
||||
totalPanier: diviseParCent(c.totals.total_price),
|
||||
});
|
||||
// Émet un Message pour réinitialiser la validation de la livraison
|
||||
emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
|
||||
})
|
||||
.ifLeft(
|
||||
(err: FetchErrors | HttpCodeErrors | ValiError<AnySchema>): void => {
|
||||
match(err)
|
||||
.with(P.instanceOf(ValiError), e => {
|
||||
reporteErreur(e);
|
||||
console.error(e.issues);
|
||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||
})
|
||||
.with(P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
|
||||
reporteErreur(e);
|
||||
console.error(e);
|
||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||
})
|
||||
.with(P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
|
||||
reporteErreur(e);
|
||||
console.error(e);
|
||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
|
||||
})
|
||||
.exhaustive();
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(true));
|
||||
})
|
||||
.run();
|
||||
},
|
||||
)
|
||||
// Bouton de soustraction
|
||||
|
|
@ -198,22 +178,24 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
(): void => {
|
||||
Maybe
|
||||
// Nécessaire pour que l'on ait une valeur à incrémenter
|
||||
.fromNullable(E_P.champQuantite.valueAsNumber)
|
||||
.fromNullable(entryButtons.quantityInput.valueAsNumber)
|
||||
.filter(valeur => valeur > 1)
|
||||
.ifJust((valeur: number) => {
|
||||
// Réalise la requête et traite sa réponse
|
||||
void EitherAsync
|
||||
// 1. Valide les Arguments de la Requête
|
||||
.liftEither(
|
||||
eitherValiParse({ key: clePanier, quantity: valeur - 1 }, WCStoreCartUpdateItemArgsSchema),
|
||||
safeSchemaParse({ key: entryKey, quantity: valeur - 1 }, WCStoreCartUpdateItemArgsSchema),
|
||||
)
|
||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes
|
||||
.ifRight(() => pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(false)))
|
||||
.ifRight(() =>
|
||||
pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false))
|
||||
)
|
||||
// 3. Exécute la requête via fetch sous forme d'EitherAsync
|
||||
.chain((args: WCStoreCartUpdateItemArgs) =>
|
||||
eitherAsyncFetch(postBackend({
|
||||
safeFetch(postBackend({
|
||||
corps: JSON.stringify(args),
|
||||
nonce: ETATS_PAGE.nonce,
|
||||
nonce: PAGE_STATE.nonce,
|
||||
route: ROUTE_API_MAJ_ARTICLE_PANIER,
|
||||
}))
|
||||
)
|
||||
|
|
@ -221,7 +203,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
.chain((reponse: Response) =>
|
||||
EitherAsync<BadRequestError | Error | ServerError, unknown>(async ({ throwE }) =>
|
||||
// Simplifie les données à matcher
|
||||
match(await creeReponseSimplifiee(reponse))
|
||||
match(await newPartialResponse(reponse))
|
||||
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
||||
.with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
|
||||
.with({ status: 200 }, r => r.body)
|
||||
|
|
@ -229,7 +211,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
)
|
||||
)
|
||||
// 5. Vérifie le Schéma de la Réponse
|
||||
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
|
||||
.chain((corps: unknown) => EitherAsync.liftEither(safeSchemaParse(corps, WCStoreCartSchema)))
|
||||
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||
.ifRight((panier: WCStoreCart): void => {
|
||||
// Émet un Message avec le nouveau nombre de Produits dans le Panier
|
||||
|
|
@ -268,7 +250,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
)
|
||||
.finally(() => {
|
||||
// Réactive les Boutons
|
||||
pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(true));
|
||||
pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(true));
|
||||
})
|
||||
.run();
|
||||
});
|
||||
|
|
@ -280,19 +262,21 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
(): void => {
|
||||
Maybe
|
||||
// TODO: Pourquoi ?
|
||||
.fromNullable(E_P.champQuantite.valueAsNumber)
|
||||
.fromNullable(entryButtons.quantityInput.valueAsNumber)
|
||||
.ifJust(() => {
|
||||
// Réalise la requête et traite sa réponse
|
||||
void EitherAsync
|
||||
// 1. Valide les Arguments de la Requête
|
||||
.liftEither(eitherValiParse({ key: clePanier }, WCStoreCartRemoveItemArgsSchema))
|
||||
.liftEither(safeSchemaParse({ key: entryKey }, WCStoreCartRemoveItemArgsSchema))
|
||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes
|
||||
.ifRight(() => pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(false)))
|
||||
.ifRight(() =>
|
||||
pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false))
|
||||
)
|
||||
// 3. Exécute la requête via fetch sous forme d'EitherAsync
|
||||
.chain((args: WCStoreCartRemoveItemArgs) =>
|
||||
eitherAsyncFetch(postBackend({
|
||||
safeFetch(postBackend({
|
||||
corps: JSON.stringify(args),
|
||||
nonce: ETATS_PAGE.nonce,
|
||||
nonce: PAGE_STATE.nonce,
|
||||
route: ROUTE_API_RETIRE_ARTICLE_PANIER,
|
||||
}))
|
||||
)
|
||||
|
|
@ -300,7 +284,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
.chain((reponse: Response) =>
|
||||
EitherAsync<BadRequestError | Error | ServerError, unknown>(async ({ throwE }) =>
|
||||
// Simplifie les données à matcher
|
||||
match(await creeReponseSimplifiee(reponse))
|
||||
match(await newPartialResponse(reponse))
|
||||
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
||||
.with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
|
||||
.with({ status: 200 }, r => r.body)
|
||||
|
|
@ -308,7 +292,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
)
|
||||
)
|
||||
// 5. Vérifie le Schéma de la Réponse
|
||||
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
|
||||
.chain((corps: unknown) => EitherAsync.liftEither(safeSchemaParse(corps, WCStoreCartSchema)))
|
||||
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||
.ifRight((panier: WCStoreCart): void => {
|
||||
// Émet un Message avec le nouveau nombre de Produits dans le Panier
|
||||
|
|
@ -324,7 +308,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
|
||||
|
||||
// Retire l'entrée du Panier du DOM
|
||||
entree.remove();
|
||||
entry.remove();
|
||||
})
|
||||
// 7. Traite les Erreurs et affiche un message à l'Utilisateur
|
||||
.ifLeft(
|
||||
|
|
@ -350,13 +334,13 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
)
|
||||
.finally(() => {
|
||||
// Réactive les Boutons
|
||||
pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(true));
|
||||
pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(true));
|
||||
})
|
||||
.run();
|
||||
});
|
||||
},
|
||||
)
|
||||
.run();
|
||||
.otherwise(_ => {});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,22 +2,21 @@
|
|||
* Scripts pour la mise à jour trans-fenêtres/trans-onglets du Bouton du Panier.
|
||||
*/
|
||||
|
||||
import { pipe } from "@mobily/ts-belt";
|
||||
|
||||
import type { MessageMajBoutonPanier } from "./lib/types/messages";
|
||||
|
||||
import { ATTRIBUT_CONTIENT_ARTICLES, SELECTEUR_BOUTON_PANIER } from "./constantes/dom.ts";
|
||||
import { NOM_CANAL_BOUTON_PANIER } from "./constantes/messages.ts";
|
||||
import { recupereElementOuLeve } from "./lib/dom.ts";
|
||||
import { mustGetEleInDocument } from "./lib/dom.ts";
|
||||
import { valideMessageMajBoutonPanier } from "./lib/messages.ts";
|
||||
import { recupereElementDocumentEither } from "./lib/utils.ts";
|
||||
|
||||
/**
|
||||
* Initialise les interactions et la mise à jour du bouton « Panier » contenant le nombre d'articles dans le Panier.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const initialiseBoutonPanier = (): void => {
|
||||
/** Le « Bouton » vers le Panier dont le texte est un indicateur du nombre de Produits dedans. */
|
||||
const BOUTON_PANIER: HTMLAnchorElement = pipe(
|
||||
recupereElementDocumentEither<HTMLAnchorElement>(SELECTEUR_BOUTON_PANIER),
|
||||
recupereElementOuLeve,
|
||||
);
|
||||
/** Le « Bouton » vers le Panier avec un indicateur de la quantité de Produits ajoutés. */
|
||||
const BOUTON_PANIER: HTMLAnchorElement = mustGetEleInDocument<HTMLAnchorElement>(SELECTEUR_BOUTON_PANIER);
|
||||
const CANAL_BOUTON_PANIER: BroadcastChannel = new BroadcastChannel(NOM_CANAL_BOUTON_PANIER);
|
||||
|
||||
CANAL_BOUTON_PANIER.onmessage = (evenementMessage: MessageEvent<unknown>): void => {
|
||||
|
|
@ -35,6 +34,6 @@ const initialiseBoutonPanier = (): void => {
|
|||
});
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
initialiseBoutonPanier();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,22 +10,13 @@ import {
|
|||
ATTRIBUT_TABINDEX,
|
||||
SELECTEUR_BOUTON_MENU_MOBILE,
|
||||
} from "./constantes/dom";
|
||||
import { recupereEleOuLeve } from "./lib/utils";
|
||||
|
||||
/**
|
||||
* 1. Récupérer la hauteur de l'écran et la hauteur de la page.
|
||||
* 2. Si la page fait moins de 3 fois la hauteur de l'écran, arrêter.
|
||||
* 3. Recommencer l'opération si la page est redimensionnée (ResizeObserver).
|
||||
* 4. Quand l'Utilisateur dépasse le seuil de 3 fois la hauteur de l'écran, faire apparaître le bouton.
|
||||
* 5. Le faire disparaître quand il retourne en-deça.
|
||||
* 6. Au clic dessus, aller sur l'ancre au sommet de la page et faire disparaître le bouton.
|
||||
*/
|
||||
import { mustGetEleInDocument } from "./lib/dom";
|
||||
|
||||
const E = {
|
||||
BOUTON_MENU_MOBILE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_MENU_MOBILE),
|
||||
BOUTON_RETOUR_SOMMET: recupereEleOuLeve<HTMLButtonElement>("#bouton-retour-haut"),
|
||||
CORPS_HTML: recupereEleOuLeve<HTMLBodyElement>("body"),
|
||||
IMAGE_BOUTON: recupereEleOuLeve<HTMLImageElement>("#bouton-retour-haut img"),
|
||||
BOUTON_MENU_MOBILE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_MENU_MOBILE),
|
||||
BOUTON_RETOUR_SOMMET: mustGetEleInDocument<HTMLButtonElement>("#bouton-retour-haut"),
|
||||
CORPS_HTML: mustGetEleInDocument<HTMLBodyElement>("body"),
|
||||
IMAGE_BOUTON: mustGetEleInDocument<HTMLImageElement>("#bouton-retour-haut img"),
|
||||
};
|
||||
|
||||
/** Le ratio minimum hauteur de page/hauteur de la fenêtre à atteindre pour que le Bouton soit nécessaire. */
|
||||
|
|
@ -47,6 +38,7 @@ const majDefilementY = (): number => window.scrollY;
|
|||
|
||||
/**
|
||||
* @param estVisible
|
||||
* @returns void
|
||||
*/
|
||||
const majVisibiliteBouton = (estVisible: boolean): void => {
|
||||
if (estVisible) {
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import { A } from "@mobily/ts-belt";
|
|||
import { match } from "ts-pattern";
|
||||
|
||||
import { SELECTEUR_ENTREE_MENU_CATEGORIES_PRODUITS, SELECTEUR_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
|
||||
import { recupereEleOuLeve, recupereElesOuLeve } from "./lib/utils.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
const MENU_CATEGORIES_PRODUITS: HTMLElement = recupereEleOuLeve(SELECTEUR_MENU_CATEGORIES_PRODUITS);
|
||||
const ENTREES_MENU_CATEGORIES_PRODUITS: Array<HTMLAnchorElement> = recupereElesOuLeve(
|
||||
const MENU_CATEGORIES_PRODUITS: HTMLElement = mustGetEleInDocument(SELECTEUR_MENU_CATEGORIES_PRODUITS);
|
||||
const ENTREES_MENU_CATEGORIES_PRODUITS: Array<HTMLAnchorElement> = mustGetElesInDocument(
|
||||
SELECTEUR_ENTREE_MENU_CATEGORIES_PRODUITS,
|
||||
);
|
||||
|
||||
A.forEachWithIndex(
|
||||
[ENTREES_MENU_CATEGORIES_PRODUITS.at(0), ENTREES_MENU_CATEGORIES_PRODUITS.at(-1)],
|
||||
(index, entreeMenu) => {
|
||||
(index, entreeMenu): void => {
|
||||
if (!entreeMenu) return;
|
||||
|
||||
new IntersectionObserver(
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import { A, O, pipe } from "@mobily/ts-belt";
|
|||
import A11yDialog from "a11y-dialog";
|
||||
|
||||
import { ATTRIBUT_MENU_MOBILE_ACTIVE, SELECTEUR_BOUTON_MENU_MOBILE, SELECTEUR_MENU_MOBILE } from "./constantes/dom.ts";
|
||||
import { recupereEleOuLeve } from "./lib/utils.ts";
|
||||
import { mustGetEleInDocument } from "./lib/dom.ts";
|
||||
|
||||
// Éléments d'intérêt
|
||||
const E = {
|
||||
BOUTON_MENU_MOBILE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_MENU_MOBILE),
|
||||
CORPS_HTML: recupereEleOuLeve<HTMLBodyElement>("body"),
|
||||
MENU_MOBILE: recupereEleOuLeve<HTMLDivElement>(SELECTEUR_MENU_MOBILE),
|
||||
BOUTON_MENU_MOBILE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_MENU_MOBILE),
|
||||
CORPS_HTML: mustGetEleInDocument<HTMLBodyElement>("body"),
|
||||
MENU_MOBILE: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_MENU_MOBILE),
|
||||
};
|
||||
|
||||
const initialiseBoutonMenuMobile = (): void => {
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ import {
|
|||
SELECTEUR_CONTENEUR_STORYTELLING_A_PROPOS,
|
||||
SELECTEUR_EPINGLE,
|
||||
} from "./constantes/dom.ts";
|
||||
import { recupereEleOuLeve, recupereElesOuLeve } from "./lib/utils.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
|
||||
/** Le Conteneur des images du storytelling. */
|
||||
const CONTENEUR_STORYTELLING = recupereEleOuLeve<HTMLElement>(
|
||||
const CONTENEUR_STORYTELLING = mustGetEleInDocument<HTMLElement>(
|
||||
SELECTEUR_CONTENEUR_STORYTELLING_A_PROPOS,
|
||||
);
|
||||
/** */
|
||||
const EPINGLES = recupereElesOuLeve<HTMLButtonElement>(SELECTEUR_EPINGLE);
|
||||
const EPINGLES = mustGetElesInDocument<HTMLButtonElement>(SELECTEUR_EPINGLE);
|
||||
/** */
|
||||
const BOITES_TEXTE = recupereElesOuLeve<HTMLDivElement>(SELECTEUR_BOITE_TEXTE);
|
||||
const BOITES_TEXTE = mustGetElesInDocument<HTMLDivElement>(SELECTEUR_BOITE_TEXTE);
|
||||
/** */
|
||||
const ENSEMBLES_EPINGLES_BOITES_TEXTE = new Map<string, [HTMLButtonElement, HTMLDivElement]>();
|
||||
A.forEachWithIndex(EPINGLES, (index, epingle) => {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,17 @@ import {
|
|||
SELECTEUR_CONTENEUR_STORYTELLING,
|
||||
SELECTEUR_IMAGES_STORYTELLING,
|
||||
} from "./constantes/dom.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
import { estEntreDeuxNombres } from "./lib/nombres.ts";
|
||||
import { recupereEleOuLeve, recupereElesOuLeve } from "./lib/utils.ts";
|
||||
|
||||
const initialiseScrollStorytelling = (): void => {
|
||||
const E = {
|
||||
/** Le conteneur des images du storytelling. */
|
||||
CONTENEUR_STORYTELLING: recupereEleOuLeve<HTMLElement>(".storytelling__conteneur"),
|
||||
CONTENEUR_STORYTELLING: mustGetEleInDocument<HTMLElement>(".storytelling__conteneur"),
|
||||
/** Les images du storytelling. */
|
||||
IMAGES_STORYTELLING: recupereElesOuLeve<HTMLDivElement>(SELECTEUR_IMAGES_STORYTELLING),
|
||||
IMAGES_STORYTELLING: mustGetElesInDocument<HTMLDivElement>(SELECTEUR_IMAGES_STORYTELLING),
|
||||
/** Le bloc contenant le storytelling. */
|
||||
STORYTELLING: recupereEleOuLeve<HTMLElement>(SELECTEUR_CONTENEUR_STORYTELLING),
|
||||
STORYTELLING: mustGetEleInDocument<HTMLElement>(SELECTEUR_CONTENEUR_STORYTELLING),
|
||||
};
|
||||
|
||||
/** La hauteur d'une image du storytelling. */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { ValiError } from "valibot";
|
|||
|
||||
import type { APIFetchErrors } from "./lib/types/api/erreurs";
|
||||
import type { WCV3Products, WCV3ProductsArgs } from "./lib/types/api/v3/products.ts";
|
||||
import type { EtatsPageGenerique } from "./lib/types/pages";
|
||||
import type { GenericPageState } from "./lib/types/pages";
|
||||
|
||||
import { ROUTE_API_NOUVELLE_PRODUCTS } from "./constantes/api.ts";
|
||||
import {
|
||||
|
|
@ -23,48 +23,53 @@ import {
|
|||
SELECTEUR_GRILLE_PRODUITS,
|
||||
} from "./constantes/dom.ts";
|
||||
import { lanceAnimationCycleLoading } from "./lib/animations.ts";
|
||||
import { html } from "./lib/dom.ts";
|
||||
import { html, mustGetEleInDocument } from "./lib/dom.ts";
|
||||
import { BadRequestError, reporteErreur, ServerError } from "./lib/erreurs.ts";
|
||||
import { creeReponseSimplifiee, getBackendAvecParametresUrl } from "./lib/reseau.ts";
|
||||
import { newPartialResponse, getBackendAvecParametresUrl } from "./lib/reseau.ts";
|
||||
import { WCV3ProductsArgsSchema, WCV3ProductsSchema } from "./lib/schemas/api/v3/products.ts";
|
||||
import { recupereEleOuLeve } from "./lib/utils.ts";
|
||||
import { eitherValiParse } from "./lib/validation.ts";
|
||||
import { safeSchemaParse } from "./lib/validation.ts";
|
||||
|
||||
type APIProductsErrors =
|
||||
| APIFetchErrors
|
||||
| ValiError<typeof WCV3ProductsArgsSchema>
|
||||
| ValiError<typeof WCV3ProductsSchema>;
|
||||
|
||||
// @ts-expect-error -- États injectés par le modèle PHP
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
|
||||
const ETATS_PAGE: EtatsPageGenerique = _etats;
|
||||
const ETATS_PAGE: GenericPageState = _etats;
|
||||
|
||||
// Numéros magiques
|
||||
const PRODUCTS_PER_PAGE = 12;
|
||||
|
||||
// Éléments d'intérêt
|
||||
const E = {
|
||||
BOUTON_PLUS_DE_PRODUITS: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_PLUS_PRODUITS),
|
||||
GRILLE_PRODUITS: recupereEleOuLeve<HTMLDivElement>(SELECTEUR_GRILLE_PRODUITS),
|
||||
BOUTON_PLUS_DE_PRODUITS: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_PLUS_PRODUITS),
|
||||
GRILLE_PRODUITS: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_GRILLE_PRODUITS),
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
const initialisePageBoutique = (): void => {
|
||||
/** ID de la Catégorie de Produits si la Page courante est l'Archive d'une Catégorie. */
|
||||
const idCategorieProduits: null | string = E.GRILLE_PRODUITS.getAttribute(ATTRIBUT_ID_CATEGORIE_PRODUITS);
|
||||
|
||||
E.BOUTON_PLUS_DE_PRODUITS.addEventListener("click", (): void => {
|
||||
/** Le numéro de page demandée par l'Utilisateur. */
|
||||
const nouveauNumeroPage = Number(E.GRILLE_PRODUITS.getAttribute(ATTRIBUT_PAGE)) + 1;
|
||||
/** Les arguments passés à la requête auprès Backend pour la nouvelle page de Produits. */
|
||||
const args: WCV3ProductsArgs = {
|
||||
page: nouveauNumeroPage,
|
||||
per_page: 12,
|
||||
per_page: PRODUCTS_PER_PAGE,
|
||||
// Ajoute conditionnellement la Catégorie de Produits
|
||||
...(idCategorieProduits && { category: idCategorieProduits }),
|
||||
};
|
||||
|
||||
type APIProductsErrors =
|
||||
| APIFetchErrors
|
||||
| ValiError<typeof WCV3ProductsArgsSchema>
|
||||
| ValiError<typeof WCV3ProductsSchema>;
|
||||
|
||||
void EitherAsync
|
||||
// 1. Valide les Arguments de la Requête
|
||||
.liftEither(eitherValiParse(args, WCV3ProductsArgsSchema))
|
||||
.liftEither(safeSchemaParse(args, WCV3ProductsArgsSchema))
|
||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
|
||||
.ifRight(() => {
|
||||
.ifRight((): void => {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
E.BOUTON_PLUS_DE_PRODUITS.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
E.BOUTON_PLUS_DE_PRODUITS.setAttribute(ATTRIBUT_CHARGEMENT, "");
|
||||
|
|
@ -86,7 +91,7 @@ const initialisePageBoutique = (): void => {
|
|||
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
|
||||
.chain((reponse: Response) =>
|
||||
EitherAsync<APIFetchErrors, unknown>(async ({ throwE }) => {
|
||||
return match(await creeReponseSimplifiee(reponse))
|
||||
return match(await newPartialResponse(reponse))
|
||||
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
||||
.with({ status: 400 }, () => throwE(new BadRequestError("400 Server Error")))
|
||||
.with({ status: 200 }, r => r.body)
|
||||
|
|
@ -94,11 +99,11 @@ const initialisePageBoutique = (): void => {
|
|||
})
|
||||
)
|
||||
// 5. Vérifie le Schéma de la Réponse
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCV3ProductsSchema)))
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCV3ProductsSchema)))
|
||||
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||
.ifRight((donnees: WCV3Products) => {
|
||||
// Cache le bouton s'il y a moins de 12 Produits disponibles (que l'on est à la dernière page)
|
||||
if (donnees.length < 12) {
|
||||
// Cache le bouton s'il y a moins de PRODUCTS_PER_PAGE Produits disponibles (que l'on est à la dernière page)
|
||||
if (donnees.length < PRODUCTS_PER_PAGE) {
|
||||
E.BOUTON_PLUS_DE_PRODUITS.toggleAttribute(ATTRIBUT_HIDDEN);
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +111,7 @@ const initialisePageBoutique = (): void => {
|
|||
const fragment: DocumentFragment = document.createDocumentFragment();
|
||||
|
||||
// Créé les Éléments <article> à insérer
|
||||
for (const produit of donnees.slice(0, 12)) {
|
||||
for (const produit of donnees.slice(0, PRODUCTS_PER_PAGE)) {
|
||||
pipe(
|
||||
html`
|
||||
<article class="produit">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { ValiError } from "valibot";
|
|||
|
||||
import { D, pipe } from "@mobily/ts-belt";
|
||||
import { forEach as arrayForEach } from "@mobily/ts-belt/Array";
|
||||
import { type Either, Maybe } from "purify-ts";
|
||||
import { Maybe } from "purify-ts";
|
||||
|
||||
import type { MessageMajContenuPanierSchema } from "./lib/schemas/messages.ts";
|
||||
import type { WCStoreCartItem } from "./lib/types/api/cart";
|
||||
|
|
@ -14,32 +14,26 @@ import {
|
|||
ATTRIBUT_DESACTIVE,
|
||||
ATTRIBUT_HIDDEN,
|
||||
SELECTEUR_BOUTON_ADDITION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SEPARATION_ADRESSES,
|
||||
SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SUPPRESSION_PANIER,
|
||||
SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
SELECTEUR_CONTENEUR_PANIER,
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
SELECTEUR_FORMULAIRE_FACTURATION,
|
||||
SELECTEUR_PRIX_LIGNE_PANIER,
|
||||
SELECTEUR_SOUS_TOTAL_PRODUITS,
|
||||
SELECTEUR_TOTAL_PANIER,
|
||||
SELECTEUR_TOTAL_REDUCTION_VALEUR,
|
||||
} from "./constantes/dom.ts";
|
||||
import { NOM_CANAL_BOUTON_PANIER, NOM_CANAL_CONTENU_PANIER } from "./constantes/messages.ts";
|
||||
import { recupereElementAvecSelecteur, recupereElementOuLeve, recupereElementsAvecSelecteur } from "./lib/dom.ts";
|
||||
import { getDOMElementsWithSelector, recupereElementAvecSelecteur, recupereElementOuLeve } from "./lib/dom.ts";
|
||||
import { type CleNonTrouveError, reporteErreur } from "./lib/erreurs.ts";
|
||||
import { valideMessageMajBoutonPanier, valideMessageMajContenuPanier } from "./lib/messages.ts";
|
||||
import { arrondisADeuxDecimales, diviseParCent, formateEnEuros, inverseNombre } from "./lib/nombres.ts";
|
||||
import { propEither, recupereElementsDocumentEither, recupereEleOuLeve } from "./lib/utils.ts";
|
||||
import { propEither } from "./lib/utils.ts";
|
||||
import {
|
||||
initialiseBoutonCalculLivraison,
|
||||
initialiseBoutonCreationCommande,
|
||||
initialiseEmetteursEvenementsFormulairePanier,
|
||||
initCartFormEventEmiters,
|
||||
initOrderCreationButton,
|
||||
initShippingCalculationButton,
|
||||
} from "./page-panier/scripts-page-panier-adresses.ts";
|
||||
import { initialiseElementsCodePromo } from "./page-panier/scripts-page-panier-code-promo.ts";
|
||||
import { E } from "./page-panier/scripts-page-panier-elements.ts";
|
||||
import { souscrisEvenementsPanier } from "./page-panier/scripts-page-panier-evenement.ts";
|
||||
import { initialiseBoutonsChoixMethodesLivraison } from "./page-panier/scripts-page-panier-methodes-livraison.ts";
|
||||
import { initShippingRatesChoicesActions } from "./page-panier/scripts-page-panier-methodes-livraison.ts";
|
||||
import { initialiseActionsEntreesPanier } from "./page-panier/scripts-page-panier-panneau-produits.ts";
|
||||
|
||||
type ElementsEntreePanier = {
|
||||
|
|
@ -60,19 +54,6 @@ type EtatsPage = {
|
|||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
|
||||
const ETATS_PAGE: EtatsPage = _etats;
|
||||
|
||||
// Éléments d'intérêt
|
||||
const ENTREES_PANIER_EITHER: Either<SyntaxError, Array<HTMLElement>> = recupereElementsDocumentEither<HTMLElement>(
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
);
|
||||
const CONTENEUR_PANIER: HTMLElement = recupereEleOuLeve(SELECTEUR_CONTENEUR_PANIER);
|
||||
const SOUS_TOTAL_PRODUITS: HTMLElement = recupereEleOuLeve(SELECTEUR_SOUS_TOTAL_PRODUITS);
|
||||
const SOUS_TOTAL_REDUCTION: HTMLSpanElement = recupereEleOuLeve(SELECTEUR_TOTAL_REDUCTION_VALEUR);
|
||||
const TOTAL_PANIER: HTMLParagraphElement = recupereEleOuLeve(SELECTEUR_TOTAL_PANIER);
|
||||
const BOUTON_SEPARATION_ADRESSES: HTMLInputElement = recupereEleOuLeve(
|
||||
SELECTEUR_BOUTON_SEPARATION_ADRESSES,
|
||||
);
|
||||
const FORMULAIRE_FACTURATION: HTMLDivElement = recupereEleOuLeve(SELECTEUR_FORMULAIRE_FACTURATION);
|
||||
|
||||
/**
|
||||
* Fonction utilitaire pour récupérer un Élément dans une ligne (entrée) du Panier, en levant une
|
||||
* Erreur s'il n'existe pas.
|
||||
|
|
@ -112,7 +93,7 @@ const initialiseMajConteneurPanier = (): void => {
|
|||
.map(D.getUnsafe("donnees"))
|
||||
// Met à jour l'Attribut de présence de Produits dans le Panier
|
||||
.ifRight((donnees: MessageMajBoutonPanierDonnees) => {
|
||||
CONTENEUR_PANIER.setAttribute(ATTRIBUT_CONTIENT_ARTICLES, String(donnees.quantiteProduits !== 0));
|
||||
E.CONTENEUR_PANIER.setAttribute(ATTRIBUT_CONTIENT_ARTICLES, String(donnees.quantiteProduits !== 0));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
@ -126,7 +107,7 @@ const initialiseMajContenuPanier = (): void => {
|
|||
.ifRight((donnees: MessageMajContenuPanierDonnees) => {
|
||||
donnees.produits.forEach((ligne: WCStoreCartItem) => {
|
||||
// Met à jour les entrées du Panier
|
||||
ENTREES_PANIER_EITHER.ifRight((entrees: Array<HTMLElement>) => {
|
||||
E.ENTREES_PANIER.ifRight((entrees: Array<HTMLElement>) => {
|
||||
Maybe
|
||||
.fromNullable(entrees.find(entree => entree.getAttribute(ATTRIBUT_CLE_PANIER) === ligne.key))
|
||||
.ifJust((entree: HTMLElement) => {
|
||||
|
|
@ -149,41 +130,41 @@ const initialiseMajContenuPanier = (): void => {
|
|||
});
|
||||
|
||||
// Met à jour les totaux du Panier
|
||||
SOUS_TOTAL_PRODUITS.textContent = formateEnEuros(donnees.sousTotalProduits);
|
||||
SOUS_TOTAL_REDUCTION.textContent = pipe(
|
||||
E.SOUS_TOTAL_PRODUITS.textContent = formateEnEuros(donnees.sousTotalProduits);
|
||||
E.SOUS_TOTAL_REDUCTION.textContent = pipe(
|
||||
inverseNombre(donnees.sousTotalReduction),
|
||||
arrondisADeuxDecimales,
|
||||
formateEnEuros,
|
||||
);
|
||||
TOTAL_PANIER.textContent = pipe(arrondisADeuxDecimales(donnees.totalPanier), formateEnEuros);
|
||||
E.TOTAL_PANIER.textContent = pipe(arrondisADeuxDecimales(donnees.totalPanier), formateEnEuros);
|
||||
});
|
||||
});
|
||||
})
|
||||
// Reporte tout Erreur et réactive les Boutons
|
||||
.ifLeft((erreur: CleNonTrouveError | ValiError<typeof MessageMajContenuPanierSchema>) => {
|
||||
reporteErreur(erreur);
|
||||
ENTREES_PANIER_EITHER.ifRight(entrees => majEtatsActivationBoutons(entrees));
|
||||
E.ENTREES_PANIER.ifRight(entrees => majEtatsActivationBoutons(entrees));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const initialiseMajFormulairesPanier = (): void => {
|
||||
BOUTON_SEPARATION_ADRESSES.addEventListener("click", (): void => {
|
||||
E.BOUTON_SEPARATION_ADRESSES.addEventListener("click", (): void => {
|
||||
Maybe
|
||||
.fromFalsy(BOUTON_SEPARATION_ADRESSES.checked)
|
||||
.fromFalsy(E.BOUTON_SEPARATION_ADRESSES.checked)
|
||||
// Les Adresses sont séparées
|
||||
.ifJust((): void => {
|
||||
// Rend visible le formulaire de facturation
|
||||
FORMULAIRE_FACTURATION.removeAttribute(ATTRIBUT_HIDDEN);
|
||||
recupereElementsAvecSelecteur(FORMULAIRE_FACTURATION)("input, select").ifRight(
|
||||
E.FORMULAIRE_FACTURATION.removeAttribute(ATTRIBUT_HIDDEN);
|
||||
getDOMElementsWithSelector(E.FORMULAIRE_FACTURATION)("input, select").ifRight(
|
||||
arrayForEach(champ => champ.removeAttribute(ATTRIBUT_DESACTIVE)),
|
||||
);
|
||||
})
|
||||
// Les Adresses sont combinées
|
||||
.ifNothing((): void => {
|
||||
// Cache le formulaire de facturation
|
||||
FORMULAIRE_FACTURATION.setAttribute(ATTRIBUT_HIDDEN, "");
|
||||
recupereElementsAvecSelecteur(FORMULAIRE_FACTURATION)<HTMLInputElement | HTMLSelectElement>(
|
||||
E.FORMULAIRE_FACTURATION.setAttribute(ATTRIBUT_HIDDEN, "");
|
||||
getDOMElementsWithSelector(E.FORMULAIRE_FACTURATION)<HTMLInputElement | HTMLSelectElement>(
|
||||
"input, select",
|
||||
).ifRight(arrayForEach(champ => {
|
||||
champ.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||
|
|
@ -194,14 +175,14 @@ const initialiseMajFormulairesPanier = (): void => {
|
|||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
initialiseEmetteursEvenementsFormulairePanier();
|
||||
initCartFormEventEmiters();
|
||||
souscrisEvenementsPanier();
|
||||
initialiseActionsEntreesPanier();
|
||||
initialiseBoutonsChoixMethodesLivraison();
|
||||
initShippingRatesChoicesActions();
|
||||
initialiseMajConteneurPanier();
|
||||
initialiseMajContenuPanier();
|
||||
initialiseMajFormulairesPanier();
|
||||
initialiseBoutonCalculLivraison();
|
||||
initialiseBoutonCreationCommande();
|
||||
initShippingCalculationButton();
|
||||
initOrderCreationButton();
|
||||
initialiseElementsCodePromo();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ import {
|
|||
SELECTEUR_SELECTEUR_QUANTITE,
|
||||
} from "./constantes/dom";
|
||||
import { lanceAnimationCycleLoading } from "./lib/animations.ts";
|
||||
import { recupereElementDocumentEither, mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
import { BadRequestError, reporteErreur, ServerError } from "./lib/erreurs.ts";
|
||||
import { estHTMLSelectElement } from "./lib/gardes.ts";
|
||||
import { emetMessageMajBoutonPanier } from "./lib/messages.ts";
|
||||
import { creeReponseSimplifiee, eitherAsyncFetch, postBackend } from "./lib/reseau.ts";
|
||||
import { newPartialResponse, safeFetch, postBackend } from "./lib/reseau.ts";
|
||||
import { WCStoreCartAddItemArgsSchema } from "./lib/schemas/api/cart-add-item.ts";
|
||||
import { WCStoreCartSchema } from "./lib/schemas/api/cart.ts";
|
||||
import { recupereElementDocumentEither, recupereEleOuLeve, recupereElesOuLeve } from "./lib/utils.ts";
|
||||
import { eitherValiParse } from "./lib/validation.ts";
|
||||
import { safeSchemaParse } from "./lib/validation.ts";
|
||||
|
||||
type EnsembleLienContenu = [HTMLAnchorElement, HTMLElement];
|
||||
/** États utiles pour les scripts de la page. */
|
||||
|
|
@ -67,18 +67,18 @@ const deplieToutesSections = (ensembleLiensContenus: Array<EnsembleLienContenu>)
|
|||
|
||||
// Éléments d'intérêt
|
||||
const E = {
|
||||
BOUTON_AJOUT_PANIER: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_AJOUT_PANIER),
|
||||
CONTENEUR_FLECHES_DEFILEMENT: recupereEleOuLeve<HTMLDivElement>(SELECTEUR_CONTENEUR_FLECHES_DEFILEMENT),
|
||||
FLECHE_DEFILEMENT_DROITE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_FLECHE_DEFILEMENT_DROITE),
|
||||
FLECHE_DEFILEMENT_GAUCHE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_FLECHE_DEFILEMENT_GAUCHE),
|
||||
BOUTON_AJOUT_PANIER: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_AJOUT_PANIER),
|
||||
CONTENEUR_FLECHES_DEFILEMENT: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_CONTENEUR_FLECHES_DEFILEMENT),
|
||||
FLECHE_DEFILEMENT_DROITE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_FLECHE_DEFILEMENT_DROITE),
|
||||
FLECHE_DEFILEMENT_GAUCHE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_FLECHE_DEFILEMENT_GAUCHE),
|
||||
IMAGES: A.flat([
|
||||
recupereEleOuLeve<HTMLImageElement>(SELECTEUR_IMAGE_COLONNE_GAUCHE),
|
||||
recupereElesOuLeve<HTMLImageElement>(SELECTEUR_IMAGES_COLONNE_DROITE),
|
||||
mustGetEleInDocument<HTMLImageElement>(SELECTEUR_IMAGE_COLONNE_GAUCHE),
|
||||
mustGetElesInDocument<HTMLImageElement>(SELECTEUR_IMAGES_COLONNE_DROITE),
|
||||
]),
|
||||
LIENS_ONGLETS: recupereElesOuLeve<HTMLAnchorElement>(SELECTEUR_LIENS_ONGLETS),
|
||||
PHOTOS_PRODUIT: recupereEleOuLeve<HTMLElement>(SELECTEUR_PHOTOS_PRODUIT),
|
||||
PRIX_PRODUIT: recupereEleOuLeve<HTMLParagraphElement>(SELECTEUR_PRIX_PRODUIT),
|
||||
SECTIONS_CONTENUS: recupereElesOuLeve<HTMLElement>(SELECTEUR_SECTIONS_CONTENUS),
|
||||
LIENS_ONGLETS: mustGetElesInDocument<HTMLAnchorElement>(SELECTEUR_LIENS_ONGLETS),
|
||||
PHOTOS_PRODUIT: mustGetEleInDocument<HTMLElement>(SELECTEUR_PHOTOS_PRODUIT),
|
||||
PRIX_PRODUIT: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_PRIX_PRODUIT),
|
||||
SECTIONS_CONTENUS: mustGetElesInDocument<HTMLElement>(SELECTEUR_SECTIONS_CONTENUS),
|
||||
SELECTEUR_VARIATION: recupereElementDocumentEither<HTMLSelectElement>(SELECTEUR_SELECTEUR_QUANTITE),
|
||||
};
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ const ajouteProduitAuPanier = (): void => {
|
|||
// Réalise la Requête et traite sa Réponse
|
||||
void EitherAsync
|
||||
// 1. Valide les arguments de la Requête
|
||||
.liftEither(eitherValiParse(argsRequete, WCStoreCartAddItemArgsSchema))
|
||||
.liftEither(safeSchemaParse(argsRequete, WCStoreCartAddItemArgsSchema))
|
||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
|
||||
.ifRight(() => {
|
||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||
|
|
@ -213,7 +213,7 @@ const ajouteProduitAuPanier = (): void => {
|
|||
})
|
||||
// 3. Exécute la requête via fetch sous forme d'EitherAsync
|
||||
.chain((args: WCStoreCartAddItemArgs) =>
|
||||
eitherAsyncFetch(
|
||||
safeFetch(
|
||||
postBackend({
|
||||
corps: JSON.stringify(args),
|
||||
nonce: ETATS_PAGE.nonce,
|
||||
|
|
@ -225,7 +225,7 @@ const ajouteProduitAuPanier = (): void => {
|
|||
.chain((reponse: Response) =>
|
||||
EitherAsync<BadRequestError | ServerError, unknown>(async ({ throwE }) =>
|
||||
// Simplifie les données à matcher
|
||||
match(await creeReponseSimplifiee(reponse))
|
||||
match(await newPartialResponse(reponse))
|
||||
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
||||
.with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
|
||||
.with({ status: 201 }, r => r.body)
|
||||
|
|
@ -233,7 +233,7 @@ const ajouteProduitAuPanier = (): void => {
|
|||
)
|
||||
)
|
||||
// 5. Vérifie le Schéma de la Réponse
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCStoreCartSchema)))
|
||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCStoreCartSchema)))
|
||||
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||
.ifRight((panier: WCStoreCart) =>
|
||||
pipe(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue