diff --git a/.zed/settings.json b/.zed/settings.json index becfd951..1c6babec 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -2,7 +2,9 @@ "language_servers": [ "!biome", "!deno", + "!eslint", "!intelephense", + "!oxfmt", "!prettier", "!tailwindcss-language-server", "!vtsls", diff --git a/bun.lock b/bun.lock index bcff6e5a..576ae3e7 100644 --- a/bun.lock +++ b/bun.lock @@ -17,7 +17,7 @@ "devDependencies": { "@effect/language-service": "^0.84.3", "@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274", - "@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#3e49f5e2fb", + "@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#bedd1fa23aff", "@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801", "@playwright/test": "^1.59.1", "@sentry/core": "^10.47.0", @@ -26,7 +26,7 @@ "@vitejs/plugin-legacy": "^8.0.1", "better-typescript-lib": "^2.12.0", "browserslist": "^4.28.2", - "caniuse-lite": "^1.0.30001785", + "caniuse-lite": "^1.0.30001786", "eslint": "^10.2.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -44,7 +44,7 @@ "playwright": "^1.59.1", "prettier": "^3.8.1", "prettier-plugin-pkg": "^0.22.1", - "prettier-plugin-sh": "^0.18.0", + "prettier-plugin-sh": "^0.18.1", "sass-embedded": "^1.99.0", "stylelint": "^17.6.0", "stylelint-config-clean-order": "^8.0.1", @@ -54,7 +54,7 @@ "stylelint-plugin-logical-css": "^2.1.0", "typescript": "6.0.2", "typescript-eslint": "^8.58.0", - "vite": "^8.0.3", + "vite": "^8.0.5", "vite-tsconfig-paths": "^6.1.1", }, }, @@ -296,7 +296,7 @@ "@gcch/configuration-eslint": ["@gcch/configuration-eslint@git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274f0bfebd5135a728960644f4b1cdcb8", { "dependencies": { "@eslint/js": "^10.0.1", "astro-eslint-parser": "^1.3.0", "eslint": "^10.0.3", "eslint-plugin-astro": "^1.6.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsdoc": "^62.8.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^5.6.0", "eslint-plugin-sonarjs": "^4.0.2", "eslint-plugin-unicorn": "^63.0.0", "globals": "^17.4.0", "typescript-eslint": "^8.57.0" }, "peerDependencies": { "eslint": "^10.0.3", "typescript": "^6.0.1-rc" } }, "62ee424274f0bfebd5135a728960644f4b1cdcb8"], - "@gcch/configuration-oxlint": ["@gcch/configuration-oxlint@git+https://git.gcch.fr/gcch/configuration-oxlint#3e49f5e2fbfde01ad3d75acbff0da8023d5a3802", { "dependencies": { "eslint-plugin-astro": "^1.6.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^5.8.0", "eslint-plugin-sonarjs": "^4.0.2", "globals": "^17.4.0", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0" }, "peerDependencies": { "eslint-plugin-astro": "^1.6.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^5.8.0", "eslint-plugin-sonarjs": "^4.0.2", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0", "typescript": "^6.0.2" } }, "3e49f5e2fbfde01ad3d75acbff0da8023d5a3802"], + "@gcch/configuration-oxlint": ["@gcch/configuration-oxlint@git+https://git.gcch.fr/gcch/configuration-oxlint#bedd1fa23affdb4c8ac4d3b39c4cc095791a3c05", { "dependencies": { "eslint-plugin-astro": "^1.6.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^5.8.0", "eslint-plugin-sonarjs": "^4.0.2", "globals": "^17.4.0", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0" }, "peerDependencies": { "eslint-plugin-astro": "^1.6.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^5.8.0", "eslint-plugin-sonarjs": "^4.0.2", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0", "typescript": "^6.0.2" } }, "bedd1fa23affdb4c8ac4d3b39c4cc095791a3c05"], "@gcch/configuration-prettier": ["@gcch/configuration-prettier@git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801bd44784ac91e0ff6e038d838f7eea1", { "dependencies": { "prettier": "^3.8.1", "prettier-plugin-curly": "^0.4.1", "prettier-plugin-ini": "^1.3.0", "prettier-plugin-jsdoc": "^1.8.0", "prettier-plugin-pkg": "^0.22.0", "prettier-plugin-sh": "^0.18.0", "prettier-plugin-sort-json": "^4.2.0" }, "peerDependencies": { "prettier": "^3.8.1" } }, "8de937e801bd44784ac91e0ff6e038d838f7eea1"], @@ -510,7 +510,7 @@ "@playwright/test": ["@playwright/test@1.59.1", "", { "dependencies": { "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" } }, "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg=="], - "@reteps/dockerfmt": ["@reteps/dockerfmt@0.3.6", "", {}, "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg=="], + "@reteps/dockerfmt": ["@reteps/dockerfmt@0.5.2", "", {}, "sha512-Hbr7yen4fP5TxGM54ucXa4o5NwWXatJ6Bd9I8gp0PValYbI4Rug2Gu+rVv7K7o/efQc3F5ctqWJz47rYaa8zBw=="], "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="], @@ -726,7 +726,7 @@ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001785", "", {}, "sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ=="], + "caniuse-lite": ["caniuse-lite@1.0.30001786", "", {}, "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA=="], "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="], @@ -1318,7 +1318,7 @@ "prettier-plugin-pkg": ["prettier-plugin-pkg@0.22.1", "", { "peerDependencies": { "prettier": "^3.0.3" } }, "sha512-l9qnxic48hgTXsvXi9yWWWzovpqkEYo/Ljf8Af7zSkDTNVmLwscXW63IEVMJXH94DyloO8YkogeLheJUzJh/ZQ=="], - "prettier-plugin-sh": ["prettier-plugin-sh@0.18.0", "", { "dependencies": { "@reteps/dockerfmt": "^0.3.6", "sh-syntax": "^0.5.8" }, "peerDependencies": { "prettier": "^3.6.0" } }, "sha512-cW1XL27FOJQ/qGHOW6IHwdCiNWQsAgK+feA8V6+xUTaH0cD3Mh+tFAtBvEEWvuY6hTDzRV943Fzeii+qMOh7nQ=="], + "prettier-plugin-sh": ["prettier-plugin-sh@0.18.1", "", { "dependencies": { "@reteps/dockerfmt": "^0.5.1", "sh-syntax": "^0.5.8" }, "peerDependencies": { "prettier": "^3.6.0" } }, "sha512-uZmU22wBMevjh3rmCatNQqiEer2+5KLa0xYCBX6zQQUQkcNzVL+s6FbPKK6ZSUNUbQk6jMAcQHrYPvuL2W6ihQ=="], "prettier-plugin-sort-json": ["prettier-plugin-sort-json@4.2.0", "", { "peerDependencies": { "prettier": "^3.0.0" } }, "sha512-jK1w3/7otTvHtv1eoLji2U9mEoOGeyl7QQQ/afLnjht1YtRLSUUk8o0rIIC/HUVXhoGPCFe4SVZbRGYjjUVgvA=="], @@ -1588,7 +1588,7 @@ "varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="], - "vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="], + "vite": ["vite@8.0.5", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ=="], "vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="], @@ -1634,6 +1634,8 @@ "@gcch/configuration-eslint/eslint": ["eslint@10.1.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.3", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA=="], + "@gcch/configuration-prettier/prettier-plugin-sh": ["prettier-plugin-sh@0.18.0", "", { "dependencies": { "@reteps/dockerfmt": "^0.3.6", "sh-syntax": "^0.5.8" }, "peerDependencies": { "prettier": "^3.6.0" } }, "sha512-cW1XL27FOJQ/qGHOW6IHwdCiNWQsAgK+feA8V6+xUTaH0cD3Mh+tFAtBvEEWvuY6hTDzRV943Fzeii+qMOh7nQ=="], + "@keyv/bigmap/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], @@ -1702,6 +1704,8 @@ "@gcch/configuration-eslint/eslint/@eslint/plugin-kit": ["@eslint/plugin-kit@0.6.1", "", { "dependencies": { "@eslint/core": "^1.1.1", "levn": "^0.4.1" } }, "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ=="], + "@gcch/configuration-prettier/prettier-plugin-sh/@reteps/dockerfmt": ["@reteps/dockerfmt@0.3.6", "", {}, "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg=="], + "eslint-plugin-jsx-a11y/minimatch/brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="], "stylelint/file-entry-cache/flat-cache": ["flat-cache@6.1.22", "", { "dependencies": { "cacheable": "^2.3.4", "flatted": "^3.4.2", "hookified": "^1.15.0" } }, "sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug=="], diff --git a/justfile b/justfile index 90fd2f49..d420707c 100755 --- a/justfile +++ b/justfile @@ -81,7 +81,7 @@ watch-css: # Compile TypeScript en JavaScript. [group('js')] build-js: - @bun --bun vite build --config "cfg/vite.config.ts" + bun --bun vite build --config "cfg/vite.config.ts" # Compile tout. [group('css')] @@ -94,7 +94,7 @@ build-all: # Compile TypeScript à chaque changement de fichier. [group('js')] watch-js: - bun vite build --watch + bun --bun vite build --config "cfg/vite.config.ts" --watch # Vérifie le code TypeScript avec des analyseurs statiques. [group('js')] diff --git a/package.json b/package.json index 7f6520c9..f170195f 100755 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "devDependencies": { "@effect/language-service": "^0.84.3", "@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274", - "@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#3e49f5e2fb", + "@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#bedd1fa23aff", "@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801", "@playwright/test": "^1.59.1", "@sentry/core": "^10.47.0", @@ -32,7 +32,7 @@ "@vitejs/plugin-legacy": "^8.0.1", "better-typescript-lib": "^2.12.0", "browserslist": "^4.28.2", - "caniuse-lite": "^1.0.30001785", + "caniuse-lite": "^1.0.30001786", "eslint": "^10.2.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -50,7 +50,7 @@ "playwright": "^1.59.1", "prettier": "^3.8.1", "prettier-plugin-pkg": "^0.22.1", - "prettier-plugin-sh": "^0.18.0", + "prettier-plugin-sh": "^0.18.1", "sass-embedded": "^1.99.0", "stylelint": "^17.6.0", "stylelint-config-clean-order": "^8.0.1", @@ -60,7 +60,7 @@ "stylelint-plugin-logical-css": "^2.1.0", "typescript": "6.0.2", "typescript-eslint": "^8.58.0", - "vite": "^8.0.3", + "vite": "^8.0.5", "vite-tsconfig-paths": "^6.1.1" }, "browserslist": [ diff --git a/phpstan.neon b/phpstan.neon index fc243960..5076bcdd 100755 --- a/phpstan.neon +++ b/phpstan.neon @@ -21,6 +21,8 @@ parameters: reportWrongPhpDocTypeInVarTag: true # Setting treatPhpDocTypesAsCertain to false relaxes some of the rules around type-checking. treatPhpDocTypesAsCertain: true + # PHP silently casts array keys that look like decimal integers from string to int. This means array can’t guarantee that keys are actually strings at runtime. + reportUnsafeArrayStringKeyCasting: true parallel: jobSize: 20 diff --git a/web/app/themes/haiku-atelier-2024/404.php b/web/app/themes/haiku-atelier-2024/404.php index cc280047..f6296dd4 100755 --- a/web/app/themes/haiku-atelier-2024/404.php +++ b/web/app/themes/haiku-atelier-2024/404.php @@ -31,7 +31,4 @@ function load_page_resources(): void { add_action('wp_enqueue_scripts', load_page_resources(...)); -Timber::render( - data: $context, - filenames: $templates, -); +Timber::render(data: $context, filenames: $templates); diff --git a/web/app/themes/haiku-atelier-2024/archive-product.php b/web/app/themes/haiku-atelier-2024/archive-product.php index 8cba877d..235f751e 100755 --- a/web/app/themes/haiku-atelier-2024/archive-product.php +++ b/web/app/themes/haiku-atelier-2024/archive-product.php @@ -23,10 +23,7 @@ $templates = ['boutique.twig']; /** @var list $wc_products Les informations brutes des Produits. */ $wc_products = wc_get_products(['limit' => 12, 'order' => 'DESC', 'orderby' => 'date', 'status' => 'publish']); -$products = array_map( - callback: Product::new(...), - array: $wc_products, -); +$products = array_map(callback: Product::from_wc_product(...), array: $wc_products); $context['products'] = $products; add_action('wp_enqueue_scripts', function (): void { @@ -44,7 +41,4 @@ add_action('wp_enqueue_scripts', function (): void { ); }); -Timber::render( - data: $context, - filenames: $templates, -); +Timber::render(data: $context, filenames: $templates); diff --git a/web/app/themes/haiku-atelier-2024/front-page.php b/web/app/themes/haiku-atelier-2024/front-page.php index b4963c8f..5b890db9 100755 --- a/web/app/themes/haiku-atelier-2024/front-page.php +++ b/web/app/themes/haiku-atelier-2024/front-page.php @@ -27,7 +27,4 @@ add_action('wp_enqueue_scripts', function (): void { ); }); -Timber::render( - data: $context, - filenames: $templates, -); +Timber::render(data: $context, filenames: $templates); diff --git a/web/app/themes/haiku-atelier-2024/functions.php b/web/app/themes/haiku-atelier-2024/functions.php index 824e9e0d..334520ab 100755 --- a/web/app/themes/haiku-atelier-2024/functions.php +++ b/web/app/themes/haiku-atelier-2024/functions.php @@ -23,12 +23,6 @@ Timber::$dirname = ['views']; // Charge les Scripts du thème (report d'erreurs) function load_scripts(): void { - // wp_enqueue_script_module( - // id: 'haiku-atelier-2024-gaffe', - // deps: [], - // src: get_template_directory_uri() . '/assets/js/gaffe.js', - // version: filemtime(get_template_directory() . '/assets/js/gaffe.js'), - // ); wp_enqueue_script_module( id: 'haiku-atelier-2024-bouton-panier', deps: [], diff --git a/web/app/themes/haiku-atelier-2024/index.php b/web/app/themes/haiku-atelier-2024/index.php index c31016a1..8cd2f361 100755 --- a/web/app/themes/haiku-atelier-2024/index.php +++ b/web/app/themes/haiku-atelier-2024/index.php @@ -7,7 +7,4 @@ use Timber\Timber; $context = Timber::context(); $templates = ['base.twig']; -Timber::render( - data: $context, - filenames: $templates, -); +Timber::render(data: $context, filenames: $templates); diff --git a/web/app/themes/haiku-atelier-2024/page-about.php b/web/app/themes/haiku-atelier-2024/page-about.php index e4b8b1e4..6d8690be 100755 --- a/web/app/themes/haiku-atelier-2024/page-about.php +++ b/web/app/themes/haiku-atelier-2024/page-about.php @@ -40,7 +40,4 @@ add_action('wp_enqueue_scripts', function (): void { ); }); -Timber::render( - data: $context, - filenames: $templates, -); +Timber::render(data: $context, filenames: $templates); diff --git a/web/app/themes/haiku-atelier-2024/page-cart.php b/web/app/themes/haiku-atelier-2024/page-cart.php index c1c8ab4b..6effc12a 100755 --- a/web/app/themes/haiku-atelier-2024/page-cart.php +++ b/web/app/themes/haiku-atelier-2024/page-cart.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace HaikuAtelier; use HaikuAtelier\Data\Cart; +use HaikuAtelier\Data\Product; use HaikuAtelier\WP\Resource; use Illuminate\Support\Number; use Timber\Timber; @@ -17,13 +18,8 @@ use WC_Shipping_Rate; use function add_action; use function collect; use function Crell\fp\pipe; -use function genere_balise_img_multiformats; -use function recupere_et_formate_attributs_produit; use function WC; -// Importe la fonction pour récupérer les informations affichées des Produits dans le Panier -require_once __DIR__ . '/src/inc/TraitementInformations.php'; - $context = Timber::context(); $templates = ['panier.twig']; @@ -52,12 +48,12 @@ $shipping_subtotal = Cart::parse_cart_value($cart_totals['shipping_total'] ?? 0) foreach (WC()->cart->get_cart() as $cle_panier => $article_panier) { $cart[$cle_panier] = [ 'attributs' => $article_panier['data']?->get_type() === 'variation' - ? recupere_et_formate_attributs_produit($article_panier['data']?->get_attributes()) + ? Product::recupere_et_formate_attributs_produit($article_panier['data']?->get_attributes()) : [], 'cle' => $cle_panier, 'id_produit' => $article_panier['product_id'], 'id_variation' => $article_panier['variation_id'], - 'image' => pipe($article_panier['data']?->get_image_id(), static fn($id): string => genere_balise_img_multiformats( + 'image' => pipe($article_panier['data']?->get_image_id(), static fn($id): string => Resource::output_multi_formats_img_tag( id: (string) $id, lazy: true, )), @@ -110,7 +106,4 @@ add_action('wp_enqueue_scripts', function (): void { }); // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/page-contact.php b/web/app/themes/haiku-atelier-2024/page-contact.php index 8a557b91..393e3d33 100755 --- a/web/app/themes/haiku-atelier-2024/page-contact.php +++ b/web/app/themes/haiku-atelier-2024/page-contact.php @@ -24,7 +24,4 @@ add_action('wp_enqueue_scripts', function (): void { }); // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/page-failed-order.php b/web/app/themes/haiku-atelier-2024/page-failed-order.php index df6cdc94..ea428114 100755 --- a/web/app/themes/haiku-atelier-2024/page-failed-order.php +++ b/web/app/themes/haiku-atelier-2024/page-failed-order.php @@ -24,7 +24,4 @@ add_action('wp_enqueue_scripts', function (): void { }); // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/page-successful-order.php b/web/app/themes/haiku-atelier-2024/page-successful-order.php index 9bb6d636..e72c835a 100755 --- a/web/app/themes/haiku-atelier-2024/page-successful-order.php +++ b/web/app/themes/haiku-atelier-2024/page-successful-order.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace HaikuAtelier; +use HaikuAtelier\WP\Resource; use Roots\WPConfig\Config; use Stripe\Checkout\Session; use Stripe\StripeClient; @@ -18,8 +19,6 @@ use WC_Order_Refund; use function Crell\fp\pipe; -require_once __DIR__ . '/src/inc/TraitementInformations.php'; - /** @var string $url_accueil L'URL de la page d'Accueil. */ $url_accueil = get_page_link(get_page_by_path('home')?->ID); @@ -87,7 +86,7 @@ try { return [ 'attribut' => $attribut, 'id_produit' => $id_produit, - 'image' => pipe($produit->get_image_id(), static fn($id): string => genere_balise_img_multiformats( + 'image' => pipe($produit->get_image_id(), static fn($id): string => Resource::output_multi_formats_img_tag( id: $id, lazy: true, )), @@ -114,10 +113,7 @@ try { add_action('wp_enqueue_scripts', 'charge_scripts_styles_page_succes_commande'); // Rendu - Timber::render( - filenames: $templates, - data: $context, - ); + Timber::render(filenames: $templates, data: $context); } catch (Error $error) { http_response_code(500); echo json_encode(['error' => esc_html($error->getMessage())]); diff --git a/web/app/themes/haiku-atelier-2024/page-terms-and-conditions.php b/web/app/themes/haiku-atelier-2024/page-terms-and-conditions.php index 2d3c1cce..0b9d5998 100755 --- a/web/app/themes/haiku-atelier-2024/page-terms-and-conditions.php +++ b/web/app/themes/haiku-atelier-2024/page-terms-and-conditions.php @@ -24,7 +24,4 @@ add_action('wp_enqueue_scripts', function (): void { }); // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/single-product.php b/web/app/themes/haiku-atelier-2024/single-product.php index d45a7281..a345224f 100755 --- a/web/app/themes/haiku-atelier-2024/single-product.php +++ b/web/app/themes/haiku-atelier-2024/single-product.php @@ -11,7 +11,9 @@ namespace HaikuAtelier; use Exception; use HaikuAtelier\Data\Product; use HaikuAtelier\WP\Resource; +use HaikuAtelier\WP\Term; use Illuminate\Support\Arr; +use Psl\Option\Option; use stdClass; use Timber\Timber; use WC_Product; @@ -20,12 +22,9 @@ use function add_action; use function assert; use function collect; use function is_array; -use function recupere_produits_meme_collection; use function wc_get_product; use function wp_json_encode; -require_once __DIR__ . '/src/inc/TraitementInformations.php'; - $context = Timber::context(); $templates = ['produit.twig']; @@ -37,23 +36,41 @@ if ($raw_product === null || $raw_product === false) { } // Assemble les données d'intérêt pour la page au sein d'une Classe. -$product = Product::new($raw_product); +$product = Product::from_wc_product($raw_product); /** @var int $maximum_price Le prix de la Variation la plus chère */ $maximum_price = collect($product->variations)->max('price'); /** @var list Les Produits de la même collection que celui affiché dans la Page. */ -$same_collection_products = recupere_produits_meme_collection($product->collection)($product->id) +$same_collection_products = Product::get_same_collection_products($product->collection)($product->id) |> function (/** @var list|stdClass */ mixed $products): array { assert(is_array($products), 'Les Produits de la même collection doivent être un tableau.'); return $products; } - |> (static fn(/** @var list */ array $products): array => Arr::map($products, Product::new(...))); + |> (static fn(/** @var list */ array $products): array => Arr::map( + $products, + Product::from_wc_product(...), + )); $context['product'] = $product; $context['product_json'] = wp_json_encode($product); $context['maximum_price'] = $maximum_price; $context['same_collection_products'] = $same_collection_products; +$product_tags = $raw_product->get_tag_ids() + |> (static fn($tags_ids) => Arr::map($tags_ids, static fn($id) => Term::get_term_by_id( + id: $id, + taxonomy: 'product_tag', + ))) + |> (static fn(/** @var list> */ $tags) => Arr::reject($tags, static fn($tag) => $tag->isNone())) + |> (static fn(/** @var list> */ $tags) => Arr::map($tags, static fn($tag) => $tag->unwrap())); +$tags = get_terms(['taxonomy' => 'product_tag', 'hide_empty' => true]); + +echo '
';
+print_r($product_tags);
+print_r($tags);
+echo '
'; + +exit(); add_action('wp_enqueue_scripts', function (): void { Resource::enqueue_script_module_file( @@ -67,7 +84,4 @@ add_action('wp_enqueue_scripts', function (): void { }); // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/src/StarterSite.php b/web/app/themes/haiku-atelier-2024/src/StarterSite.php index 5f3fcca0..846929a6 100755 --- a/web/app/themes/haiku-atelier-2024/src/StarterSite.php +++ b/web/app/themes/haiku-atelier-2024/src/StarterSite.php @@ -94,22 +94,11 @@ final class StarterSite extends Site { // Récupère la Page courante $url_courante = URLHelper::get_current_url(); $context['page_courante'] = $url_courante; - $context['est_page_tous_produits'] = preg_match( - pattern: '/(\bshop\b)/', - subject: $url_courante, - ); - $context['est_page_boutique'] = preg_match( - pattern: '/(\bshop\b)/', - subject: $url_courante, - ) - || preg_match( - pattern: '/(\bproduct\b)/', - subject: $url_courante, - ) - || preg_match( - pattern: '/(\bproduct-category\b)/', - subject: $url_courante, - ); + $context['est_page_tous_produits'] = preg_match(pattern: '/(\bshop\b)/', subject: $url_courante); + $context['est_page_boutique'] = + preg_match(pattern: '/(\bshop\b)/', subject: $url_courante) + || preg_match(pattern: '/(\bproduct\b)/', subject: $url_courante) + || preg_match(pattern: '/(\bproduct-category\b)/', subject: $url_courante); // Politique de confidentialité $politique_confidentialite_lien = pipe(get_privacy_policy_url(), esc_url(...)); @@ -130,10 +119,7 @@ final class StarterSite extends Site { ]; $entrees_menu_categories = pipe( get_categories(['hide_empty' => false, 'orderby' => 'menu_order', 'taxonomy' => 'product_cat']), - static fn($categories): array => array_map( - callback: $cree_entree_menu, - array: $categories, - ), + static fn($categories): array => array_map(callback: $cree_entree_menu, array: $categories), ); $context['categories_produits'] = $entrees_menu_categories; diff --git a/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php b/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php index e65da73d..8fc21406 100644 --- a/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php @@ -28,10 +28,6 @@ final readonly class Attribute { /** @var list */ $options = Arr::map($terms, AttributeOption::new(...)); - return new self( - name: $name, - slug: $slug, - options: $options, - ); + return new self(name: $name, slug: $slug, options: $options); } } diff --git a/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php b/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php index e8d00a71..4359b920 100644 --- a/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php @@ -18,10 +18,6 @@ final readonly class AttributeOption { $name = $term->name; $slug = $term->slug; - return new self( - id: $id, - name: $name, - slug: $slug, - ); + return new self(id: $id, name: $name, slug: $slug); } } diff --git a/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php b/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php index 17f1489f..5e5ee252 100644 --- a/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php @@ -5,15 +5,17 @@ declare(strict_types=1); namespace HaikuAtelier\Data; use HaikuAtelier\WP\HaikuProduct; +use HaikuAtelier\WP\Resource; use HaikuAtelier\WP\Term; use Illuminate\Support\Arr; use Psl\Option; +use stdClass; use WC_Product; use WP_Term; -use function HaikuAtelier\genere_balise_img_multiformats; use function head; use function Psl\Option\from_nullable; +use function wc_get_products; use function wpautop; final readonly class Product { @@ -41,16 +43,7 @@ final readonly class Product { public string $url, ) {} - /** - * @return list - */ - public static function get_attributes_for_product(WC_Product $product): array { - /** @var list */ - return $product->get_attributes() - |> (static fn(array $attributes): array => Arr::map($attributes, Attribute::new(...))); - } - - public static function new(WC_Product $product): self { + public static function from_wc_product(WC_Product $product): self { $attributes = self::get_attributes_for_product($product); /** @var lowercase-string */ $category = $product->get_id() |> wc_get_product_category_list(...) |> strtolower(...); @@ -73,8 +66,8 @@ final readonly class Product { $left_column_photos = HaikuProduct::get_left_column_photos($id); /** @var list */ $right_column_photos = HaikuProduct::get_right_column_photos($id); - $default_photo = $left_column_photos[0] ?? genere_balise_img_multiformats('-1'); - $hover_photo = $right_column_photos[0] ?? genere_balise_img_multiformats('-1', true); + $default_photo = $left_column_photos[0] ?? Resource::output_multi_formats_img_tag('-1'); + $hover_photo = $right_column_photos[0] ?? Resource::output_multi_formats_img_tag('-1', true); $slug = $product->get_slug(); $stock = $product->get_stock_quantity() ?? 1; /** @var array */ @@ -104,4 +97,38 @@ final readonly class Product { url: $url, ); } + + /** + * @return list + */ + public static function get_attributes_for_product(WC_Product $product): array { + /** @var list */ + return $product->get_attributes() + |> (static fn(array $attributes): array => Arr::map($attributes, Attribute::new(...))); + } + + /** + * Récupère les informations utilisées pour la grille des Produits similaires (de la même + * collection) et les retourne sous forme de tableau associatif. + * + * Pour faciliter l'usage avec `array_map`, utilise une fonction avec curryfication. + */ + public static function get_same_collection_products(string $slug_collection): callable { + return static fn(int $id_produit): array|stdClass => wc_get_products([ + 'exclude' => [$id_produit], + 'limit' => 4, + 'order' => 'DESC', + 'orderby' => 'date', + 'status' => 'publish', + 'tax_query' => [['taxonomy' => 'collection', 'field' => 'slug', 'terms' => $slug_collection]], + ]); + } + + public static function recupere_et_formate_attributs_produit(mixed $attributs_produit): mixed { + return [ + 'taille' => ['nom' => 'Size', 'valeur' => $attributs_produit['pa_size'] ?? false], + 'pierre' => ['nom' => 'Stone', 'valeur' => $attributs_produit['pa_stone'] ?? false], + 'cote' => ['nom' => 'Side', 'valeur' => $attributs_produit['pa_side'] ?? false], + ]; + } } diff --git a/web/app/themes/haiku-atelier-2024/src/inc/FonctionnalitesWooCommerce.php b/web/app/themes/haiku-atelier-2024/src/inc/FonctionnalitesWooCommerce.php index 26089f92..479b02e7 100755 --- a/web/app/themes/haiku-atelier-2024/src/inc/FonctionnalitesWooCommerce.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/FonctionnalitesWooCommerce.php @@ -6,9 +6,9 @@ declare(strict_types=1); -use function Crell\fp\pipe; +use HaikuAtelier\WP\Resource; -require_once 'TraitementInformations.php'; +use function Crell\fp\pipe; // Images du Produit @@ -145,13 +145,10 @@ function genere_balises_img_dans_produit_dans_reponse_rest( array: $metadata, callback: static fn($entree): bool => '_photos_colonne_gauche|||0|value' === $entree->key, ), - static fn($metadata): array => array_map( - array: $metadata, - callback: static fn($entree): string => genere_balise_img_multiformats( - id: $entree?->value, - lazy: true, - ), - ), + static fn($metadata): array => array_map(array: $metadata, callback: static fn($entree): string => Resource::output_multi_formats_img_tag( + id: $entree?->value, + lazy: true, + )), static fn($image) => array_values(array: $image)[0], ); @@ -162,13 +159,10 @@ function genere_balises_img_dans_produit_dans_reponse_rest( array: $metadata, callback: static fn($entree): bool => '_photos_colonne_droite|||0|value' === $entree->key, ), - static fn($metadata): array => array_map( - array: $metadata, - callback: static fn($entree): string => genere_balise_img_multiformats( - id: $entree?->value, - lazy: true, - ), - ), + static fn($metadata): array => array_map(array: $metadata, callback: static fn($entree): string => Resource::output_multi_formats_img_tag( + id: $entree?->value, + lazy: true, + )), static fn($image) => array_values(array: $image)[0], ); diff --git a/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php b/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php deleted file mode 100755 index ae8c869a..00000000 --- a/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php +++ /dev/null @@ -1,116 +0,0 @@ - array_filter( - array: $tableau, - callback: static fn($chemin_format): bool => false !== $chemin_format, - ), - static fn($tableau): array => array_map( - array: $tableau, - callback: static fn($chemin_format): array => [ - 'format' => pathinfo((string) $chemin_format)['extension'], - 'taille' => filesize($chemin_format), - 'url' => - pathinfo($url)['dirname'] - . '/' - . pathinfo($url)['filename'] - . '.' - . pathinfo((string) $chemin_format)['extension'], - ], - ), - ); - usort( - array: $formats, - callback: static fn($a, $b): int => $a['taille'] <=> $b['taille'], - ); - - // Construis les balises avec les formats valides - $sources = ''; - foreach ($formats as $format) { - $height = $dimensions[0]; - $width = $dimensions[1]; - $sources .= "\n"; - } - - $loading = $lazy ? 'lazy' : 'eager'; - - return << - EOD; -} - -// Page Produit - -/** - * Récupère les informations utilisées pour la grille des Produits similaires (de la même - * collection) et les retourne sous forme de tableau associatif. - * - * Pour faciliter l'usage avec `array_map`, utilise une fonction avec curryfication. - */ -function recupere_produits_meme_collection(string $slug_collection): callable { - return static fn(int $id_produit): array|stdClass => wc_get_products([ - 'exclude' => [$id_produit], - 'limit' => 4, - 'order' => 'DESC', - 'orderby' => 'date', - 'status' => 'publish', - 'tax_query' => [['taxonomy' => 'collection', 'field' => 'slug', 'terms' => $slug_collection]], - ]); -} - -// Page Panier - -function recupere_et_formate_attributs_produit(mixed $attributs_produit): mixed { - return [ - 'taille' => ['nom' => 'Size', 'valeur' => $attributs_produit['pa_size'] ?? false], - 'pierre' => ['nom' => 'Stone', 'valeur' => $attributs_produit['pa_stone'] ?? false], - 'cote' => ['nom' => 'Side', 'valeur' => $attributs_produit['pa_side'] ?? false], - ]; -} diff --git a/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php b/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php index cd195b72..ce08e524 100644 --- a/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php @@ -7,7 +7,6 @@ namespace HaikuAtelier\WP; use Illuminate\Support\Arr; use function carbon_get_post_meta; -use function HaikuAtelier\genere_balise_img_multiformats; use function is_array; use function is_string; @@ -19,7 +18,7 @@ final readonly class HaikuProduct { /** @var list */ return Post::get_post_meta_array($post_id, '_photos_colonne_gauche|||0|value')->unwrapOr([]) |> (static fn(array $meta) => Arr::where($meta, static fn($meta): bool => is_string($meta))) - |> (static fn(array $array) => Arr::map($array, genere_balise_img_multiformats(...))); + |> (static fn(array $array) => Arr::map($array, Resource::output_multi_formats_img_tag(...))); } /** @@ -31,7 +30,7 @@ final readonly class HaikuProduct { if (is_array($meta)) { /** @var list */ return Arr::where($meta, static fn($meta): bool => is_string($meta)) - |> (static fn(array $array) => Arr::map($array, genere_balise_img_multiformats(...))); + |> (static fn(array $array) => Arr::map($array, Resource::output_multi_formats_img_tag(...))); } return []; diff --git a/web/app/themes/haiku-atelier-2024/src/inc/WP/Resource.php b/web/app/themes/haiku-atelier-2024/src/inc/WP/Resource.php index b0151dfc..d4a67475 100644 --- a/web/app/themes/haiku-atelier-2024/src/inc/WP/Resource.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/WP/Resource.php @@ -6,6 +6,7 @@ namespace HaikuAtelier\WP; use Exception; +use function Crell\fp\pipe; use function filemtime; use function get_template_directory; use function get_template_directory_uri; @@ -29,12 +30,7 @@ final readonly class Resource { $version = (string) $file_mtime; - wp_enqueue_script_module( - id: $id, - src: $file_uri, - deps: [], - version: $version, - ); + wp_enqueue_script_module(id: $id, src: $file_uri, deps: [], version: $version); } /** @@ -52,12 +48,74 @@ final readonly class Resource { $ver = (string) $file_mtime; - wp_enqueue_style( - handle: $handle, - src: $file_uri, - deps: [], - ver: $ver, - media: 'all', + wp_enqueue_style(handle: $handle, src: $file_uri, deps: [], ver: $ver, media: 'all'); + } + + /** + * TODO. + * + * @param string $id TODO + * @param bool $lazy TODO + * + * @return string TODO + */ + public static function output_multi_formats_img_tag(string $id, bool $lazy = false): string { + $int_id = (int) $id; + + if (-1 === $int_id) { + return ''; + } + + $url = wp_get_attachment_image_url($int_id, 'full'); + $chemin = realpath(get_attached_file($int_id)) ?: realpath(get_attached_file($int_id)); + $alt = get_post_meta($int_id, '_wp_attachment_image_alt', true); + $dimensions = $chemin ? getimagesize($chemin) : ['', '']; + + $avif = $chemin ? realpath(pathinfo($chemin)['dirname'] . '/' . pathinfo($chemin)['filename'] . '.avif') : false; + $jxl = $chemin ? realpath(pathinfo($chemin)['dirname'] . '/' . pathinfo($chemin)['filename'] . '.jxl') : false; + + // Génère un tableau avec les différents formats valides + $formats = pipe( + [$avif, $jxl], + static fn($tableau): array => array_filter( + array: $tableau, + callback: static fn($chemin_format): bool => false !== $chemin_format, + ), + static fn($tableau): array => array_map(array: $tableau, callback: static fn($chemin_format): array => [ + 'format' => pathinfo((string) $chemin_format)['extension'], + 'taille' => filesize($chemin_format), + 'url' => + pathinfo($url)['dirname'] + . '/' + . pathinfo($url)['filename'] + . '.' + . pathinfo((string) $chemin_format)['extension'], + ]), ); + usort(array: $formats, callback: static fn($a, $b): int => $a['taille'] <=> $b['taille']); + + // Construis les balises avec les formats valides + $sources = ''; + foreach ($formats as $format) { + $height = $dimensions[0]; + $width = $dimensions[1]; + $sources .= "\n"; + } + + $loading = $lazy ? 'lazy' : 'eager'; + + return << + EOD; } } diff --git a/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php b/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php index 690c4dab..e96155df 100644 --- a/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php @@ -12,6 +12,19 @@ use function Psl\Option\none; use function Psl\Option\some; final readonly class Term { + /** + * @return Option\Option + */ + public static function get_term_by_id(int $id, string $taxonomy): Option\Option { + $term_data = get_term(term: $id, taxonomy: $taxonomy); + + if ($term_data instanceof WP_Term) { + return some($term_data); + } else { + return none(); + } + } + /** * @return Option\Option> */ diff --git a/web/app/themes/haiku-atelier-2024/src/scripts-effect/lib/dom.ts b/web/app/themes/haiku-atelier-2024/src/scripts-effect/lib/dom.ts index e618c8c7..4dc5db46 100644 --- a/web/app/themes/haiku-atelier-2024/src/scripts-effect/lib/dom.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts-effect/lib/dom.ts @@ -1,27 +1,34 @@ -import { Option, pipe } from "effect"; -import { Array as FxArray } from "effect"; -import { NonEmptyReadonlyArray } from "effect/Array"; -import { getOptionOrThrowWithError } from "./utils"; +import { Array as FxArray, Option, pipe } from "effect"; +import type { NonEmptyReadonlyArray } from "effect/Array"; + +import { getOptionOrThrowWithError } from "./utils.ts"; /** Type union des parents possibles pour un `querySelector`. */ -export type ParentElement = Document | Element; +type ParentElement = Document | Element; -export const getFirstSelectorFromParent = +const getFirstSelectorFromParent = (parent: ParentElement) => (selector: string): Option.Option> => Option.fromNullishOr(parent.querySelector(selector)); -export const getFirstSelectorFromDocument = ( - selector: string, -): Option.Option> => getFirstSelectorFromParent(document)(selector); +const getFirstSelectorFromParentOrThrow = + (parent: ParentElement) => + (selector: string): NonNullable => + pipe( + getFirstSelectorFromParent(parent)(selector), + getOptionOrThrowWithError(`Il n'y a pas d'Élément dans le parent avec le sélecteur suivant : ${selector}.`), + ); -export const getFirstSelectorFromDocumentOrThrow = (selector: string): NonNullable => +const getFirstSelectorFromDocument = (selector: string): Option.Option> => + getFirstSelectorFromParent(document)(selector); + +const getFirstSelectorFromDocumentOrThrow = (selector: string): NonNullable => pipe( getFirstSelectorFromDocument(selector), getOptionOrThrowWithError(`Il n'y a pas d'Élément dans le Document avec le sélecteur suivant : ${selector}.`), ); -export const getAllSelectorFromParent = +const getAllSelectorFromParent = (parent: ParentElement) => (selector: string): Option.Option> => pipe( @@ -31,14 +38,23 @@ export const getAllSelectorFromParent = (xs: Array) => Option.liftPredicate(FxArray.isReadonlyArrayNonEmpty)(xs), ); -export const getAllSelectorFromDocument = ( +const getAllSelectorFromDocument = ( selector: string, ): Option.Option> => getAllSelectorFromParent(document)(selector); -export const getAllSelectorFromDocumentOrThrow = ( - selector: string, -): NonEmptyReadonlyArray => +const getAllSelectorFromDocumentOrThrow = (selector: string): NonEmptyReadonlyArray => pipe( getAllSelectorFromDocument(selector), getOptionOrThrowWithError(`Il n'y a pas d'Éléments dans le Document avec le sélecteur suivant : ${selector}.`), ); + +export { + getAllSelectorFromDocument, + getAllSelectorFromDocumentOrThrow, + getAllSelectorFromParent, + getFirstSelectorFromDocument, + getFirstSelectorFromDocumentOrThrow, + getFirstSelectorFromParent, + getFirstSelectorFromParentOrThrow, + type ParentElement, +}; diff --git a/web/app/themes/haiku-atelier-2024/src/scripts/lib/schemas/api/cart-add-item.ts b/web/app/themes/haiku-atelier-2024/src/scripts/lib/schemas/api/cart-add-item.ts index 7ff7b45d..840024ac 100755 --- a/web/app/themes/haiku-atelier-2024/src/scripts/lib/schemas/api/cart-add-item.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts/lib/schemas/api/cart-add-item.ts @@ -13,5 +13,5 @@ export const WCStoreCartAddItemArgsSchema = v.object({ /** Quantity of this item to add to the basket. */ quantity: v.optional(v.number()), /** Chosen attributes (for variations). */ - variation: v.optional(v.array(WCStoreCartAddItemArgsItemsSchema)), + variation: v.pipe(v.optional(v.array(WCStoreCartAddItemArgsItemsSchema)), v.readonly()), }); diff --git a/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-adresses.ts b/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-adresses.ts index 0773bca6..a96b04a6 100755 --- a/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-adresses.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-adresses.ts @@ -7,42 +7,44 @@ import { match, P } from "ts-pattern"; import { ValiError } from "valibot"; import type { AnySchema } from "valibot"; -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 { WCV3Order, WCV3OrdersArgs } from "../lib/types/api/v3/orders"; -import type { GenericPageState } from "../lib/types/pages"; -import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau"; +import type { WCStoreBillingAddress, WCStoreShippingAddress } from "../lib/types/api/adresses.ts"; +import type { WCStoreCart, WCStoreShippingRate, WCStoreShippingRateShippingRate } from "../lib/types/api/cart.ts"; +import type { WCStoreCartUpdateCustomerArgs } from "../lib/types/api/cart-update-customer.ts"; +import type { WCV3Order, WCV3OrdersArgs } from "../lib/types/api/v3/orders.ts"; +import type { GenericPageState } from "../lib/types/pages.ts"; +import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau.ts"; -import { ROUTE_API_MAJ_CLIENT, ROUTE_API_NOUVELLE_COMMANDES } from "../constantes/api"; -import { ATTRIBUT_CHARGEMENT, ATTRIBUT_LIVRAISON_VALIDEE } from "../constantes/dom"; -import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages"; +import { ROUTE_API_MAJ_CLIENT, ROUTE_API_NOUVELLE_COMMANDES } from "../constantes/api.ts"; +import { ATTRIBUT_CHARGEMENT, ATTRIBUT_LIVRAISON_VALIDEE } from "../constantes/dom.ts"; +import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages.ts"; import { ERREUR_ADRESSE_GENERIQUE, ERREUR_ADRESSE_MAUVAIS_CODE_POSTAL, ERREUR_GENERIQUE_CREATION_COMMANDE, ERREUR_GENERIQUE_RESEAU, ERREUR_GENERIQUE_SOUMISSION_ADRESSES, -} from "../constantes/messages-utilisateur"; -import { estErreurFetch, estErreurHttp, setButtonLoadingState } from "../lib/dom"; -import { reporteEtJournaliseErreur } from "../lib/erreurs"; -import { ErreurAdresseInvalide } from "../lib/erreurs/adresses"; +} from "../constantes/messages-utilisateur.ts"; +import { estErreurFetch, estErreurHttp, setButtonLoadingState } from "../lib/dom.ts"; +import { reporteEtJournaliseErreur } from "../lib/erreurs.ts"; +import { ErreurAdresseInvalide } from "../lib/erreurs/adresses.ts"; import { ADRESSES_MAJ_EVENT, createUpdatedShippingRatesEvent, createUpdatedTotalsEvent, -} from "../lib/evenements/panier"; -import { emetUniqueMessageBroadcastChannel } from "../lib/messages"; -import { diviseParCent } from "../lib/nombres"; -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 { estWCAddressError } from "../lib/schemas/api/erreurs"; -import { WCV3OrdersArgsSchema, WCV3OrderSchema } from "../lib/schemas/api/v3/orders"; -import { safeSchemaParse } from "../lib/validation"; -import { E } from "./scripts-page-panier-elements"; -import { getShippingRatesLS } from "./scripts-page-panier-local-storage"; +} from "../lib/evenements/panier.ts"; +import { emetUniqueMessageBroadcastChannel } from "../lib/messages.ts"; +import { diviseParCent } from "../lib/nombres.ts"; +import { newPartialResponse, prefilledPostBackend, safeFetch, traiteErreursBackendWooCommerce } from "../lib/reseau.ts"; +import { find, first } from "../lib/safe-arrays.ts"; +import { WCStoreCartSchema } from "../lib/schemas/api/cart.ts"; +import { WCStoreCartUpdateCustomerArgsSchema } from "../lib/schemas/api/cart-update-customer.ts"; +import { estWCAddressError } from "../lib/schemas/api/erreurs.ts"; +import { WCV3OrdersArgsSchema, WCV3OrderSchema } from "../lib/schemas/api/v3/orders.ts"; +import { safeSchemaParse } from "../lib/validation.ts"; +import { E } from "./scripts-page-panier-elements.ts"; +import { getShippingRatesLS } from "./scripts-page-panier-local-storage.ts"; +import { ReadonlyRecord } from "effect/Record"; +import { Console, Effect, Stream } from "effect"; type Addresses = { billing_address: WCStoreBillingAddress; @@ -50,23 +52,44 @@ type Addresses = { }; // @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: GenericPageState = _etats; const postBackend = prefilledPostBackend(ETATS_PAGE.nonce, ETATS_PAGE.authString); + /** * Initialise les Émetteurs d'Événements sur divers parties du Panier. * - * @returns void + * @returns Un `Effect` ne retournant rien et ne pouvant échouer. */ -export const initCartFormEventEmitters = (): void => { - E.FORMULAIRE_PANIER.addEventListener("change", (): void => { - Maybe.fromFalsy(E.FORMULAIRE_PANIER.checkValidity()).ifJust((): boolean => - window.dispatchEvent(ADRESSES_MAJ_EVENT), - ); - }); -}; +export const initCartFormEventEmitters = Effect.fn("initCartFormEventEmitters")(function* () { + return yield* pipe( + Stream.fromEventListener(E.FORMULAIRE_PANIER, "change"), + Stream.tap((event: Event) => { + // La cible ne peut qu'être un Formulaire. + const target = event.target as HTMLFormElement; -export const getAddressesFromForm = (formFields: Record, areAddressesMerged: boolean): Addresses => ({ + if (target.checkValidity()) { + window.dispatchEvent(ADRESSES_MAJ_EVENT); + } + + return Effect.void; + }), + Stream.runDrain, + ); +}); + +/** + * Récupère les valeurs des adresses renseignées dans les Formulaires correspondants. + * + * @param formFields Les champs du Formulaire sous forme d'Objet. + * @param areAddressesMerged Est-ce que l'utilisateur utilise la même adresse pour livraison et facturation. + * @returns Les Adresses proprement renseignées. + * + * TODO: Utiliser un Schéma pour parser. + */ +export const getAddressesFromForm = ( + formFields: ReadonlyRecord, + areAddressesMerged: boolean, +): Addresses => ({ billing_address: { address_1: formFields["facturation-adresse"] ?? formFields["livraison-adresse"] ?? "", address_2: "", diff --git a/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-panneau-produits.ts b/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-panneau-produits.ts index ca604516..15c6168f 100755 --- a/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-panneau-produits.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts/page-panier/scripts-page-panier-panneau-produits.ts @@ -7,13 +7,13 @@ import { match, P } from "ts-pattern"; import { ValiError } from "valibot"; import type { AnySchema } 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 { GenericPageState } from "../lib/types/pages"; -import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau"; +import type { WCStoreCart } from "../lib/types/api/cart.ts"; +import type { WCStoreCartRemoveItemArgs } from "../lib/types/api/cart-remove-item.ts"; +import type { WCStoreCartUpdateItemArgs } from "../lib/types/api/cart-update-item.ts"; +import type { GenericPageState } from "../lib/types/pages.ts"; +import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau.ts"; -import { ROUTE_API_MAJ_ARTICLE_PANIER, ROUTE_API_RETIRE_ARTICLE_PANIER } from "../constantes/api"; +import { ROUTE_API_MAJ_ARTICLE_PANIER, ROUTE_API_RETIRE_ARTICLE_PANIER } from "../constantes/api.ts"; import { ATTRIBUT_CLE_PANIER, ATTRIBUT_DESACTIVE, @@ -21,25 +21,24 @@ import { DOM_BOUTON_SOUSTRACTION_QUANTITE, DOM_BOUTON_SUPPRESSION_PANIER, DOM_CHAMP_QUANTITE_LIGNE_PANIER, -} from "../constantes/dom"; -import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages"; -import { mustGetEleInParent } from "../lib/dom"; -import { BadRequestError, reporteErreur, ServerError } from "../lib/erreurs"; +} from "../constantes/dom.ts"; +import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages.ts"; +import { BadRequestError, reporteErreur, ServerError } from "../lib/erreurs.ts"; import { emetMessageMajBoutonPanier, emetMessageMajContenuPanier, emetUniqueMessageBroadcastChannel, -} from "../lib/messages"; -import { diviseParCent } from "../lib/nombres"; -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 { safeSchemaParse } from "../lib/validation"; -import { E } from "./scripts-page-panier-elements"; +} from "../lib/messages.ts"; +import { diviseParCent } from "../lib/nombres.ts"; +import { newPartialResponse, postBackend, safeFetch, traiteErreursBackendWooCommerce } from "../lib/reseau.ts"; +import { WCStoreCartSchema } from "../lib/schemas/api/cart.ts"; +import { WCStoreCartRemoveItemArgsSchema } from "../lib/schemas/api/cart-remove-item.ts"; +import { WCStoreCartUpdateItemArgsSchema } from "../lib/schemas/api/cart-update-item.ts"; +import { safeSchemaParse } from "../lib/validation.ts"; +import { E } from "./scripts-page-panier-elements.ts"; +import { getFirstSelectorFromParentOrThrow } from "../../scripts-effect/lib/dom.ts"; // @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 PAGE_STATE: GenericPageState = _etats; type CartEntryInteractiveElements = { @@ -49,31 +48,35 @@ type CartEntryInteractiveElements = { substractionButton: HTMLButtonElement; }; -const getCartEntryInteractiveEles = (entree: HTMLElement): CartEntryInteractiveElements => { - const mustGetEle = mustGetEleInParent(entree); +const getCartEntryInteractiveEles = (entry: HTMLElement): CartEntryInteractiveElements => { + const entrySelector = getFirstSelectorFromParentOrThrow(entry); return { - additionButton: mustGetEle(DOM_BOUTON_ADDITION_QUANTITE), - deletionButton: mustGetEle(DOM_BOUTON_SUPPRESSION_PANIER), - quantityInput: mustGetEle(DOM_CHAMP_QUANTITE_LIGNE_PANIER), - substractionButton: mustGetEle(DOM_BOUTON_SOUSTRACTION_QUANTITE), + additionButton: entrySelector(DOM_BOUTON_ADDITION_QUANTITE), + deletionButton: entrySelector(DOM_BOUTON_SUPPRESSION_PANIER), + quantityInput: entrySelector(DOM_CHAMP_QUANTITE_LIGNE_PANIER), + substractionButton: entrySelector(DOM_BOUTON_SOUSTRACTION_QUANTITE), }; }; /** -* Met à jour l'état d'activation des Boutons d'action sur chaque Entrée du Panier. +* Met à jour l'état d'activation des Boutons d'action pour toutes les Entrées du Panier. * @param activated Le nouvel état d'activation (activé/désactivé). * @returns Rien. */ -export const toggleCartEntryButtons = +const toggleCartEntryButtons = (activated: boolean) => - (cartEntries: ReadonlyArray): void => + (cartEntries: ReadonlyArray): 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); + const entryQuantity = Number(e.quantityInput.value); + // Empêche la réducation de la quantité si on se trouve au minimum. + if (entryQuantity === 1) { + e.substractionButton.setAttribute(ATTRIBUT_DESACTIVE, ""); + } else { + e.substractionButton.removeAttribute(ATTRIBUT_DESACTIVE); + } e.additionButton.removeAttribute(ATTRIBUT_DESACTIVE); e.deletionButton.removeAttribute(ATTRIBUT_DESACTIVE); e.deletionButton.textContent = "Remove"; @@ -85,22 +88,29 @@ export const toggleCartEntryButtons = e.deletionButton.textContent = "Loading"; } }); + }; -export const initialiseActionsEntreesPanier = (): void => { +const initActionsOnCartEntries = (): void => { // Initialise des actions uniquement si des Entrées dans le Panier existent - E.ENTREES_PANIER.ifRight((cartEntries: Array) => + E.ENTREES_PANIER.ifRight((cartEntries: Array) => { 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()) + .ifNothing(() => { + entry.remove(); + }) .orDefault("CLE_PANIER_INEXISTANTE"); const entryButtons: CartEntryInteractiveElements = getCartEntryInteractiveEles(entry); 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)) + [ + // Cas impossible + "with" + ](P.nullish, () => { + console.error(event.target); + }) // Clic sur le Bouton d'addition .when( (target: EventTarget) => (target as HTMLElement).matches(DOM_BOUTON_ADDITION_QUANTITE), @@ -116,7 +126,9 @@ export const initialiseActionsEntreesPanier = (): void => { safeSchemaParse({ key: entryKey, quantity: q + 1 }, WCStoreCartUpdateItemArgsSchema), ), ) - .ifRight(() => pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false))) + .ifRight(() => { + pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false)); + }) .chain((args: WCStoreCartUpdateItemArgs) => safeFetch( postBackend({ @@ -129,7 +141,7 @@ export const initialiseActionsEntreesPanier = (): void => { .chain((r: Response) => EitherAsync(async ({ throwE }) => match(await newPartialResponse(r)) - .with({ status: 200 }, (r) => r.body) + ["with"]({ status: 200 }, (r) => r.body) .otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs))), ), ) @@ -149,24 +161,24 @@ export const initialiseActionsEntreesPanier = (): void => { }) .ifLeft((err: FetchErrors | HttpCodeErrors | ValiError): void => { match(err) - .with(P.instanceOf(ValiError), (e) => { + ["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) => { + ["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) => { + ["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(() => { + ["finally"](() => { pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(true)); }) .run(); @@ -188,9 +200,9 @@ export const initialiseActionsEntreesPanier = (): void => { safeSchemaParse({ key: entryKey, quantity: valeur - 1 }, WCStoreCartUpdateItemArgsSchema), ) // 2. Exécute un Effet pour empêcher les requêtes concurrentes - .ifRight(() => - pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false)), - ) + .ifRight(() => { + pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false)); + }) // 3. Exécute la requête via fetch sous forme d'EitherAsync .chain((args: WCStoreCartUpdateItemArgs) => safeFetch( @@ -206,9 +218,9 @@ export const initialiseActionsEntreesPanier = (): void => { EitherAsync(async ({ throwE }) => // Simplifie les données à matcher 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) + ["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)}`))), ), ) @@ -231,24 +243,24 @@ export const initialiseActionsEntreesPanier = (): void => { // 7. Traite les Erreurs et affiche un message à l'Utilisateur .ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError): void => { match(erreur) - .with(P.instanceOf(ValiError), (e) => { + ["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) => { + ["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) => { + ["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(() => { + ["finally"](() => { // Réactive les Boutons pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(true)); }) @@ -269,9 +281,9 @@ export const initialiseActionsEntreesPanier = (): void => { // 1. Valide les Arguments de la Requête .liftEither(safeSchemaParse({ key: entryKey }, WCStoreCartRemoveItemArgsSchema)) // 2. Exécute un Effet pour empêcher les requêtes concurrentes - .ifRight(() => - pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false)), - ) + .ifRight(() => { + pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false)); + }) // 3. Exécute la requête via fetch sous forme d'EitherAsync .chain((args: WCStoreCartRemoveItemArgs) => safeFetch( @@ -287,9 +299,9 @@ export const initialiseActionsEntreesPanier = (): void => { EitherAsync(async ({ throwE }) => // Simplifie les données à matcher 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) + ["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)}`))), ), ) @@ -315,24 +327,24 @@ export const initialiseActionsEntreesPanier = (): void => { // 7. Traite les Erreurs et affiche un message à l'Utilisateur .ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError): void => { match(erreur) - .with(P.instanceOf(ValiError), (e) => { + ["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) => { + ["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) => { + ["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(() => { + ["finally"](() => { // Réactive les Boutons pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(true)); }) @@ -342,6 +354,8 @@ export const initialiseActionsEntreesPanier = (): void => { ) .otherwise((_) => {}); }); - }), - ); + }); + }); }; + +export { toggleCartEntryButtons, initActionsOnCartEntries as initialiseActionsEntreesPanier }; diff --git a/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-panier.ts b/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-panier.ts index 6198bb0f..07460649 100755 --- a/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-panier.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-panier.ts @@ -36,6 +36,7 @@ import { E } from "./page-panier/scripts-page-panier-elements.ts"; import { souscrisEvenementsPanier } from "./page-panier/scripts-page-panier-evenement.ts"; import { initShippingRatesChoicesActions } from "./page-panier/scripts-page-panier-methodes-livraison.ts"; import { initialiseActionsEntreesPanier } from "./page-panier/scripts-page-panier-panneau-produits.ts"; +import { Effect } from "effect"; type ElementsEntreePanier = { boutonAddition: HTMLButtonElement; @@ -176,7 +177,7 @@ const initialiseMajFormulairesPanier = (): void => { }; document.addEventListener("DOMContentLoaded", (): void => { - initCartFormEventEmitters(); + Effect.runFork(initCartFormEventEmitters()); souscrisEvenementsPanier(); initialiseActionsEntreesPanier(); initShippingRatesChoicesActions(); diff --git a/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts b/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts index c4176417..1cfb03ce 100755 --- a/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts @@ -10,7 +10,7 @@ import { match, P } from "ts-pattern"; import { ValiError } from "valibot"; import type { AnySchema } from "valibot"; -import type { WCStoreCart } from "./lib/types/api/cart"; +import type { WCStoreCart } from "./lib/types/api/cart.ts"; import type { WCStoreCartAddItemArgs, WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.ts"; import type { FetchErrors } from "./lib/types/reseau.ts"; @@ -39,6 +39,7 @@ import { WCStoreCartSchema } from "./lib/schemas/api/cart.ts"; import { safeSchemaParse } from "./lib/validation"; type EnsembleLienContenu = [HTMLAnchorElement, HTMLElement]; + /** États utiles pour les scripts de la page. */ type EtatsPage = { /** L'ID en base de données du Produit. */ @@ -48,7 +49,6 @@ type EtatsPage = { }; // @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: EtatsPage = _etats; /** @@ -149,18 +149,18 @@ const getAttributesFromDom = (): ReadonlyArray => { return attributes; }; -function areArraysEqual(array1: Array, array2: Array): boolean { +const areArraysEqual = (array1: Array, array2: Array): boolean => { if (array1 !== array2) { const a1 = JSON.stringify(array1.toSorted()); const a2 = JSON.stringify(array2.toSorted()); return a1 === a2; } return true; -} +}; const updatePriceOnAttributeChange = (): void => { E.VARIATION_CHOICE_FORM.addEventListener("change", (): void => { - if (!E.VARIATION_CHOICE_FORM.checkValidity()) { + if (E.VARIATION_CHOICE_FORM.checkValidity() === false) { return; } @@ -183,9 +183,8 @@ const ajouteProduitAuPanier = (event: MouseEvent): void => { 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 .orDefault(ETATS_PAGE.idProduit), - // Id: ETATS_PAGE.idProduit, quantity: 1, - // Variation: getAttributeValuesFromDom(), + variation: getAttributesFromDom(), }; // Réalise la Requête et traite sa Réponse diff --git a/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php b/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php index d402c0e5..2d0fa317 100755 --- a/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php +++ b/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php @@ -39,7 +39,10 @@ $products = wc_get_products([ assert(is_array($products), 'Les Produits de la Catégorie doivent être un tableau.'); return $products; } - |> (static fn(/** @var list */ array $products): array => Arr::map($products, Product::new(...))); + |> (static fn(/** @var list */ array $products): array => Arr::map( + $products, + Product::from_wc_product(...), + )); $context['products'] = $products; $context['category_id'] = $current_term->term_id; @@ -60,7 +63,4 @@ add_action('wp_enqueue_scripts', function (): void { }); // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-completed-order.php b/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-completed-order.php index 8172afdc..61fbe911 100755 --- a/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-completed-order.php +++ b/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-completed-order.php @@ -41,7 +41,4 @@ $email = [ $context['commande'] = $email; // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-invoice.php b/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-invoice.php index 5b30f537..cbdcd419 100755 --- a/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-invoice.php +++ b/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-invoice.php @@ -76,7 +76,4 @@ $email['adresses']['facturation']['country'] = WC()->countries->countries[$comma $context['commande'] = $email; // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context); diff --git a/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-processing-order.php b/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-processing-order.php index 5fbaa72a..830883aa 100755 --- a/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-processing-order.php +++ b/web/app/themes/haiku-atelier-2024/woocommerce/emails/customer-processing-order.php @@ -73,7 +73,4 @@ $email['adresses']['facturation']['country'] = WC()->countries->countries[$comma $context['commande'] = $email; // Rendu -Timber::render( - filenames: $templates, - data: $context, -); +Timber::render(filenames: $templates, data: $context);