2026-04-13
This commit is contained in:
parent
d50de6d534
commit
08ad871e0c
9 changed files with 320 additions and 293 deletions
|
|
@ -1,12 +1,14 @@
|
||||||
# Tout ce qui est traité par dprint
|
# Tout ce qui est traité par treefmt
|
||||||
*.css
|
*.css
|
||||||
*.html
|
*.html
|
||||||
*.js
|
*.js
|
||||||
|
*.json
|
||||||
*.md
|
*.md
|
||||||
*.mjs
|
*.mjs
|
||||||
*.mts
|
*.mts
|
||||||
*.php
|
*.php
|
||||||
*.scss
|
*.scss
|
||||||
|
*.sh
|
||||||
*.ts
|
*.ts
|
||||||
*.xml
|
*.xml
|
||||||
*.yaml
|
*.yaml
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,12 @@
|
||||||
"composer/installers": "^2.3",
|
"composer/installers": "^2.3",
|
||||||
"crell/fp": "^1.0",
|
"crell/fp": "^1.0",
|
||||||
"htmlburger/carbon-fields": "^3.6.9",
|
"htmlburger/carbon-fields": "^3.6.9",
|
||||||
"illuminate/support": "^13.3",
|
"illuminate/support": "^13.4",
|
||||||
"laravel/helpers": "^1.8.3",
|
"laravel/helpers": "^1.8.3",
|
||||||
"log1x/wp-smtp": "^1.0.2",
|
"log1x/wp-smtp": "^1.0.2",
|
||||||
"lstrojny/functional-php": "^1.18",
|
"lstrojny/functional-php": "^1.18",
|
||||||
"mnsami/composer-custom-directory-installer": "^2.0",
|
"mnsami/composer-custom-directory-installer": "^2.0",
|
||||||
"nesbot/carbon": "^3.11.3",
|
"nesbot/carbon": "^3.11.4",
|
||||||
"oscarotero/env": "^2.1.1",
|
"oscarotero/env": "^2.1.1",
|
||||||
"php": ">=8.5",
|
"php": ">=8.5",
|
||||||
"php-standard-library/php-standard-library": "^6.1.1",
|
"php-standard-library/php-standard-library": "^6.1.1",
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
"vlucas/phpdotenv": "^5.6.3",
|
"vlucas/phpdotenv": "^5.6.3",
|
||||||
"wpackagist-plugin/falcon": "^2.9.3",
|
"wpackagist-plugin/falcon": "^2.9.3",
|
||||||
"wpackagist-plugin/force-regenerate-thumbnails": "^2.3.0",
|
"wpackagist-plugin/force-regenerate-thumbnails": "^2.3.0",
|
||||||
"wpackagist-plugin/query-monitor": "^3.20.4",
|
"wpackagist-plugin/query-monitor": "^4.0.5",
|
||||||
"wpackagist-plugin/redis-cache": "^2.7.0",
|
"wpackagist-plugin/redis-cache": "^2.7.0",
|
||||||
"wpackagist-plugin/wc-multishipping": "^3.0.2",
|
"wpackagist-plugin/wc-multishipping": "^3.0.2",
|
||||||
"wpackagist-plugin/woo-preview-emails": "^2.2.14",
|
"wpackagist-plugin/woo-preview-emails": "^2.2.14",
|
||||||
|
|
|
||||||
100
composer.lock
generated
100
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "bf6e098198b957782555eeb97479b37e",
|
"content-hash": "3144138aa029c01a516e9b6ce664271b",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "carbonphp/carbon-doctrine-types",
|
"name": "carbonphp/carbon-doctrine-types",
|
||||||
|
|
@ -2506,16 +2506,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
"reference": "141046a8f9477948ff284fa65be2095baafb94f2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
|
||||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
"reference": "141046a8f9477948ff284fa65be2095baafb94f2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2565,7 +2565,7 @@
|
||||||
"portable"
|
"portable"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2585,20 +2585,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
"time": "2026-04-10T16:19:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
"reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315",
|
||||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
"reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2650,7 +2650,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2670,20 +2670,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-23T08:48:59+00:00"
|
"time": "2026-04-10T17:25:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php80",
|
"name": "symfony/polyfill-php80",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
|
"reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
|
||||||
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
"reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2734,7 +2734,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2754,20 +2754,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-01-02T08:10:11+00:00"
|
"time": "2026-04-10T16:19:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php84",
|
"name": "symfony/polyfill-php84",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php84.git",
|
"url": "https://github.com/symfony/polyfill-php84.git",
|
||||||
"reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
|
"reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
|
"url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06",
|
||||||
"reference": "d8ced4d875142b6a7426000426b8abc631d6b191",
|
"reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2814,7 +2814,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-php84/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2834,20 +2834,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-24T13:30:11+00:00"
|
"time": "2026-04-10T18:47:49+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php85",
|
"name": "symfony/polyfill-php85",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php85.git",
|
"url": "https://github.com/symfony/polyfill-php85.git",
|
||||||
"reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91"
|
"reference": "2c408a6bb0313e6001a83628dc5506100474254e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
|
"url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/2c408a6bb0313e6001a83628dc5506100474254e",
|
||||||
"reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
|
"reference": "2c408a6bb0313e6001a83628dc5506100474254e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2894,7 +2894,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-php85/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2914,20 +2914,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-23T16:12:55+00:00"
|
"time": "2026-04-10T16:50:15+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-uuid",
|
"name": "symfony/polyfill-uuid",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-uuid.git",
|
"url": "https://github.com/symfony/polyfill-uuid.git",
|
||||||
"reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2"
|
"reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2",
|
"url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94",
|
||||||
"reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2",
|
"reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2977,7 +2977,7 @@
|
||||||
"uuid"
|
"uuid"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-uuid/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2997,7 +2997,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
"time": "2026-04-10T16:19:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation",
|
"name": "symfony/translation",
|
||||||
|
|
@ -3629,15 +3629,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "wpackagist-plugin/query-monitor",
|
"name": "wpackagist-plugin/query-monitor",
|
||||||
"version": "3.20.4",
|
"version": "4.0.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "svn",
|
"type": "svn",
|
||||||
"url": "https://plugins.svn.wordpress.org/query-monitor/",
|
"url": "https://plugins.svn.wordpress.org/query-monitor/",
|
||||||
"reference": "tags/3.20.4"
|
"reference": "tags/4.0.5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://downloads.wordpress.org/plugin/query-monitor.3.20.4.zip"
|
"url": "https://downloads.wordpress.org/plugin/query-monitor.4.0.5.zip"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"composer/installers": "^1.0 || ^2.0"
|
"composer/installers": "^1.0 || ^2.0"
|
||||||
|
|
@ -6715,16 +6715,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-grapheme",
|
"name": "symfony/polyfill-intl-grapheme",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||||
"reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
|
"reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df",
|
||||||
"reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
|
"reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -6773,7 +6773,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -6793,11 +6793,11 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-27T09:58:17+00:00"
|
"time": "2026-04-10T16:19:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-normalizer",
|
"name": "symfony/polyfill-intl-normalizer",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||||
|
|
@ -6858,7 +6858,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -6882,7 +6882,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php81",
|
"name": "symfony/polyfill-php81",
|
||||||
"version": "v1.33.0",
|
"version": "v1.34.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||||
|
|
@ -6938,7 +6938,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0"
|
"source": "https://github.com/symfony/polyfill-php81/tree/v1.34.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
6
justfile
6
justfile
|
|
@ -28,8 +28,10 @@ format:
|
||||||
-vendor/bin/twig-cs-fixer fix web/app/themes/haiku-atelier-2024/
|
-vendor/bin/twig-cs-fixer fix web/app/themes/haiku-atelier-2024/
|
||||||
# PhpCsFixer
|
# PhpCsFixer
|
||||||
# -vendor/bin/php-cs-fixer fix --allow-risky yes
|
# -vendor/bin/php-cs-fixer fix --allow-risky yes
|
||||||
dprint --config "~/.config/dprint/dprint.jsonc" fmt
|
treefmt \
|
||||||
fish scripts/format-sort-files.fish
|
--config-file ~/.config/treefmt/treefmt.toml \
|
||||||
|
--tree-root . \
|
||||||
|
.
|
||||||
|
|
||||||
# Compile, minifie et optimise Sass vers CSS.
|
# Compile, minifie et optimise Sass vers CSS.
|
||||||
[group('css')]
|
[group('css')]
|
||||||
|
|
|
||||||
15
package.json
15
package.json
|
|
@ -73,8 +73,17 @@
|
||||||
"ios >0 and last 3 years"
|
"ios >0 and last 3 years"
|
||||||
],
|
],
|
||||||
"knip": {
|
"knip": {
|
||||||
"entry": ["web/app/themes/haiku-atelier-2024/src/scripts/*.ts"],
|
"entry": [
|
||||||
"project": ["web/app/themes/haiku-atelier-2024/src/scripts/**/*.{js,ts,d.ts}"]
|
"web/app/themes/haiku-atelier-2024/src/scripts/*.ts"
|
||||||
|
],
|
||||||
|
"project": [
|
||||||
|
"web/app/themes/haiku-atelier-2024/src/scripts/**/*.{js,ts,d.ts}"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"trustedDependencies": ["@parcel/watcher", "core-js", "lightningcss-cli", "msgpackr-extract"]
|
"trustedDependencies": [
|
||||||
|
"@parcel/watcher",
|
||||||
|
"core-js",
|
||||||
|
"lightningcss-cli",
|
||||||
|
"msgpackr-extract"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,13 +37,15 @@ Array.from<TestPage>([
|
||||||
},
|
},
|
||||||
]).forEach(({ pageName, url }) => {
|
]).forEach(({ pageName, url }) => {
|
||||||
test.skip(pageName, async ({ page }, testInfo) => {
|
test.skip(pageName, async ({ page }, testInfo) => {
|
||||||
await page["goto"](url);
|
await page.goto(url);
|
||||||
|
|
||||||
const projectName = testInfo.project.name;
|
const projectName = testInfo.project.name;
|
||||||
const timestamp: string = genTimestamp();
|
const timestamp: string = genTimestamp();
|
||||||
const viewport = page.viewportSize();
|
const viewportSize = page.viewportSize() ?? { height: 0, width: 0 };
|
||||||
|
|
||||||
const captureName = `${pageName}/${projectName}-${viewport?.width}-${viewport?.height} ${timestamp}.png`;
|
const captureName = `${pageName}/${projectName}-${String(viewportSize.width)}-${
|
||||||
|
String(viewportSize.height)
|
||||||
|
} ${timestamp}.png`;
|
||||||
|
|
||||||
await takeFullPageScreenshot(page, captureName);
|
await takeFullPageScreenshot(page, captureName);
|
||||||
await expect(page).toHaveURL(url);
|
await expect(page).toHaveURL(url);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import type { MessageMajContenuPanierSchema } from "./lib/schemas/messages.ts";
|
||||||
import type { WCStoreCartItem } from "./lib/types/api/cart";
|
import type { WCStoreCartItem } from "./lib/types/api/cart";
|
||||||
import type { MessageMajBoutonPanierDonnees, MessageMajContenuPanierDonnees } from "./lib/types/messages";
|
import type { MessageMajBoutonPanierDonnees, MessageMajContenuPanierDonnees } from "./lib/types/messages";
|
||||||
|
|
||||||
import { Effect } from "effect";
|
|
||||||
import { Effect } from "effect";
|
import { Effect } from "effect";
|
||||||
import {
|
import {
|
||||||
ATTRIBUT_CLE_PANIER,
|
ATTRIBUT_CLE_PANIER,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,23 @@
|
||||||
import { Array as FxArray, Console, Context, Effect, HashMap, Layer, ManagedRuntime, Option, pipe } from "effect";
|
// oxlint-disable typescript/dot-notation
|
||||||
|
import {
|
||||||
|
Array as FxArray,
|
||||||
|
Console,
|
||||||
|
Context,
|
||||||
|
Effect,
|
||||||
|
HashMap,
|
||||||
|
Layer,
|
||||||
|
ManagedRuntime,
|
||||||
|
Option,
|
||||||
|
pipe,
|
||||||
|
Stream,
|
||||||
|
} from "effect";
|
||||||
import type { NonEmptyReadonlyArray } from "effect/Array";
|
import type { NonEmptyReadonlyArray } from "effect/Array";
|
||||||
import type { NoSuchElementError } from "effect/Cause";
|
import type { NoSuchElementError } from "effect/Cause";
|
||||||
import { getAllSelectorFromDocument, getFirstSelectorFromDocument } from "../scripts-effect/lib/dom.ts";
|
import { getAllSelectorFromDocument, getFirstSelectorFromDocument } from "../scripts-effect/lib/dom.ts";
|
||||||
import {
|
import {
|
||||||
ATTRIBUT_ARIA_CONTROLS,
|
ATTRIBUT_ARIA_CONTROLS,
|
||||||
ATTRIBUT_ARIA_EXPANDED,
|
ATTRIBUT_ARIA_EXPANDED,
|
||||||
|
ATTRIBUT_DESACTIVE,
|
||||||
ATTRIBUT_HIDDEN,
|
ATTRIBUT_HIDDEN,
|
||||||
DOM_BOUTON_AJOUT_PANIER,
|
DOM_BOUTON_AJOUT_PANIER,
|
||||||
DOM_BOUTONS_ACCORDEON,
|
DOM_BOUTONS_ACCORDEON,
|
||||||
|
|
@ -22,7 +35,7 @@ type DetailEnsemble = {
|
||||||
class ProductPageElements extends Context.Service<
|
class ProductPageElements extends Context.Service<
|
||||||
ProductPageElements,
|
ProductPageElements,
|
||||||
{
|
{
|
||||||
AddProductButton: HTMLButtonElement;
|
AddToCartButton: HTMLButtonElement;
|
||||||
Details: HashMap.HashMap<string, DetailEnsemble>;
|
Details: HashMap.HashMap<string, DetailEnsemble>;
|
||||||
DetailsButtons: NonEmptyReadonlyArray<HTMLButtonElement>;
|
DetailsButtons: NonEmptyReadonlyArray<HTMLButtonElement>;
|
||||||
DetailsContents: NonEmptyReadonlyArray<HTMLDivElement>;
|
DetailsContents: NonEmptyReadonlyArray<HTMLDivElement>;
|
||||||
|
|
@ -35,7 +48,7 @@ class ProductPageElements extends Context.Service<
|
||||||
static readonly layer = Layer.effect(
|
static readonly layer = Layer.effect(
|
||||||
ProductPageElements,
|
ProductPageElements,
|
||||||
Effect.gen(function*() {
|
Effect.gen(function*() {
|
||||||
const AddProductButton = yield* getFirstSelectorFromDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER);
|
const AddToCartButton = yield* getFirstSelectorFromDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER);
|
||||||
const DetailsButtons = yield* getAllSelectorFromDocument<HTMLButtonElement>(DOM_BOUTONS_ACCORDEON);
|
const DetailsButtons = yield* getAllSelectorFromDocument<HTMLButtonElement>(DOM_BOUTONS_ACCORDEON);
|
||||||
const DetailsContents = yield* getAllSelectorFromDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON);
|
const DetailsContents = yield* getAllSelectorFromDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON);
|
||||||
const ProductPrice = yield* getFirstSelectorFromDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT);
|
const ProductPrice = yield* getFirstSelectorFromDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT);
|
||||||
|
|
@ -62,7 +75,7 @@ class ProductPageElements extends Context.Service<
|
||||||
);
|
);
|
||||||
|
|
||||||
return ProductPageElements.of({
|
return ProductPageElements.of({
|
||||||
AddProductButton,
|
AddToCartButton,
|
||||||
Details,
|
Details,
|
||||||
DetailsButtons,
|
DetailsButtons,
|
||||||
DetailsContents,
|
DetailsContents,
|
||||||
|
|
@ -78,10 +91,32 @@ class ProductPageElements extends Context.Service<
|
||||||
class ProductPageDOM extends Context.Service<
|
class ProductPageDOM extends Context.Service<
|
||||||
ProductPageDOM,
|
ProductPageDOM,
|
||||||
{
|
{
|
||||||
|
initPriceUpdatesOnVariationChange: () => Effect.Effect<void>;
|
||||||
|
onVariationChangeHandler: () => Effect.Effect<void>;
|
||||||
/**
|
/**
|
||||||
* Récupère les Attributs du Produit depuis les Elements au sein du DOM.
|
* Récupère les Attributs du Produit depuis les Elements au sein du DOM.
|
||||||
*/
|
*/
|
||||||
getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>>;
|
getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>>;
|
||||||
|
/**
|
||||||
|
* Initialise l'état initial du Bouton d'ajout au Panier.
|
||||||
|
*/
|
||||||
|
initAddToCartButtonInitialState: () => Effect.Effect<void>;
|
||||||
|
/**
|
||||||
|
* Initialise les mises à jour du Bouton d'ajout au Panier en fonction des interactions de l'Utilisateur.
|
||||||
|
*/
|
||||||
|
initAddToCartButtonUpdates: () => Effect.Effect<void>;
|
||||||
|
/**
|
||||||
|
* Initialise les interactions des Sections de la Description du Produit.
|
||||||
|
*/
|
||||||
|
initDetailInteractions: () => Effect.Effect<void, NoSuchElementError>;
|
||||||
|
/**
|
||||||
|
* Met à jour l'état des Sections de la Description du Produit.
|
||||||
|
*/
|
||||||
|
onDetailButtonClickHandler: (evt: Event) => Effect.Effect<void, NoSuchElementError>;
|
||||||
|
/**
|
||||||
|
* Met à jour l'état du Bouton d'ajout au Panier.
|
||||||
|
*/
|
||||||
|
onFormChangeHandler: (evt: Event) => Effect.Effect<void>;
|
||||||
/**
|
/**
|
||||||
* Replie toutes les sections de la description du Produit.
|
* Replie toutes les sections de la description du Produit.
|
||||||
*/
|
*/
|
||||||
|
|
@ -91,7 +126,19 @@ class ProductPageDOM extends Context.Service<
|
||||||
static readonly layer = Layer.effect(
|
static readonly layer = Layer.effect(
|
||||||
ProductPageDOM,
|
ProductPageDOM,
|
||||||
Effect.gen(function*() {
|
Effect.gen(function*() {
|
||||||
const { Details, VariationSelectors } = yield* ProductPageElements;
|
const { AddToCartButton, Details, ProductPrice, DetailsButtons, ProductRawJson, VariationChoiceForm, VariationSelectors } =
|
||||||
|
yield* ProductPageElements;
|
||||||
|
|
||||||
|
const onFormChangeHandler = Effect.fnUntraced(function*(evt: Event) {
|
||||||
|
// La cible ne peut qu'être un Formulaire.
|
||||||
|
const target: HTMLFormElement = evt.target as HTMLFormElement;
|
||||||
|
const isClickAllowed = target.checkValidity() === false;
|
||||||
|
|
||||||
|
// Active/désactive le Bouton en fonction de la validité du Formulaire du Produit.
|
||||||
|
AddToCartButton.toggleAttribute(ATTRIBUT_DESACTIVE, isClickAllowed);
|
||||||
|
|
||||||
|
return yield* Effect.void;
|
||||||
|
});
|
||||||
|
|
||||||
const toggleAllDetails: () => Effect.Effect<void> = () =>
|
const toggleAllDetails: () => Effect.Effect<void> = () =>
|
||||||
Effect.sync((): void => {
|
Effect.sync((): void => {
|
||||||
|
|
@ -105,6 +152,37 @@ class ProductPageDOM extends Context.Service<
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onDetailButtonClickHandler = Effect.fnUntraced(function*(evt: Event) {
|
||||||
|
// Empêche la pollution de l'historique de navigation
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
// La cible est connue.
|
||||||
|
const target = evt.target as HTMLButtonElement;
|
||||||
|
|
||||||
|
// Récupère le contenu correspondant au Bouton.
|
||||||
|
const linkedSection = yield* pipe(
|
||||||
|
Option.fromNullishOr(target.getAttribute(ATTRIBUT_ARIA_CONTROLS)),
|
||||||
|
Option.flatMap((contentId: string) => HashMap.get(Details, contentId)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sauvegarde l'état d'ouverture de la Section avant de toutes les fermer.
|
||||||
|
const wasCurrentSection: boolean = target.getAttribute(ATTRIBUT_ARIA_EXPANDED) === "true";
|
||||||
|
|
||||||
|
// Replie toutes les Sections.
|
||||||
|
yield* toggleAllDetails();
|
||||||
|
|
||||||
|
// Ne fais rien de plus si l'onglet sélectionné était le courant
|
||||||
|
if (wasCurrentSection === true) {
|
||||||
|
return yield* Effect.void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ouvre le nouvel onglet sélectionné
|
||||||
|
target.toggleAttribute(ATTRIBUT_ARIA_EXPANDED, true);
|
||||||
|
linkedSection.content.toggleAttribute(ATTRIBUT_HIDDEN, false);
|
||||||
|
|
||||||
|
return yield* Effect.void;
|
||||||
|
});
|
||||||
|
|
||||||
const getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>> = () =>
|
const getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>> = () =>
|
||||||
Effect.sync(() =>
|
Effect.sync(() =>
|
||||||
FxArray.map(VariationSelectors, (select: HTMLSelectElement) => ({
|
FxArray.map(VariationSelectors, (select: HTMLSelectElement) => ({
|
||||||
|
|
@ -113,8 +191,79 @@ class ProductPageDOM extends Context.Service<
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const initAddToCartButtonInitialState = Effect.fn("initAddToCartButtonInitialState")(function*() {
|
||||||
|
/** Est-ce que le Produit affiché est en stock ? */
|
||||||
|
const isProductInStock = AddToCartButton.hasAttribute("data-in-stock") === true;
|
||||||
|
|
||||||
|
// S'il n'y a pas de stock, ne rien faire.
|
||||||
|
if (isProductInStock === false) {
|
||||||
|
return yield* Effect.void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// S'il n'y a pas de Sélecteurs de variations, activer le Bouton d'ajout au Panier.
|
||||||
|
if (FxArray.isReadonlyArrayEmpty(VariationSelectors)) {
|
||||||
|
AddToCartButton.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return yield* Effect.void;
|
||||||
|
});
|
||||||
|
|
||||||
|
const initAddToCartButtonUpdates = Effect.fn("initAddToCartInteractionUpdates")(function*() {
|
||||||
|
return yield* pipe(
|
||||||
|
Stream.fromEventListener(VariationChoiceForm, "change"),
|
||||||
|
Stream.tap(onFormChangeHandler),
|
||||||
|
Stream.runDrain,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const initPriceUpdatesOnVariationChange = Effect.fn("initPriceUpdatesOnVariationChange")(function*(){
|
||||||
|
return yield* pipe(
|
||||||
|
Stream.fromEventListener(VariationChoiceForm, "change"),
|
||||||
|
Stream.tap(onVariationChangeHandler),
|
||||||
|
Stream.runDrain,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
const onVariationChangeHandler = Effect.fn("onVariationChangeHandler")(function*(){
|
||||||
|
if (VariationChoiceForm.checkValidity() === false) {
|
||||||
|
return yield* Effect.void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variations = JSON.parse(ProductRawJson.textContent)?.variations as ReadonlyArray<unknown>;
|
||||||
|
const chosenAttributes = yield* getProductAttributesFromDOM();
|
||||||
|
|
||||||
|
const equivalence = FxArray.makeEquivalence<{attribute: string,value: string}>((a,b) => {
|
||||||
|
return a.attribute === b.attribute && a.value === b.value;
|
||||||
|
});
|
||||||
|
const chosenVariation = yield* FxArray.findFirst(variations, variation => equivalence(variation.attributes, chosenAttributes));
|
||||||
|
const newPrice = chosenVariation.price;
|
||||||
|
|
||||||
|
ProductPrice.textContent = `${newPrice}€`;
|
||||||
|
return yield* Effect.void;
|
||||||
|
});
|
||||||
|
|
||||||
|
const initDetailInteractions = Effect.fn("initDetailInteractions")(function*() {
|
||||||
|
return yield* pipe(
|
||||||
|
// Créé un Stream par Bouton de Section.
|
||||||
|
FxArray.map(
|
||||||
|
DetailsButtons,
|
||||||
|
(button: HTMLButtonElement) =>
|
||||||
|
pipe(Stream.fromEventListener(button, "click"), Stream.tap(onDetailButtonClickHandler)),
|
||||||
|
),
|
||||||
|
Stream.mergeAll({ concurrency: "unbounded" }),
|
||||||
|
Stream.runDrain,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return ProductPageDOM.of({
|
return ProductPageDOM.of({
|
||||||
getProductAttributesFromDOM,
|
getProductAttributesFromDOM,
|
||||||
|
initAddToCartButtonInitialState,
|
||||||
|
initAddToCartButtonUpdates,
|
||||||
|
initDetailInteractions,
|
||||||
|
initPriceUpdatesOnVariationChange,
|
||||||
|
onDetailButtonClickHandler,
|
||||||
|
onFormChangeHandler,
|
||||||
|
onVariationChangeHandler,
|
||||||
toggleAllDetails,
|
toggleAllDetails,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,10 @@
|
||||||
// Scripts pour la Page Produit
|
// Scripts pour la Page Produit
|
||||||
|
|
||||||
import { pipe } from "@mobily/ts-belt";
|
import { pipe } from "@mobily/ts-belt";
|
||||||
import { get as dictGet } from "@mobily/ts-belt/Dict";
|
import { Console, Effect, pipe as epipe } from "effect";
|
||||||
import { tap as optionTap } from "@mobily/ts-belt/Option";
|
|
||||||
import { Array as FxArray, Console, Effect, HashMap, Option, pipe as epipe, Stream } from "effect";
|
|
||||||
import { EitherAsync } from "purify-ts";
|
|
||||||
import { match, P } from "ts-pattern";
|
|
||||||
import { ValiError } from "valibot";
|
|
||||||
import type { AnySchema } from "valibot";
|
|
||||||
|
|
||||||
import type { WCStoreCartAddItemArgs, WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.ts";
|
|
||||||
import type { WCStoreCart } from "./lib/types/api/cart.ts";
|
|
||||||
import type { FetchErrors } from "./lib/types/reseau.ts";
|
|
||||||
|
|
||||||
import { ROUTE_API_AJOUTE_ARTICLE_PANIER } from "./constantes/api.ts";
|
import { ProductPageRuntime } from "./scripts-page-produit-service.ts";
|
||||||
import {
|
|
||||||
ATTRIBUT_ARIA_CONTROLS,
|
|
||||||
ATTRIBUT_ARIA_EXPANDED,
|
|
||||||
ATTRIBUT_CHARGEMENT,
|
|
||||||
ATTRIBUT_DESACTIVE,
|
|
||||||
ATTRIBUT_HIDDEN,
|
|
||||||
DOM_BOUTON_AJOUT_PANIER,
|
|
||||||
DOM_BOUTONS_ACCORDEON,
|
|
||||||
DOM_CONTENUS_ACCORDEON,
|
|
||||||
DOM_DOM_QUANTITE,
|
|
||||||
DOM_PRIX_PRODUIT,
|
|
||||||
} from "./constantes/dom.ts";
|
|
||||||
import { lanceAnimationCycleLoading } from "./lib/animations.ts";
|
|
||||||
import { mustGetEleInDocument, mustGetElesInDocument, recupereElementDocumentEither } from "./lib/dom.ts";
|
|
||||||
import { BadRequestError, reporteErreur, ServerError } from "./lib/erreurs.ts";
|
|
||||||
import { emetMessageMajBoutonPanier } from "./lib/messages.ts";
|
|
||||||
import { newPartialResponse, postBackend, safeFetch } from "./lib/reseau.ts";
|
|
||||||
import { WCStoreCartAddItemArgsSchema } from "./lib/schemas/api/cart-add-item.ts";
|
|
||||||
import { WCStoreCartSchema } from "./lib/schemas/api/cart.ts";
|
|
||||||
import { safeSchemaParse } from "./lib/validation.ts";
|
|
||||||
import { ProductPageElements, ProductPageRuntime } from "./scripts-page-produit-service.ts";
|
|
||||||
|
|
||||||
/** États utiles pour les scripts de la page. */
|
/** États utiles pour les scripts de la page. */
|
||||||
type EtatsPage = {
|
type EtatsPage = {
|
||||||
|
|
@ -72,199 +42,93 @@ const updatePriceOnAttributeChange = (): void => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ajouteProduitAuPanier = (event: MouseEvent): void => {
|
// const ajouteProduitAuPanier = (event: MouseEvent): void => {
|
||||||
event.preventDefault();
|
// event.preventDefault();
|
||||||
console.debug("getAttributeValuesFromDom", getAttributesFromDom());
|
// console.debug("getAttributeValuesFromDom", getAttributesFromDom());
|
||||||
|
|
||||||
// Construis les arguments de la requête au backend
|
// // Construis les arguments de la requête au backend
|
||||||
const argsRequete: WCStoreCartAddItemArgs = {
|
// const argsRequete: WCStoreCartAddItemArgs = {
|
||||||
id: E.DOM_VARIATION.map((selecteur: HTMLSelectElement): number => Number(selecteur.value))
|
// id: E.DOM_VARIATION.map((selecteur: HTMLSelectElement): number => Number(selecteur.value))
|
||||||
// Récupère l'ID du Produit de la Page pour les Produits simples
|
// // Récupère l'ID du Produit de la Page pour les Produits simples
|
||||||
.orDefault(ETATS_PAGE.idProduit),
|
// .orDefault(ETATS_PAGE.idProduit),
|
||||||
quantity: 1,
|
// quantity: 1,
|
||||||
variation: getAttributesFromDom(),
|
// variation: getAttributesFromDom(),
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Réalise la Requête et traite sa Réponse
|
// // Réalise la Requête et traite sa Réponse
|
||||||
void EitherAsync
|
// void EitherAsync
|
||||||
// 1. Valide les arguments de la Requête
|
// // 1. Valide les arguments de la Requête
|
||||||
.liftEither(safeSchemaParse(argsRequete, WCStoreCartAddItemArgsSchema))
|
// .liftEither(safeSchemaParse(argsRequete, WCStoreCartAddItemArgsSchema))
|
||||||
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
|
// // 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
|
||||||
.ifRight(() => {
|
// .ifRight(() => {
|
||||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
// // Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||||
E.BOUTON_AJOUT_PANIER.setAttribute(ATTRIBUT_DESACTIVE, "");
|
// E.BOUTON_AJOUT_PANIER.setAttribute(ATTRIBUT_DESACTIVE, "");
|
||||||
E.BOUTON_AJOUT_PANIER.setAttribute(ATTRIBUT_CHARGEMENT, "");
|
// E.BOUTON_AJOUT_PANIER.setAttribute(ATTRIBUT_CHARGEMENT, "");
|
||||||
|
|
||||||
// Lance un cycle d'animation sur le texte de chargement
|
// // Lance un cycle d'animation sur le texte de chargement
|
||||||
lanceAnimationCycleLoading(E.BOUTON_AJOUT_PANIER, 500);
|
// lanceAnimationCycleLoading(E.BOUTON_AJOUT_PANIER, 500);
|
||||||
})
|
// })
|
||||||
// 3. Exécute la requête via fetch sous forme d'EitherAsync
|
// // 3. Exécute la requête via fetch sous forme d'EitherAsync
|
||||||
.chain((args: WCStoreCartAddItemArgs) =>
|
// .chain((args: WCStoreCartAddItemArgs) =>
|
||||||
safeFetch(
|
// safeFetch(
|
||||||
postBackend({
|
// postBackend({
|
||||||
corps: JSON.stringify(args),
|
// corps: JSON.stringify(args),
|
||||||
nonce: ETATS_PAGE.nonce,
|
// nonce: ETATS_PAGE.nonce,
|
||||||
route: ROUTE_API_AJOUTE_ARTICLE_PANIER,
|
// route: ROUTE_API_AJOUTE_ARTICLE_PANIER,
|
||||||
}),
|
// }),
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
|
// // 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
|
||||||
.chain((reponse: Response) =>
|
// .chain((reponse: Response) =>
|
||||||
EitherAsync<BadRequestError | ServerError, unknown>(async ({ throwE }) =>
|
// EitherAsync<BadRequestError | ServerError, unknown>(async ({ throwE }) =>
|
||||||
// Simplifie les données à matcher
|
// // Simplifie les données à matcher
|
||||||
match(await newPartialResponse(reponse))
|
// match(await newPartialResponse(reponse))
|
||||||
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
// .with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
|
||||||
.with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
|
// .with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
|
||||||
.with({ status: 201 }, r => r.body)
|
// .with({ status: 201 }, r => r.body)
|
||||||
.otherwise(erreur => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`)))
|
// .otherwise(erreur => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`)))
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
// 5. Vérifie le Schéma de la Réponse
|
// // 5. Vérifie le Schéma de la Réponse
|
||||||
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(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
|
// // 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
|
||||||
.ifRight((panier: WCStoreCart) =>
|
// .ifRight((panier: WCStoreCart) =>
|
||||||
pipe(
|
// pipe(
|
||||||
dictGet(panier, "items_count"),
|
// dictGet(panier, "items_count"),
|
||||||
optionTap((totalArticles: number) => {
|
// optionTap((totalArticles: number) => {
|
||||||
E.BOUTON_AJOUT_PANIER.textContent = "Added to cart!";
|
// E.BOUTON_AJOUT_PANIER.textContent = "Added to cart!";
|
||||||
emetMessageMajBoutonPanier({ quantiteProduits: totalArticles });
|
// emetMessageMajBoutonPanier({ quantiteProduits: totalArticles });
|
||||||
}),
|
// }),
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
.ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError<AnySchema>) => {
|
// .ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError<AnySchema>) => {
|
||||||
match(erreur)
|
// match(erreur)
|
||||||
.with(P.instanceOf(ValiError), e => {
|
// .with(P.instanceOf(ValiError), e => {
|
||||||
reporteErreur(e);
|
// reporteErreur(e);
|
||||||
console.error(e.issues);
|
// console.error(e.issues);
|
||||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
// // E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||||
})
|
// })
|
||||||
.with(P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
|
// .with(P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
|
||||||
reporteErreur(e);
|
// reporteErreur(e);
|
||||||
console.error(e);
|
// console.error(e);
|
||||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
// // E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
|
||||||
})
|
// })
|
||||||
.with(P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
|
// .with(P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
|
||||||
reporteErreur(e);
|
// reporteErreur(e);
|
||||||
console.error(e);
|
// console.error(e);
|
||||||
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
|
// // E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
|
||||||
})
|
// })
|
||||||
.exhaustive();
|
// .exhaustive();
|
||||||
|
|
||||||
E.BOUTON_AJOUT_PANIER.textContent = "Add to cart";
|
// E.BOUTON_AJOUT_PANIER.textContent = "Add to cart";
|
||||||
})
|
// })
|
||||||
.finally((): void => {
|
// .finally((): void => {
|
||||||
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
// // Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
|
||||||
E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_CHARGEMENT);
|
// E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_CHARGEMENT);
|
||||||
E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_DESACTIVE);
|
// E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_DESACTIVE);
|
||||||
})
|
// })
|
||||||
.run();
|
// .run();
|
||||||
};
|
// };
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise l'état initial d'interactivité du Bouton d'ajout de Produit au Panier.
|
|
||||||
*/
|
|
||||||
const initAddToCartButton = Effect.fn("initAddToCartButton")(function*() {
|
|
||||||
const { AddProductButton, VariationSelectors } = yield* ProductPageElements;
|
|
||||||
/** Est-ce que le Produit affiché est en stock ? */
|
|
||||||
const isProductInStock = AddProductButton.hasAttribute("data-in-stock") === true;
|
|
||||||
|
|
||||||
// S'il n'y a pas de stock, ne rien faire.
|
|
||||||
if (isProductInStock === false) {
|
|
||||||
console.debug("initAddToCartButton", "Pas de stock.");
|
|
||||||
return yield* Effect.void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// S'il n'y a pas de Sélecteurs de variations, activer le Bouton d'ajout au Panier.
|
|
||||||
if (FxArray.isReadonlyArrayEmpty(VariationSelectors)) {
|
|
||||||
console.debug("initAddToCartButton", "Produt simple.");
|
|
||||||
E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_DESACTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return yield* Effect.void;
|
|
||||||
});
|
|
||||||
|
|
||||||
const onFormChange = Effect.fnUntraced(function*(evt: Event) {
|
|
||||||
const { AddProductButton } = yield* ProductPageElements;
|
|
||||||
// La cible ne peut qu'être un Formulaire.
|
|
||||||
const target: HTMLFormElement = evt.target as HTMLFormElement;
|
|
||||||
const isClickAllowed = target.checkValidity() === false;
|
|
||||||
|
|
||||||
// Active/désactive le Bouton en fonction de la validité du Formulaire du Produit.
|
|
||||||
AddProductButton.toggleAttribute(ATTRIBUT_DESACTIVE, isClickAllowed);
|
|
||||||
|
|
||||||
return yield* Effect.void;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise la mise à jour de l'état d'interactivité du Bouton d'ajout de Produit au Panier en fonction des actions de l'Utilisateur.
|
|
||||||
*/
|
|
||||||
const initAddToCartInteractionUpdates = Effect.fn("initAddToCartInteractionUpdates")(function*() {
|
|
||||||
return yield* pipe(
|
|
||||||
Stream.fromEventListener(E.VARIATION_CHOICE_FORM, "change"),
|
|
||||||
Stream.tap(onFormChange),
|
|
||||||
Stream.runDrain,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const onDetailButtonClick = Effect.fnUntraced(function*(evt: Event) {
|
|
||||||
const { Details } = yield* ProductPageElements;
|
|
||||||
// Empêche la pollution de l'historique de navigation
|
|
||||||
evt.preventDefault();
|
|
||||||
|
|
||||||
// La cible est connue.
|
|
||||||
const target = evt.target as HTMLButtonElement;
|
|
||||||
// Récupère le contenu correspondant.
|
|
||||||
const linkedSection = yield* pipe(
|
|
||||||
Option.fromNullishOr(target.getAttribute(ATTRIBUT_ARIA_CONTROLS)),
|
|
||||||
Option.flatMap((contentId: string) => HashMap.get(Details, contentId)),
|
|
||||||
);
|
|
||||||
// Sauvegarde l'état d'ouverture de la Section avant de toutes les fermer.
|
|
||||||
const wasCurrentSection: boolean = target.getAttribute(ATTRIBUT_ARIA_EXPANDED) === "true";
|
|
||||||
|
|
||||||
// Replie toutes les Sections.
|
|
||||||
yield* toggleAllDetails();
|
|
||||||
|
|
||||||
// Ne fais rien de plus si l'onglet sélectionné était le courant
|
|
||||||
if (wasCurrentSection === true) {
|
|
||||||
return yield* Effect.void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ouvre le nouvel onglet sélectionné
|
|
||||||
target.toggleAttribute(ATTRIBUT_ARIA_EXPANDED, true);
|
|
||||||
linkedSection.content.toggleAttribute(ATTRIBUT_HIDDEN, false);
|
|
||||||
|
|
||||||
return yield* Effect.void;
|
|
||||||
});
|
|
||||||
|
|
||||||
const initDetailInteractions = Effect.fn("initDetailInteractions")(function*() {
|
|
||||||
const PageElements = yield* ProductPageElements;
|
|
||||||
|
|
||||||
return yield* pipe(
|
|
||||||
FxArray.map(
|
|
||||||
PageElements.DetailsButtons,
|
|
||||||
(button: HTMLButtonElement) => pipe(Stream.fromEventListener(button, "click"), Stream.tap(onDetailButtonClick)),
|
|
||||||
),
|
|
||||||
Stream.mergeAll({ concurrency: "unbounded" }),
|
|
||||||
Stream.runDrain,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const getAttributesFromDom = (): ReadonlyArray<WCStoreCartAddItemArgsItems> => {
|
|
||||||
const selectElements = epipe(
|
|
||||||
document.querySelectorAll<HTMLSelectElement>(".selecteur-produit select"),
|
|
||||||
Array.from<HTMLSelectElement>,
|
|
||||||
);
|
|
||||||
if (selectElements.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const attributes = selectElements.map((select: HTMLSelectElement) => ({
|
|
||||||
attribute: select.id,
|
|
||||||
value: select.value,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return attributes;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (): void => {
|
document.addEventListener("DOMContentLoaded", (): void => {
|
||||||
ProductPageRuntime.runFork(pipe(initAddToCartButton(), Effect.tapCause(Console.error)));
|
ProductPageRuntime.runFork(pipe(initAddToCartButton(), Effect.tapCause(Console.error)));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue