2026-04-05
This commit is contained in:
parent
5f835ca4e6
commit
2971f5516d
62 changed files with 439 additions and 497 deletions
|
|
@ -8,5 +8,17 @@
|
||||||
"!vtsls",
|
"!vtsls",
|
||||||
"..."
|
"..."
|
||||||
],
|
],
|
||||||
"languages": {}
|
"lsp": {
|
||||||
|
"oxlint": {
|
||||||
|
"initialization_options": {
|
||||||
|
"settings": {
|
||||||
|
"configPath": "cfg/oxlint.config.ts",
|
||||||
|
"run": "onType",
|
||||||
|
"disableNestedConfig": false,
|
||||||
|
"fixKind": "safe_fix",
|
||||||
|
"unusedDisableDirectives": "deny"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
bun.lock
44
bun.lock
|
|
@ -7,9 +7,9 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mobily/ts-belt": "v4.0.0-rc.5",
|
"@mobily/ts-belt": "v4.0.0-rc.5",
|
||||||
"@sentry/browser": "^10.47.0",
|
"@sentry/browser": "^10.47.0",
|
||||||
"a11y-dialog": "^8.1.4",
|
"a11y-dialog": "^8.1.5",
|
||||||
"effect": "^4.0.0-beta.43",
|
"effect": "^4.0.0-beta.43",
|
||||||
"lit-html": "^3.3.1",
|
"lit-html": "^3.3.2",
|
||||||
"purify-ts": "2.1.2",
|
"purify-ts": "2.1.2",
|
||||||
"ts-pattern": "^5.9.0",
|
"ts-pattern": "^5.9.0",
|
||||||
"valibot": "1.1.0",
|
"valibot": "1.1.0",
|
||||||
|
|
@ -17,17 +17,17 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@effect/language-service": "^0.84.3",
|
"@effect/language-service": "^0.84.3",
|
||||||
"@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274",
|
"@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274",
|
||||||
"@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#93dd909919",
|
"@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#3e49f5e2fb",
|
||||||
"@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801",
|
"@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801",
|
||||||
"@playwright/test": "^1.59.1",
|
"@playwright/test": "^1.59.1",
|
||||||
"@sentry/core": "^10.47.0",
|
"@sentry/core": "^10.47.0",
|
||||||
"@types/bun": "^1.3.11",
|
"@types/bun": "^1.3.11",
|
||||||
"@types/node": "^25.5.1",
|
"@types/node": "^25.5.2",
|
||||||
"@vitejs/plugin-legacy": "^8.0.1",
|
"@vitejs/plugin-legacy": "^8.0.1",
|
||||||
"better-typescript-lib": "^2.12.0",
|
"better-typescript-lib": "^2.12.0",
|
||||||
"browserslist": "^4.28.2",
|
"browserslist": "^4.28.2",
|
||||||
"caniuse-lite": "^1.0.30001784",
|
"caniuse-lite": "^1.0.30001785",
|
||||||
"eslint": "^10.1.0",
|
"eslint": "^10.2.0",
|
||||||
"eslint-plugin-functional": "^9.0.4",
|
"eslint-plugin-functional": "^9.0.4",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-oxlint": "^1.58.0",
|
"eslint-plugin-oxlint": "^1.58.0",
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
"stylelint-declaration-block-no-ignored-properties": "^3.0.0",
|
"stylelint-declaration-block-no-ignored-properties": "^3.0.0",
|
||||||
"stylelint-plugin-logical-css": "^2.1.0",
|
"stylelint-plugin-logical-css": "^2.1.0",
|
||||||
"typescript": "6.0.2",
|
"typescript": "6.0.2",
|
||||||
"typescript-eslint": "^8.48.1",
|
"typescript-eslint": "^8.58.0",
|
||||||
"vite": "^8.0.3",
|
"vite": "^8.0.3",
|
||||||
"vite-tsconfig-paths": "^6.1.1",
|
"vite-tsconfig-paths": "^6.1.1",
|
||||||
},
|
},
|
||||||
|
|
@ -282,21 +282,21 @@
|
||||||
|
|
||||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
||||||
|
|
||||||
"@eslint/config-array": ["@eslint/config-array@0.23.3", "", { "dependencies": { "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw=="],
|
"@eslint/config-array": ["@eslint/config-array@0.23.4", "", { "dependencies": { "@eslint/object-schema": "^3.0.4", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow=="],
|
||||||
|
|
||||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.5.3", "", { "dependencies": { "@eslint/core": "^1.1.1" } }, "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw=="],
|
"@eslint/config-helpers": ["@eslint/config-helpers@0.5.4", "", { "dependencies": { "@eslint/core": "^1.2.0" } }, "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg=="],
|
||||||
|
|
||||||
"@eslint/core": ["@eslint/core@1.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ=="],
|
"@eslint/core": ["@eslint/core@1.2.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A=="],
|
||||||
|
|
||||||
"@eslint/js": ["@eslint/js@10.0.1", "", { "peerDependencies": { "eslint": "^10.0.0" }, "optionalPeers": ["eslint"] }, "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA=="],
|
"@eslint/js": ["@eslint/js@10.0.1", "", { "peerDependencies": { "eslint": "^10.0.0" }, "optionalPeers": ["eslint"] }, "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA=="],
|
||||||
|
|
||||||
"@eslint/object-schema": ["@eslint/object-schema@3.0.3", "", {}, "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ=="],
|
"@eslint/object-schema": ["@eslint/object-schema@3.0.4", "", {}, "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw=="],
|
||||||
|
|
||||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.6.1", "", { "dependencies": { "@eslint/core": "^1.1.1", "levn": "^0.4.1" } }, "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ=="],
|
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.0", "", { "dependencies": { "@eslint/core": "^1.2.0", "levn": "^0.4.1" } }, "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg=="],
|
||||||
|
|
||||||
"@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-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#93dd9099199603d2fe2c334227e8051104b8f1a0", { "dependencies": { "globals": "^17.4.0", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0" }, "optionalDependencies": { "eslint-plugin-astro": "^1.6.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.7.0", "eslint-plugin-sonarjs": "^4.0.2", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0", "typescript": "^6.0.2" } }, "93dd9099199603d2fe2c334227e8051104b8f1a0"],
|
"@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-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"],
|
"@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"],
|
||||||
|
|
||||||
|
|
@ -726,7 +726,7 @@
|
||||||
|
|
||||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001784", "", {}, "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001785", "", {}, "sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ=="],
|
||||||
|
|
||||||
"change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="],
|
"change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="],
|
||||||
|
|
||||||
|
|
@ -826,7 +826,7 @@
|
||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
"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=="],
|
"eslint": ["eslint@10.2.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.4", "@eslint/config-helpers": "^0.5.4", "@eslint/core": "^1.2.0", "@eslint/plugin-kit": "^0.7.0", "@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-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA=="],
|
||||||
|
|
||||||
"eslint-compat-utils": ["eslint-compat-utils@0.6.5", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="],
|
"eslint-compat-utils": ["eslint-compat-utils@0.6.5", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="],
|
||||||
|
|
||||||
|
|
@ -1632,6 +1632,8 @@
|
||||||
|
|
||||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||||
|
|
||||||
|
"@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=="],
|
||||||
|
|
||||||
"@keyv/bigmap/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
|
"@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=="],
|
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||||
|
|
@ -1644,6 +1646,8 @@
|
||||||
|
|
||||||
"babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
"browserslist/caniuse-lite": ["caniuse-lite@1.0.30001784", "", {}, "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw=="],
|
||||||
|
|
||||||
"browserslist-to-esbuild/meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="],
|
"browserslist-to-esbuild/meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="],
|
||||||
|
|
||||||
"cacheable/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
|
"cacheable/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
|
||||||
|
|
@ -1690,6 +1694,14 @@
|
||||||
|
|
||||||
"vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"@gcch/configuration-eslint/eslint/@eslint/config-array": ["@eslint/config-array@0.23.3", "", { "dependencies": { "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw=="],
|
||||||
|
|
||||||
|
"@gcch/configuration-eslint/eslint/@eslint/config-helpers": ["@eslint/config-helpers@0.5.3", "", { "dependencies": { "@eslint/core": "^1.1.1" } }, "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw=="],
|
||||||
|
|
||||||
|
"@gcch/configuration-eslint/eslint/@eslint/core": ["@eslint/core@1.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ=="],
|
||||||
|
|
||||||
|
"@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=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
"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=="],
|
||||||
|
|
@ -1700,6 +1712,8 @@
|
||||||
|
|
||||||
"table/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"table/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"@gcch/configuration-eslint/eslint/@eslint/config-array/@eslint/object-schema": ["@eslint/object-schema@3.0.3", "", {}, "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ=="],
|
||||||
|
|
||||||
"eslint-plugin-jsx-a11y/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
"eslint-plugin-jsx-a11y/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
import config from "@gcch/configuration-oxlint";
|
import gcchConfig from "@gcch/configuration-oxlint";
|
||||||
|
import { OxlintConfig } from "oxlint";
|
||||||
|
|
||||||
|
const config: OxlintConfig = {
|
||||||
|
...gcchConfig,
|
||||||
|
globals: {
|
||||||
|
Bun: "readonly",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,6 @@ import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
reporter: "list",
|
|
||||||
retries: 1,
|
|
||||||
testDir: "../tests",
|
|
||||||
timeout: 10_000,
|
|
||||||
workers: "100%",
|
|
||||||
use: {
|
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
||||||
baseURL: "https://haikuatelier.gcch.local",
|
|
||||||
trace: "retry-with-trace",
|
|
||||||
clientCertificates: [
|
|
||||||
{
|
|
||||||
origin: "https://haikuatelier.gcch.local",
|
|
||||||
certPath: "../containers/data/certs/_wildcard.gcch.local.pem",
|
|
||||||
keyPath: "../containers/data/certs/_wildcard.gcch.local-key.pem",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ignoreHTTPSErrors: true,
|
|
||||||
},
|
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: "desktop-chromium-1920",
|
name: "desktop-chromium-1920",
|
||||||
|
|
@ -62,4 +44,22 @@ export default defineConfig({
|
||||||
// use: { ...devices["Pixel 7 landscape"] },
|
// use: { ...devices["Pixel 7 landscape"] },
|
||||||
// },
|
// },
|
||||||
],
|
],
|
||||||
|
reporter: "list",
|
||||||
|
retries: 1,
|
||||||
|
testDir: "../tests",
|
||||||
|
timeout: 10_000,
|
||||||
|
use: {
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
baseURL: "https://haikuatelier.gcch.local",
|
||||||
|
trace: "retry-with-trace",
|
||||||
|
clientCertificates: [
|
||||||
|
{
|
||||||
|
origin: "https://haikuatelier.gcch.local",
|
||||||
|
certPath: "../containers/data/certs/_wildcard.gcch.local.pem",
|
||||||
|
keyPath: "../containers/data/certs/_wildcard.gcch.local-key.pem",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ignoreHTTPSErrors: true,
|
||||||
|
},
|
||||||
|
workers: "100%",
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,59 @@
|
||||||
// @ts-expect-error -- La dépendance ne dispose pas de types.
|
import { pipe, Array as FxArray } from "effect";
|
||||||
|
import type stylelint from "stylelint";
|
||||||
import { propertyGroups } from "stylelint-config-clean-order";
|
import { propertyGroups } from "stylelint-config-clean-order";
|
||||||
|
|
||||||
/** @type {string[][]} */
|
/**
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- Impossible de typer correctement avec JSDoc.
|
* Définition d'un groupe de Propriétés _CSS_ du plugin `stylelint-config-clean-order` pour _Stylelint_.
|
||||||
const groupesProprietes = Array.from(/** @type {GroupeProprietes} */ propertyGroups);
|
*/
|
||||||
|
type StylelintConfigCleanOrderPropertyGroup = {
|
||||||
|
emptyLineBefore: "never" | "threshold";
|
||||||
|
noEmptyLineBetween: boolean;
|
||||||
|
properties: ReadonlyArray<string> | string;
|
||||||
|
};
|
||||||
|
|
||||||
/** @import { StylelintConfigCleanOrderPropertyGroup } from "./lib/stylelint" */
|
const ordreProprietes: ReadonlyArray<StylelintConfigCleanOrderPropertyGroup> = pipe(
|
||||||
/** @type {Array<StylelintConfigCleanOrderPropertyGroup>} */
|
Array.from(propertyGroups),
|
||||||
const ordreProprietes = groupesProprietes.map((properties) => ({
|
FxArray.map((properties: ReadonlyArray<string>) => ({
|
||||||
emptyLineBefore: "never",
|
emptyLineBefore: "never",
|
||||||
noEmptyLineBetween: true,
|
noEmptyLineBetween: true,
|
||||||
properties,
|
properties,
|
||||||
}));
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
/** @type {import("stylelint").Config} */
|
const stylelintConfig: stylelint.Config = {
|
||||||
export default {
|
|
||||||
extends: ["stylelint-config-standard-scss", "stylelint-config-sass-guidelines", "stylelint-config-clean-order"],
|
extends: ["stylelint-config-standard-scss", "stylelint-config-sass-guidelines", "stylelint-config-clean-order"],
|
||||||
plugins: ["stylelint-declaration-block-no-ignored-properties"],
|
plugins: ["stylelint-declaration-block-no-ignored-properties"],
|
||||||
rules: {
|
rules: {
|
||||||
"@stylistic/function-parentheses-space-inside": null,
|
"@stylistic/function-parentheses-space-inside": undefined,
|
||||||
"@stylistic/selector-list-comma-newline-after": null,
|
"@stylistic/selector-list-comma-newline-after": undefined,
|
||||||
"@stylistic/string-quotes": null,
|
"@stylistic/string-quotes": undefined,
|
||||||
"custom-property-pattern": null,
|
"custom-property-pattern": undefined,
|
||||||
"declaration-block-no-duplicate-custom-properties": true,
|
"declaration-block-no-duplicate-custom-properties": true,
|
||||||
"declaration-block-no-duplicate-properties": true,
|
"declaration-block-no-duplicate-properties": true,
|
||||||
"declaration-block-no-redundant-longhand-properties": true,
|
"declaration-block-no-redundant-longhand-properties": true,
|
||||||
"declaration-block-no-shorthand-property-overrides": true,
|
"declaration-block-no-shorthand-property-overrides": true,
|
||||||
"max-nesting-depth": null,
|
"max-nesting-depth": undefined,
|
||||||
"no-descending-specificity": null,
|
"no-descending-specificity": undefined,
|
||||||
"no-duplicate-selectors": [true, { disallowInList: false }],
|
"no-duplicate-selectors": [
|
||||||
"order/properties-order": [ordreProprietes, { severity: "error", unspecified: "bottomAlphabetical" }],
|
true,
|
||||||
|
{
|
||||||
|
disallowInList: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"order/properties-order": [
|
||||||
|
ordreProprietes,
|
||||||
|
{
|
||||||
|
severity: "error",
|
||||||
|
unspecified: "bottomAlphabetical",
|
||||||
|
},
|
||||||
|
],
|
||||||
"plugin/declaration-block-no-ignored-properties": true,
|
"plugin/declaration-block-no-ignored-properties": true,
|
||||||
"selector-class-pattern": null,
|
"selector-class-pattern": undefined,
|
||||||
"selector-id-pattern": null,
|
"selector-id-pattern": undefined,
|
||||||
"selector-max-compound-selectors": null,
|
"selector-max-compound-selectors": undefined,
|
||||||
"selector-max-id": null,
|
"selector-max-id": undefined,
|
||||||
"selector-no-qualifying-type": null,
|
"selector-no-qualifying-type": undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default stylelintConfig;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ export default defineConfig(({ mode }) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
base: "/",
|
base: "/",
|
||||||
cacheDir: ".cache/vite",
|
|
||||||
build: {
|
build: {
|
||||||
assetsDir: ".",
|
assetsDir: ".",
|
||||||
cssMinify: "lightningcss",
|
cssMinify: "lightningcss",
|
||||||
|
|
@ -40,6 +39,7 @@ export default defineConfig(({ mode }) => {
|
||||||
target: "es2020",
|
target: "es2020",
|
||||||
write: true,
|
write: true,
|
||||||
},
|
},
|
||||||
|
cacheDir: ".cache/vite",
|
||||||
css: {
|
css: {
|
||||||
devSourcemap: true,
|
devSourcemap: true,
|
||||||
transformer: "lightningcss",
|
transformer: "lightningcss",
|
||||||
|
|
|
||||||
28
composer.lock
generated
28
composer.lock
generated
|
|
@ -5129,12 +5129,12 @@
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||||
"reference": "958bc6ccdb194912236265e4f36256e55bb0c9da"
|
"reference": "db78064456eb735e368677828095fb7fe5aeda6f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/958bc6ccdb194912236265e4f36256e55bb0c9da",
|
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/db78064456eb735e368677828095fb7fe5aeda6f",
|
||||||
"reference": "958bc6ccdb194912236265e4f36256e55bb0c9da",
|
"reference": "db78064456eb735e368677828095fb7fe5aeda6f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
|
|
@ -5555,7 +5555,7 @@
|
||||||
"knplabs/knp-snappy": "<=1.4.2",
|
"knplabs/knp-snappy": "<=1.4.2",
|
||||||
"kohana/core": "<3.3.3",
|
"kohana/core": "<3.3.3",
|
||||||
"koillection/koillection": "<1.6.12",
|
"koillection/koillection": "<1.6.12",
|
||||||
"krayin/laravel-crm": "<=1.3",
|
"krayin/laravel-crm": "<=2.2",
|
||||||
"kreait/firebase-php": ">=3.2,<3.8.1",
|
"kreait/firebase-php": ">=3.2,<3.8.1",
|
||||||
"kumbiaphp/kumbiapp": "<=1.1.1",
|
"kumbiaphp/kumbiapp": "<=1.1.1",
|
||||||
"la-haute-societe/tcpdf": "<6.2.22",
|
"la-haute-societe/tcpdf": "<6.2.22",
|
||||||
|
|
@ -5818,7 +5818,7 @@
|
||||||
"roadiz/documents": "<2.3.42|>=2.4,<2.5.44|>=2.6,<2.6.28|>=2.7,<2.7.9",
|
"roadiz/documents": "<2.3.42|>=2.4,<2.5.44|>=2.6,<2.6.28|>=2.7,<2.7.9",
|
||||||
"robrichards/xmlseclibs": "<3.1.5",
|
"robrichards/xmlseclibs": "<3.1.5",
|
||||||
"roots/soil": "<4.1",
|
"roots/soil": "<4.1",
|
||||||
"roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11",
|
"roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11|>=1.7.0.0-beta,<1.7.0.0-RC5-dev",
|
||||||
"rudloff/alltube": "<3.0.3",
|
"rudloff/alltube": "<3.0.3",
|
||||||
"rudloff/rtmpdump-bin": "<=2.3.1",
|
"rudloff/rtmpdump-bin": "<=2.3.1",
|
||||||
"s-cart/core": "<=9.0.5",
|
"s-cart/core": "<=9.0.5",
|
||||||
|
|
@ -6171,20 +6171,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-04-03T04:48:53+00:00"
|
"time": "2026-04-04T07:24:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/diff",
|
"name": "sebastian/diff",
|
||||||
"version": "8.0.0",
|
"version": "8.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||||
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3"
|
"reference": "9c957d730257f49c873f3761674559bd90098a7d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
|
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/9c957d730257f49c873f3761674559bd90098a7d",
|
||||||
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
|
"reference": "9c957d730257f49c873f3761674559bd90098a7d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -6197,7 +6197,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "8.0-dev"
|
"dev-main": "8.1-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
@ -6230,7 +6230,7 @@
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/diff/tree/8.0.0"
|
"source": "https://github.com/sebastianbergmann/diff/tree/8.1.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -6250,7 +6250,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-02-06T04:42:27+00:00"
|
"time": "2026-04-05T12:02:33+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
|
|
@ -7483,5 +7483,5 @@
|
||||||
"php": ">=8.5"
|
"php": ">=8.5"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.9.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@
|
||||||
"curryfication",
|
"curryfication",
|
||||||
"giftcard",
|
"giftcard",
|
||||||
"taplo",
|
"taplo",
|
||||||
"phpactor"
|
"phpactor",
|
||||||
|
"gcch"
|
||||||
],
|
],
|
||||||
"words": [
|
"words": [
|
||||||
"GLITCHTIP",
|
"GLITCHTIP",
|
||||||
|
|
|
||||||
8
lib/stylelint.d.ts
vendored
8
lib/stylelint.d.ts
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
/**
|
|
||||||
* Définition d'un groupe de Propriétés _CSS_ du plugin `stylelint-config-clean-order` pour _Stylelint_.
|
|
||||||
*/
|
|
||||||
export type stylelintconfigcleanorderpropertygroup = {
|
|
||||||
emptyLineBefore: "never" | "threshold";
|
|
||||||
noEmptyLineBetween: boolean;
|
|
||||||
properties: string | array<string>;
|
|
||||||
};
|
|
||||||
14
package.json
14
package.json
|
|
@ -13,9 +13,9 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mobily/ts-belt": "v4.0.0-rc.5",
|
"@mobily/ts-belt": "v4.0.0-rc.5",
|
||||||
"@sentry/browser": "^10.47.0",
|
"@sentry/browser": "^10.47.0",
|
||||||
"a11y-dialog": "^8.1.4",
|
"a11y-dialog": "^8.1.5",
|
||||||
"effect": "^4.0.0-beta.43",
|
"effect": "^4.0.0-beta.43",
|
||||||
"lit-html": "^3.3.1",
|
"lit-html": "^3.3.2",
|
||||||
"purify-ts": "2.1.2",
|
"purify-ts": "2.1.2",
|
||||||
"ts-pattern": "^5.9.0",
|
"ts-pattern": "^5.9.0",
|
||||||
"valibot": "1.1.0"
|
"valibot": "1.1.0"
|
||||||
|
|
@ -23,17 +23,17 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@effect/language-service": "^0.84.3",
|
"@effect/language-service": "^0.84.3",
|
||||||
"@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274",
|
"@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274",
|
||||||
"@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#93dd909919",
|
"@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#3e49f5e2fb",
|
||||||
"@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801",
|
"@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801",
|
||||||
"@playwright/test": "^1.59.1",
|
"@playwright/test": "^1.59.1",
|
||||||
"@sentry/core": "^10.47.0",
|
"@sentry/core": "^10.47.0",
|
||||||
"@types/bun": "^1.3.11",
|
"@types/bun": "^1.3.11",
|
||||||
"@types/node": "^25.5.1",
|
"@types/node": "^25.5.2",
|
||||||
"@vitejs/plugin-legacy": "^8.0.1",
|
"@vitejs/plugin-legacy": "^8.0.1",
|
||||||
"better-typescript-lib": "^2.12.0",
|
"better-typescript-lib": "^2.12.0",
|
||||||
"browserslist": "^4.28.2",
|
"browserslist": "^4.28.2",
|
||||||
"caniuse-lite": "^1.0.30001784",
|
"caniuse-lite": "^1.0.30001785",
|
||||||
"eslint": "^10.1.0",
|
"eslint": "^10.2.0",
|
||||||
"eslint-plugin-functional": "^9.0.4",
|
"eslint-plugin-functional": "^9.0.4",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-oxlint": "^1.58.0",
|
"eslint-plugin-oxlint": "^1.58.0",
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
"stylelint-declaration-block-no-ignored-properties": "^3.0.0",
|
"stylelint-declaration-block-no-ignored-properties": "^3.0.0",
|
||||||
"stylelint-plugin-logical-css": "^2.1.0",
|
"stylelint-plugin-logical-css": "^2.1.0",
|
||||||
"typescript": "6.0.2",
|
"typescript": "6.0.2",
|
||||||
"typescript-eslint": "^8.48.1",
|
"typescript-eslint": "^8.58.0",
|
||||||
"vite": "^8.0.3",
|
"vite": "^8.0.3",
|
||||||
"vite-tsconfig-paths": "^6.1.1"
|
"vite-tsconfig-paths": "^6.1.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,69 @@
|
||||||
import { YAML } from "bun";
|
import { YAML } from "bun";
|
||||||
import { Array as EffectArray, Console, Data, Effect, pipe, Record, Schema, SchemaIssue } from "effect";
|
import { Console, Data, Effect, Array as EffectArray, pipe, Record, Schema, SchemaIssue } from "effect";
|
||||||
import { NoSuchElementError } from "effect/Cause";
|
|
||||||
import { type ReadonlyRecord } from "effect/Record";
|
|
||||||
import { SchemaError } from "effect/Schema";
|
import { SchemaError } from "effect/Schema";
|
||||||
|
|
||||||
const COMPOSE_PATH = "compose.yaml";
|
const COMPOSE_PATH = "compose.yaml";
|
||||||
|
const DEFAULT_CMD_TIMEOUT = 10_000;
|
||||||
|
class Compose extends Schema.Class<Compose>("Compose")({
|
||||||
|
name: Schema.String,
|
||||||
|
services: Schema.Record(Schema.String, Schema.Unknown),
|
||||||
|
}) {}
|
||||||
|
|
||||||
class ScriptError extends Data.TaggedError("ScriptError")<{ cause: unknown }> {}
|
class ScriptError extends Data.TaggedError("ScriptError")<{ cause: unknown }> {}
|
||||||
|
|
||||||
const ComposeSchema = Schema.Record(Schema.Union([Schema.String, Schema.Symbol]), Schema.Any);
|
// Const composeSchema = Schema.Record(Schema.Union([Schema.String, Schema.Symbol]), Schema.Unknown);
|
||||||
|
// type YamlRecord = ReadonlyRecord<string | symbol, unknown>;
|
||||||
|
|
||||||
type ComposeYaml = ReadonlyRecord<string | symbol, any>;
|
/* */
|
||||||
|
|
||||||
const getObjectKey = (key: string, yaml: ComposeYaml): Effect.Effect<ReadonlyArray<string>, ScriptError> =>
|
/**
|
||||||
pipe(
|
* Retourne les noms des services déclarés dans un ficher _Compose_.
|
||||||
Record.get(key)(yaml),
|
* @param compose Le fichier _Compose_ sous forme d'objet.
|
||||||
Effect.fromOption,
|
* @returns Les noms des Services sous forme de tableau.
|
||||||
Effect.map((yaml: any) => Record.keys(yaml)),
|
*/
|
||||||
Effect.mapError((e: NoSuchElementError) => new ScriptError({ cause: e })),
|
const getServicesFromComposeYaml: (compose: Compose) => ReadonlyArray<string> = (compose) =>
|
||||||
);
|
Record.keys(compose.services);
|
||||||
|
|
||||||
const getFileContent = Effect.fn("getFileContent")(function* (filePath: string) {
|
/**
|
||||||
const fileRef = Bun.file(filePath);
|
* Récupère le contenu texte d'un fichier pour un chemin donné.
|
||||||
|
*
|
||||||
|
* @param filePath Le chemin du fichier dont on souhaite le contenu.
|
||||||
|
* @returns Le contenu textuel du fichier sous forme de chaîne de caractères.
|
||||||
|
*/
|
||||||
|
const getFileContent: (filePath: string) => Effect.Effect<string, ScriptError> = Effect.fn("getFileContent")(
|
||||||
|
function* (filePath) {
|
||||||
|
const fileRef: Bun.BunFile = Bun.file(filePath);
|
||||||
|
|
||||||
yield* Effect.tryPromise({
|
yield* Effect.tryPromise({
|
||||||
try: () => fileRef.exists(),
|
catch: (_): ScriptError => new ScriptError({ cause: "The wanted file does not exist." }),
|
||||||
catch: (_) => new ScriptError({ cause: "The wanted file does not exist." }),
|
try: async (): Promise<boolean> => fileRef.exists(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return yield* Effect.tryPromise({
|
return yield* Effect.tryPromise({
|
||||||
try: () => fileRef.text(),
|
catch: (_): ScriptError => new ScriptError({ cause: "Can't retrieve the file's text content." }),
|
||||||
catch: (_) => new ScriptError({ cause: "Can't retrieve the file's text content." }),
|
try: async (): Promise<string> => fileRef.text(),
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const getComposeYaml = <A>(filePath: string, schema: Schema.Schema<A>) =>
|
/**
|
||||||
pipe(
|
* Récupère le contenu _YAML_ d'un fichier _Compose_ sous forme de `Record`.
|
||||||
getFileContent(filePath),
|
*
|
||||||
Effect.map((text: string) => YAML.parse(text)),
|
* @param path Le chemin du fichier _Compose_.
|
||||||
Effect.flatMap((yaml: unknown) => Schema.decodeUnknownEffect(schema)(yaml, { errors: "all" })),
|
* @param schema Le `Schema` utilisée pour le parsage des données du fichier.
|
||||||
Effect.mapError((error) => {
|
* @returns Un `Record` des données du fichier.
|
||||||
|
*/
|
||||||
|
const getComposeYaml: <ComposeSchema>(
|
||||||
|
path: string,
|
||||||
|
schema: Schema.Schema<ComposeSchema>,
|
||||||
|
) => Effect.Effect<ComposeSchema, ScriptError, unknown> = Effect.fn("getComposeYaml")(function* (path, schema) {
|
||||||
|
return yield* pipe(
|
||||||
|
getFileContent(path),
|
||||||
|
Effect.map((text: string): unknown => YAML.parse(text)),
|
||||||
|
Effect.flatMap((yaml: unknown) =>
|
||||||
|
Schema.decodeUnknownEffect(schema)(yaml, { errors: "all", onExcessProperty: "ignore" }),
|
||||||
|
),
|
||||||
|
Effect.mapError((error): ScriptError => {
|
||||||
if (error instanceof SchemaError) {
|
if (error instanceof SchemaError) {
|
||||||
return new ScriptError({ cause: SchemaIssue.makeFormatterStandardSchemaV1()(error.issue) });
|
return new ScriptError({ cause: SchemaIssue.makeFormatterStandardSchemaV1()(error.issue) });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -47,14 +71,15 @@ const getComposeYaml = <A>(filePath: string, schema: Schema.Schema<A>) =>
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const program: Effect.Effect<ReadonlyArray<string>, ScriptError> = pipe(
|
const program: Effect.Effect<ReadonlyArray<string>, ScriptError> = pipe(
|
||||||
getComposeYaml(COMPOSE_PATH, ComposeSchema),
|
getComposeYaml(COMPOSE_PATH, Compose),
|
||||||
Effect.flatMap((yaml: ComposeYaml) => getObjectKey("services", yaml)),
|
Effect.map((compose: Compose) => getServicesFromComposeYaml(compose)),
|
||||||
Effect.map((keys: ReadonlyArray<string>) => EffectArray.filter(keys, (key) => key !== "wordpress")),
|
Effect.map((keys: ReadonlyArray<string>) => EffectArray.filter(keys, (key) => key !== "wordpress")),
|
||||||
Effect.orElseSucceed(() => [""]),
|
Effect.orElseSucceed(() => [""]),
|
||||||
Effect.tap((services: ReadonlyArray<string>) => {
|
Effect.tap((services: ReadonlyArray<string>) => {
|
||||||
Bun.spawn({ cmd: ["podman", "compose", "pull", ...services], timeout: 10000 });
|
Bun.spawn({ cmd: ["podman", "compose", "pull", ...services], timeout: DEFAULT_CMD_TIMEOUT });
|
||||||
return Effect.succeed(services);
|
return Effect.succeed(services);
|
||||||
}),
|
}),
|
||||||
Effect.tapCause(Console.error),
|
Effect.tapCause(Console.error),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import { expect, type Page, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
import type { Page } from "@playwright/test";
|
||||||
|
|
||||||
type TestPage = {
|
type TestPage = {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const genTimestamp = (): string =>
|
const genTimestamp = (): string => Intl.DateTimeFormat("sv-SE", { dateStyle: "short" }).format(Date.now());
|
||||||
Intl.DateTimeFormat("sv-SE", {
|
|
||||||
dateStyle: "short",
|
|
||||||
}).format(Date.now());
|
|
||||||
|
|
||||||
const takeFullPageScreenshot = async (page: Page, name: string): Promise<void> => {
|
const takeFullPageScreenshot = async (page: Page, name: string): Promise<void> => {
|
||||||
await page.screenshot({ fullPage: false, path: `captures/${name}`, type: "png" });
|
await page.screenshot({ fullPage: false, path: `captures/${name}`, type: "png" });
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ type ProductsFixture = {
|
||||||
type ProductsKinds = {
|
type ProductsKinds = {
|
||||||
allProducts: WCV3Products;
|
allProducts: WCV3Products;
|
||||||
simpleProducts: WCV3Products;
|
simpleProducts: WCV3Products;
|
||||||
simpleProductsWithStock: WCV3Products;
|
|
||||||
simpleProductsWithoutStock: WCV3Products;
|
simpleProductsWithoutStock: WCV3Products;
|
||||||
|
simpleProductsWithStock: WCV3Products;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const test = base.extend<ProductsFixture>({
|
export const test = base.extend<ProductsFixture>({
|
||||||
|
|
@ -34,7 +34,7 @@ export const test = base.extend<ProductsFixture>({
|
||||||
const backendHeaders: BackendHeaders = await getBackendHeadersFromHtml(page);
|
const backendHeaders: BackendHeaders = await getBackendHeadersFromHtml(page);
|
||||||
|
|
||||||
const response = await request.get("/wp-json/wc/v3/products?page=1&per_page=100&status=publish", {
|
const response = await request.get("/wp-json/wc/v3/products?page=1&per_page=100&status=publish", {
|
||||||
headers: { Nonce: backendHeaders.nonce, Authorization: `Basic ${backendHeaders.authString}` },
|
headers: { Authorization: `Basic ${backendHeaders.authString}`, Nonce: backendHeaders.nonce },
|
||||||
});
|
});
|
||||||
expect(response.ok(), "The API returned the list of every Product").toBeTruthy();
|
expect(response.ok(), "The API returned the list of every Product").toBeTruthy();
|
||||||
|
|
||||||
|
|
@ -50,15 +50,15 @@ export const test = base.extend<ProductsFixture>({
|
||||||
const kinds = {
|
const kinds = {
|
||||||
allProducts,
|
allProducts,
|
||||||
simpleProducts,
|
simpleProducts,
|
||||||
simpleProductsWithStock,
|
|
||||||
simpleProductsWithoutStock,
|
simpleProductsWithoutStock,
|
||||||
|
simpleProductsWithStock,
|
||||||
} satisfies ProductsKinds;
|
} satisfies ProductsKinds;
|
||||||
|
|
||||||
await use(kinds);
|
await use(kinds);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can add a Product without variation with stock to the Cart", async ({ products, page }) => {
|
test("can add a Product without variation with stock to the Cart", async ({ page, products }) => {
|
||||||
// Prend un produit au hasard.
|
// Prend un produit au hasard.
|
||||||
const randomProductIndex = getRandomIntInclusive(0, products.simpleProductsWithStock.length - 1);
|
const randomProductIndex = getRandomIntInclusive(0, products.simpleProductsWithStock.length - 1);
|
||||||
const randomProduct = products.simpleProductsWithStock.at(randomProductIndex);
|
const randomProduct = products.simpleProductsWithStock.at(randomProductIndex);
|
||||||
|
|
@ -71,7 +71,7 @@ test("can add a Product without variation with stock to the Cart", async ({ prod
|
||||||
await page.goto(randomProduct.permalink);
|
await page.goto(randomProduct.permalink);
|
||||||
|
|
||||||
// Vérifie le bon état du bouton de l'ajout au Panier.
|
// Vérifie le bon état du bouton de l'ajout au Panier.
|
||||||
const addToCartButton = page.getByRole("button", { name: "Add to cart", disabled: false });
|
const addToCartButton = page.getByRole("button", { disabled: false, name: "Add to cart" });
|
||||||
await addToCartButton.scrollIntoViewIfNeeded();
|
await addToCartButton.scrollIntoViewIfNeeded();
|
||||||
await expect(addToCartButton, "The add to cart button must be visible").toBeVisible();
|
await expect(addToCartButton, "The add to cart button must be visible").toBeVisible();
|
||||||
await expect(addToCartButton, "The add to cart button must be enabled").toBeEnabled();
|
await expect(addToCartButton, "The add to cart button must be enabled").toBeEnabled();
|
||||||
|
|
@ -91,7 +91,7 @@ test("can add a Product without variation with stock to the Cart", async ({ prod
|
||||||
expect(cartLink, "The cart items' indicator has been incremented").toBeDefined();
|
expect(cartLink, "The cart items' indicator has been incremented").toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can't add a Product without variation without stock to the Cart", async ({ products, page }) => {
|
test("can't add a Product without variation without stock to the Cart", async ({ page, products }) => {
|
||||||
// Prend un produit au hasard.
|
// Prend un produit au hasard.
|
||||||
const randomProductIndex = getRandomIntInclusive(0, products.simpleProductsWithoutStock.length - 1);
|
const randomProductIndex = getRandomIntInclusive(0, products.simpleProductsWithoutStock.length - 1);
|
||||||
const randomProduct = products.simpleProductsWithoutStock.at(randomProductIndex);
|
const randomProduct = products.simpleProductsWithoutStock.at(randomProductIndex);
|
||||||
|
|
@ -104,7 +104,7 @@ test("can't add a Product without variation without stock to the Cart", async ({
|
||||||
await page.goto(randomProduct.permalink);
|
await page.goto(randomProduct.permalink);
|
||||||
|
|
||||||
// Vérifie le bon état du bouton de l'ajout au Panier.
|
// Vérifie le bon état du bouton de l'ajout au Panier.
|
||||||
const outOfStockButton = page.getByRole("button", { name: "Out of stock", disabled: true });
|
const outOfStockButton = page.getByRole("button", { disabled: true, name: "Out of stock" });
|
||||||
await outOfStockButton.scrollIntoViewIfNeeded();
|
await outOfStockButton.scrollIntoViewIfNeeded();
|
||||||
await expect(outOfStockButton, "The add to cart button must be visible").toBeVisible();
|
await expect(outOfStockButton, "The add to cart button must be visible").toBeVisible();
|
||||||
await expect(outOfStockButton, "The add to cart button must be disabled").toBeDisabled();
|
await expect(outOfStockButton, "The add to cart button must be disabled").toBeDisabled();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { test, expect, Page, Locator, Response, APIRequestContext } from "@playwright/test";
|
import { APIRequestContext, expect, Locator, Page, Response, test } from "@playwright/test";
|
||||||
import { WCV3Products } from "../../web/app/themes/haiku-atelier-2024/src/scripts/lib/types/api/v3/products";
|
import { WCV3Products } from "../../web/app/themes/haiku-atelier-2024/src/scripts/lib/types/api/v3/products";
|
||||||
import { BackendHeaders, getBackendHeadersFromHtml } from "./utils.ts";
|
import { BackendHeaders, getBackendHeadersFromHtml } from "./utils.ts";
|
||||||
|
|
||||||
test.describe.configure({ mode: "parallel", timeout: 60000 });
|
test.describe.configure({ mode: "parallel", timeout: 60_000 });
|
||||||
|
|
||||||
test("can scroll to the end of the grid", async ({ page }): Promise<void> => {
|
test("can scroll to the end of the grid", async ({ page }): Promise<void> => {
|
||||||
await scrollToGridsEnd(page);
|
await scrollToGridsEnd(page);
|
||||||
|
|
@ -22,12 +22,10 @@ test.skip("can access all Products' pages", async ({ page, request }): Promise<v
|
||||||
const getAllProductsLinks = async (page: Page, request: APIRequestContext): Promise<Array<string>> => {
|
const getAllProductsLinks = async (page: Page, request: APIRequestContext): Promise<Array<string>> => {
|
||||||
const backendHeaders: BackendHeaders = await getBackendHeadersFromHtml(page);
|
const backendHeaders: BackendHeaders = await getBackendHeadersFromHtml(page);
|
||||||
const response = await request.get("/wp-json/wc/v3/products?page=1&per_page=100&status=publish", {
|
const response = await request.get("/wp-json/wc/v3/products?page=1&per_page=100&status=publish", {
|
||||||
headers: { Nonce: backendHeaders.nonce, Authorization: `Basic ${backendHeaders.authString}` },
|
headers: { Authorization: `Basic ${backendHeaders.authString}`, Nonce: backendHeaders.nonce },
|
||||||
});
|
});
|
||||||
const json = (await response.json()) as WCV3Products;
|
const json = (await response.json()) as WCV3Products;
|
||||||
const links = json.map((p) => p.permalink);
|
return json.map((p) => p.permalink);
|
||||||
|
|
||||||
return links;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollToGridsEnd = async (page: Page): Promise<void> => {
|
const scrollToGridsEnd = async (page: Page): Promise<void> => {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noPropertyAccessFromIndexSignature": true,
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
"noStrictGenericChecks": false,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"noUncheckedSideEffectImports": true,
|
"noUncheckedSideEffectImports": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\Data\Product;
|
use HaikuAtelier\Data\Product;
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Timber\Timber;
|
use Timber\Timber;
|
||||||
|
|
@ -18,8 +17,6 @@ use function add_action;
|
||||||
use function array_map;
|
use function array_map;
|
||||||
use function wc_get_products;
|
use function wc_get_products;
|
||||||
|
|
||||||
require_once __DIR__ . '/src/inc/TraitementInformations.php';
|
|
||||||
|
|
||||||
$context = Timber::context();
|
$context = Timber::context();
|
||||||
$templates = ['boutique.twig'];
|
$templates = ['boutique.twig'];
|
||||||
|
|
||||||
|
|
@ -32,12 +29,7 @@ $products = array_map(
|
||||||
);
|
);
|
||||||
$context['products'] = $products;
|
$context['products'] = $products;
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-boutique',
|
handle: 'haiku-atelier-2024-styles-page-boutique',
|
||||||
path: '/assets/css/pages/page-boutique.css',
|
path: '/assets/css/pages/page-boutique.css',
|
||||||
|
|
@ -50,9 +42,7 @@ function load_page_resources(): void {
|
||||||
id: 'haiku-atelier-2024-scripts-menu-categories',
|
id: 'haiku-atelier-2024-scripts-menu-categories',
|
||||||
path: '/assets/js/scripts-menu-categories.js',
|
path: '/assets/js/scripts-menu-categories.js',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
Timber::render(
|
Timber::render(
|
||||||
data: $context,
|
data: $context,
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@ button.bouton-blanc-sur-noir {
|
||||||
}
|
}
|
||||||
button.bouton-retour-haut {
|
button.bouton-retour-haut {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
z-index: 500;
|
||||||
right: var(--espace-xl);
|
right: var(--espace-xl);
|
||||||
bottom: calc(var(--espace-l) + var(--pied-de-page-hauteur));
|
bottom: calc(var(--espace-l) + var(--pied-de-page-hauteur));
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
|
|
@ -321,7 +322,6 @@ button.bouton-retour-haut {
|
||||||
background: var(--couleur-fond);
|
background: var(--couleur-fond);
|
||||||
box-shadow: initial;
|
box-shadow: initial;
|
||||||
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
|
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
|
||||||
z-index: 500;
|
|
||||||
}
|
}
|
||||||
button.bouton-retour-haut img {
|
button.bouton-retour-haut img {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -11,18 +11,18 @@ jQuery(document).ready(function ($) {
|
||||||
|
|
||||||
$(".customize-control-tinymce-editor").each(function () {
|
$(".customize-control-tinymce-editor").each(function () {
|
||||||
// Get the toolbar strings that were passed from the PHP Class
|
// Get the toolbar strings that were passed from the PHP Class
|
||||||
var tinyMCEToolbar1String = _wpCustomizeSettings.controls[$(this).attr("id")].skyrockettinymcetoolbar1;
|
const tinyMCEToolbar1String = _wpCustomizeSettings.controls[$(this).attr("id")].skyrockettinymcetoolbar1;
|
||||||
var tinyMCEToolbar2String = _wpCustomizeSettings.controls[$(this).attr("id")].skyrockettinymcetoolbar2;
|
const tinyMCEToolbar2String = _wpCustomizeSettings.controls[$(this).attr("id")].skyrockettinymcetoolbar2;
|
||||||
var tinyMCEMediaButtons = _wpCustomizeSettings.controls[$(this).attr("id")].skyrocketmediabuttons;
|
const tinyMCEMediaButtons = _wpCustomizeSettings.controls[$(this).attr("id")].skyrocketmediabuttons;
|
||||||
|
|
||||||
wp.editor.initialize($(this).attr("id"), {
|
wp.editor.initialize($(this).attr("id"), {
|
||||||
|
mediaButtons: tinyMCEMediaButtons,
|
||||||
|
quicktags: true,
|
||||||
tinymce: {
|
tinymce: {
|
||||||
wpautop: true,
|
wpautop: true,
|
||||||
toolbar1: tinyMCEToolbar1String,
|
toolbar1: tinyMCEToolbar1String,
|
||||||
toolbar2: tinyMCEToolbar2String,
|
toolbar2: tinyMCEToolbar2String,
|
||||||
},
|
},
|
||||||
quicktags: true,
|
|
||||||
mediaButtons: tinyMCEMediaButtons,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(document).on("tinymce-editor-init", function (event, editor) {
|
$(document).on("tinymce-editor-init", function (event, editor) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Timber\Timber;
|
use Timber\Timber;
|
||||||
|
|
||||||
|
|
@ -17,12 +16,7 @@ use function add_action;
|
||||||
$context = Timber::context();
|
$context = Timber::context();
|
||||||
$templates = ['accueil.twig'];
|
$templates = ['accueil.twig'];
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-accueil',
|
handle: 'haiku-atelier-2024-styles-page-accueil',
|
||||||
path: '/assets/css/pages/page-accueil.css',
|
path: '/assets/css/pages/page-accueil.css',
|
||||||
|
|
@ -31,9 +25,7 @@ function load_page_resources(): void {
|
||||||
id: 'haiku-atelier-2024-scripts-page-accueil',
|
id: 'haiku-atelier-2024-scripts-page-accueil',
|
||||||
path: '/assets/js/scripts-page-accueil.js',
|
path: '/assets/js/scripts-page-accueil.js',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
Timber::render(
|
Timber::render(
|
||||||
data: $context,
|
data: $context,
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,7 @@ if (is_bool($image_dimensions)) {
|
||||||
|
|
||||||
$context['image_dimensions'] = $image_dimensions;
|
$context['image_dimensions'] = $image_dimensions;
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-a-propos',
|
handle: 'haiku-atelier-2024-styles-page-a-propos',
|
||||||
path: '/assets/css/pages/page-a-propos.css',
|
path: '/assets/css/pages/page-a-propos.css',
|
||||||
|
|
@ -43,9 +38,7 @@ function load_page_resources(): void {
|
||||||
id: 'haiku-atelier-2024-scripts-page-a-propos',
|
id: 'haiku-atelier-2024-scripts-page-a-propos',
|
||||||
path: '/assets/js/scripts-page-a-propos.js',
|
path: '/assets/js/scripts-page-a-propos.js',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
Timber::render(
|
Timber::render(
|
||||||
data: $context,
|
data: $context,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\Data\Cart;
|
use HaikuAtelier\Data\Cart;
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Illuminate\Support\Number;
|
use Illuminate\Support\Number;
|
||||||
|
|
@ -99,12 +98,7 @@ $context['pays_livraison'] = $allowed_countries;
|
||||||
$context['sous_total_livraison'] = $shipping_subtotal;
|
$context['sous_total_livraison'] = $shipping_subtotal;
|
||||||
$context['methodes_livraison'] = $methodes_livraison;
|
$context['methodes_livraison'] = $methodes_livraison;
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-panier',
|
handle: 'haiku-atelier-2024-styles-page-panier',
|
||||||
path: '/assets/css/pages/page-panier.css',
|
path: '/assets/css/pages/page-panier.css',
|
||||||
|
|
@ -113,9 +107,7 @@ function load_page_resources(): void {
|
||||||
id: 'haiku-atelier-2024-scripts-page-panier',
|
id: 'haiku-atelier-2024-scripts-page-panier',
|
||||||
path: '/assets/js/scripts-page-panier.js',
|
path: '/assets/js/scripts-page-panier.js',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
// Rendu
|
// Rendu
|
||||||
Timber::render(
|
Timber::render(
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Timber\Timber;
|
use Timber\Timber;
|
||||||
|
|
||||||
|
|
@ -17,19 +16,12 @@ use function add_action;
|
||||||
$context = Timber::context();
|
$context = Timber::context();
|
||||||
$templates = ['contact.twig'];
|
$templates = ['contact.twig'];
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-contact',
|
handle: 'haiku-atelier-2024-styles-page-contact',
|
||||||
path: '/assets/css/pages/page-contact.css',
|
path: '/assets/css/pages/page-contact.css',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
// Rendu
|
// Rendu
|
||||||
Timber::render(
|
Timber::render(
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Timber\Timber;
|
use Timber\Timber;
|
||||||
|
|
||||||
|
|
@ -17,19 +16,12 @@ use function add_action;
|
||||||
$context = Timber::context();
|
$context = Timber::context();
|
||||||
$templates = ['echec-commande.twig'];
|
$templates = ['echec-commande.twig'];
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-modele-simple',
|
handle: 'haiku-atelier-2024-styles-page-modele-simple',
|
||||||
path: '/assets/css/pages/page-modele-simple.css',
|
path: '/assets/css/pages/page-modele-simple.css',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
// Rendu
|
// Rendu
|
||||||
Timber::render(
|
Timber::render(
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,20 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Timber\Timber;
|
use Timber\Timber;
|
||||||
|
|
||||||
|
use function add_action;
|
||||||
|
|
||||||
$context = Timber::context();
|
$context = Timber::context();
|
||||||
$templates = ['cgv.twig'];
|
$templates = ['cgv.twig'];
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: '/assets/css/pages/page-modele-simple.css',
|
handle: '/assets/css/pages/page-modele-simple.css',
|
||||||
path: '/assets/css/pages/page-modele-simple.css',
|
path: '/assets/css/pages/page-modele-simple.css',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
// Rendu
|
// Rendu
|
||||||
Timber::render(
|
Timber::render(
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ use function add_action;
|
||||||
use function assert;
|
use function assert;
|
||||||
use function collect;
|
use function collect;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function is_bool;
|
|
||||||
use function recupere_produits_meme_collection;
|
use function recupere_produits_meme_collection;
|
||||||
use function wc_get_product;
|
use function wc_get_product;
|
||||||
use function wp_json_encode;
|
use function wp_json_encode;
|
||||||
|
|
@ -56,12 +55,7 @@ $context['product_json'] = wp_json_encode($product);
|
||||||
$context['maximum_price'] = $maximum_price;
|
$context['maximum_price'] = $maximum_price;
|
||||||
$context['same_collection_products'] = $same_collection_products;
|
$context['same_collection_products'] = $same_collection_products;
|
||||||
|
|
||||||
/**
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_script_module_file(
|
Resource::enqueue_script_module_file(
|
||||||
id: 'haiku-atelier-2024-scripts-page-produit',
|
id: 'haiku-atelier-2024-scripts-page-produit',
|
||||||
path: '/assets/js/scripts-page-produit.js',
|
path: '/assets/js/scripts-page-produit.js',
|
||||||
|
|
@ -70,9 +64,7 @@ function load_page_resources(): void {
|
||||||
id: 'haiku-atelier-2024-scripts-menu-categories',
|
id: 'haiku-atelier-2024-scripts-menu-categories',
|
||||||
path: '/assets/js/scripts-menu-categories.js',
|
path: '/assets/js/scripts-menu-categories.js',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
// Rendu
|
// Rendu
|
||||||
Timber::render(
|
Timber::render(
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -8,6 +8,8 @@ use Illuminate\Support\Arr;
|
||||||
use WC_Product_Attribute;
|
use WC_Product_Attribute;
|
||||||
use WP_Term;
|
use WP_Term;
|
||||||
|
|
||||||
|
use function wc_attribute_label;
|
||||||
|
|
||||||
final readonly class Attribute {
|
final readonly class Attribute {
|
||||||
/**
|
/**
|
||||||
* @param list<AttributeOption> $options
|
* @param list<AttributeOption> $options
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,17 @@ use Psl\Option;
|
||||||
use WC_Product;
|
use WC_Product;
|
||||||
use WP_Term;
|
use WP_Term;
|
||||||
|
|
||||||
|
use function HaikuAtelier\genere_balise_img_multiformats;
|
||||||
|
use function head;
|
||||||
use function Psl\Option\from_nullable;
|
use function Psl\Option\from_nullable;
|
||||||
|
use function wpautop;
|
||||||
|
|
||||||
final readonly class Product {
|
final readonly class Product {
|
||||||
/**
|
/**
|
||||||
* @param list<Attribute> $attributes
|
* @param list<Attribute> $attributes
|
||||||
* @param list<string> $left_column_photos
|
* @param list<string> $left_column_photos
|
||||||
* @param list<string> $right_column_photos
|
* @param list<string> $right_column_photos
|
||||||
* @param list<ProductVariation> $variations
|
* @param array<ProductVariation> $variations
|
||||||
*/
|
*/
|
||||||
private function __construct(
|
private function __construct(
|
||||||
public array $attributes,
|
public array $attributes,
|
||||||
|
|
@ -43,11 +46,11 @@ final readonly class Product {
|
||||||
*/
|
*/
|
||||||
public static function get_attributes_for_product(WC_Product $product): array {
|
public static function get_attributes_for_product(WC_Product $product): array {
|
||||||
/** @var list<Attribute> */
|
/** @var list<Attribute> */
|
||||||
return $product->get_attributes() |> (static fn($attributes) => Arr::map($attributes, Attribute::new(...)));
|
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 new(WC_Product $product): self {
|
||||||
/** @var list<Attribute> */
|
|
||||||
$attributes = self::get_attributes_for_product($product);
|
$attributes = self::get_attributes_for_product($product);
|
||||||
/** @var lowercase-string */
|
/** @var lowercase-string */
|
||||||
$category = $product->get_id() |> wc_get_product_category_list(...) |> strtolower(...);
|
$category = $product->get_id() |> wc_get_product_category_list(...) |> strtolower(...);
|
||||||
|
|
@ -74,9 +77,13 @@ final readonly class Product {
|
||||||
$hover_photo = $right_column_photos[0] ?? genere_balise_img_multiformats('-1', true);
|
$hover_photo = $right_column_photos[0] ?? genere_balise_img_multiformats('-1', true);
|
||||||
$slug = $product->get_slug();
|
$slug = $product->get_slug();
|
||||||
$stock = $product->get_stock_quantity() ?? 1;
|
$stock = $product->get_stock_quantity() ?? 1;
|
||||||
|
/** @var array<ProductVariation> */
|
||||||
$variations = $product->get_children()
|
$variations = $product->get_children()
|
||||||
|> (static fn($ids) => Arr::map($ids, wc_get_product(...)))
|
|> (static fn(/** @var list<int> */ array $ids): array => Arr::map($ids, wc_get_product(...)))
|
||||||
|> (static fn($products) => Arr::map($products, ProductVariation::new(...)));
|
|> (static fn(/** @var list<WC_Product> */ array $products): array => Arr::map(
|
||||||
|
$products,
|
||||||
|
ProductVariation::new(...),
|
||||||
|
));
|
||||||
$url = $product->get_permalink();
|
$url = $product->get_permalink();
|
||||||
|
|
||||||
return new self(
|
return new self(
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Définis les fonctionnalités du thème et personnalise certaines existantes.
|
* Définis les fonctionnalités du thème et personnalise certaines existantes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
// Désactive divers transformations du contenu par WordPress
|
// Désactive divers transformations du contenu par WordPress
|
||||||
function desactive_wpautop(): void {
|
function desactive_wpautop(): void {
|
||||||
|
|
@ -41,7 +43,7 @@ function autorise_import_svg_mediatheque(array $file_types): array {
|
||||||
$new_filetypes = [];
|
$new_filetypes = [];
|
||||||
$new_filetypes['svg'] = 'image/svg+xml';
|
$new_filetypes['svg'] = 'image/svg+xml';
|
||||||
|
|
||||||
return array_merge($file_types, $new_filetypes);
|
return [...$file_types, ...$new_filetypes];
|
||||||
}
|
}
|
||||||
|
|
||||||
function retire_motifs_blocs_gutenberg(): void {
|
function retire_motifs_blocs_gutenberg(): void {
|
||||||
|
|
@ -56,8 +58,8 @@ function retire_styles_core_block(): void {
|
||||||
add_filter('async_update_translation', '__return_false');
|
add_filter('async_update_translation', '__return_false');
|
||||||
add_filter('auto_update_translation', '__return_false');
|
add_filter('auto_update_translation', '__return_false');
|
||||||
|
|
||||||
add_action('init', 'desactive_wpautop');
|
add_action('init', desactive_wpautop(...));
|
||||||
add_filter('tiny_mce_before_init', 'desactive_transformation_contenu_tinymce');
|
add_filter('tiny_mce_before_init', desactive_transformation_contenu_tinymce(...));
|
||||||
add_filter('upload_mimes', 'autorise_import_svg_mediatheque');
|
add_filter('upload_mimes', autorise_import_svg_mediatheque(...));
|
||||||
add_action('after_setup_theme', 'retire_motifs_blocs_gutenberg');
|
add_action('after_setup_theme', retire_motifs_blocs_gutenberg(...));
|
||||||
add_action('wp_footer', 'retire_styles_core_block', 5);
|
add_action('wp_footer', retire_styles_core_block(...), 5);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Créé les Taxonomies associées aux Produits.
|
* Créé les Taxonomies associées aux Produits.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
|
use function add_action;
|
||||||
|
use function register_taxonomy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enregistre la Taxonomie « Collection ».
|
* Enregistre la Taxonomie « Collection ».
|
||||||
|
|
@ -37,4 +42,4 @@ function enregistre_taxonomie_collection(): void {
|
||||||
register_taxonomy('collection', ['product'], $args);
|
register_taxonomy('collection', ['product'], $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_action('init', 'enregistre_taxonomie_collection');
|
add_action('init', enregistre_taxonomie_collection(...));
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ declare(strict_types=1);
|
||||||
* Fonctions pour le traitement d'informations.
|
* Fonctions pour le traitement d'informations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use HaikuAtelier\Data\Attribute;
|
namespace HaikuAtelier;
|
||||||
use HaikuAtelier\Data\Product;
|
|
||||||
|
|
||||||
use function Crell\fp\pipe;
|
use function Crell\fp\pipe;
|
||||||
|
|
||||||
|
|
@ -87,118 +86,8 @@ function genere_balise_img_multiformats(string $id, bool $lazy = false): string
|
||||||
EOD;
|
EOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO.
|
|
||||||
*/
|
|
||||||
function tri_variations_par_prix_descendant(WC_Product $a, WC_Product $b): int {
|
|
||||||
return $b->get_price() <=> $a->get_price();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère les informations utilisées pour la grille des Produits et les retourne sous forme
|
|
||||||
* de tableau associatif.
|
|
||||||
*
|
|
||||||
* @return mixed un tableau avec uniquement les informations pour la Grille de Produits
|
|
||||||
*/
|
|
||||||
function recupere_informations_produit_shop(WC_Product $produit): mixed {
|
|
||||||
/** @var int $prix_maximal Le prix maximal du Produit. */
|
|
||||||
$prix_maximal = pipe(
|
|
||||||
// Récupère les Variations
|
|
||||||
$produit->get_children(),
|
|
||||||
// Récupère les informations de chaque Variation
|
|
||||||
static fn($enfants): array => array_map(
|
|
||||||
callback: wc_get_product(...),
|
|
||||||
array: $enfants,
|
|
||||||
),
|
|
||||||
// Trie les Variations par prix descendant
|
|
||||||
static fn($variations): array => array_map(
|
|
||||||
callback: static fn($variation) => $variation->get_price(),
|
|
||||||
array: $variations,
|
|
||||||
),
|
|
||||||
// Récupère le Prix de la Variation la plus chère
|
|
||||||
static fn($prix) => collect($prix)->max(),
|
|
||||||
// Récupère le Prix pour la Variation la plus chère OU le prix du Produit simple
|
|
||||||
static fn($prix_variation_maximale) => $prix_variation_maximale ?? $produit->get_price(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// TEMP: Cas de la Carte Cadeau où aucun prix ne doit être affiché. Idéalement utiliser un système d'étiquettes pour ces cas là.
|
|
||||||
if ($produit->get_sku() === 'GIFTcard') {
|
|
||||||
$prix_maximal = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
// Identifiant du Produit
|
|
||||||
'id' => $produit->get_id(),
|
|
||||||
// Nom affiché du Produit
|
|
||||||
'nom' => $produit->get_name(),
|
|
||||||
// Prix affiché du Produit
|
|
||||||
'prix' => "{$prix_maximal}",
|
|
||||||
// Photo du Produit affichée par défaut
|
|
||||||
'photo_repos' => genere_balise_img_multiformats(
|
|
||||||
get_post_meta($post_id = $produit->get_id(), $key = '_photos_colonne_gauche|||0|value')[0] ?? -1,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
// Photo du Produit affichée au survol de l'image
|
|
||||||
'photo_survol' => genere_balise_img_multiformats(
|
|
||||||
get_post_meta($post_id = $produit->get_id(), $key = '_photos_colonne_droite|||0|value')[0] ?? -1,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
// URL du Produit pour les liens vers celui-ci
|
|
||||||
'url' => $produit->get_permalink(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page Produit
|
// Page Produit
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne un tableau associatif des informations affichées sur la page Produit depuis les données brutes d'un Produit.
|
|
||||||
*/
|
|
||||||
function recupere_informations_produit_page_produit(WC_Product $product): mixed {
|
|
||||||
/** @var list<Attribute> */
|
|
||||||
$attributs = Product::get_attributes_for_product($product);
|
|
||||||
|
|
||||||
return [
|
|
||||||
// Attributs du Produit
|
|
||||||
'attributs' => $attributs,
|
|
||||||
// Catégorie du Produit
|
|
||||||
'categorie' => pipe($product->get_id(), wc_get_product_category_list(...), strtolower(...)),
|
|
||||||
// Slug de la Collection - Peut ne pas avoir été défini
|
|
||||||
'collection' => get_the_terms($product->get_id(), 'collection')[0]->slug ?? '',
|
|
||||||
// Détails (Description) du Produit
|
|
||||||
'details' => wpautop($product->get_description()),
|
|
||||||
// Identifiant du Produit
|
|
||||||
'id' => $product->get_id(),
|
|
||||||
// Nom affiché du Produit
|
|
||||||
'nom' => $product->get_name(),
|
|
||||||
// Prix affiché du Produit
|
|
||||||
'prix' => $product->get_price(),
|
|
||||||
'photos_colonne_gauche' => array_map(
|
|
||||||
callback: genere_balise_img_multiformats(...),
|
|
||||||
array: get_post_meta($post_id = $product->get_id(), $key = '_photos_colonne_gauche|||0|value'),
|
|
||||||
),
|
|
||||||
'photos_colonne_droite' => array_map(
|
|
||||||
callback: genere_balise_img_multiformats(...),
|
|
||||||
array: carbon_get_the_post_meta('photos_colonne_droite'),
|
|
||||||
),
|
|
||||||
'photo_repos' => genere_balise_img_multiformats(
|
|
||||||
get_post_meta($post_id = $product->get_id(), $key = '_photos_colonne_gauche|||0|value')[0] ?? -1,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
'photo_survol' => genere_balise_img_multiformats(
|
|
||||||
get_post_meta($post_id = $product->get_id(), $key = '_photos_colonne_droite|||0|value')[0] ?? -1,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
// Slug du Produit
|
|
||||||
'slug' => $product->get_slug(),
|
|
||||||
// Quantité de Produit en stock
|
|
||||||
'stock' => $product->get_stock_quantity() ?? 1,
|
|
||||||
// Variations du Produit
|
|
||||||
'variations_ids' => $product->get_children(),
|
|
||||||
// URL du Produit
|
|
||||||
'url' => $product->get_permalink(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère les informations utilisées pour la grille des Produits similaires (de la même
|
* 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.
|
* collection) et les retourne sous forme de tableau associatif.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ namespace HaikuAtelier\WP;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
use function carbon_get_post_meta;
|
||||||
|
use function HaikuAtelier\genere_balise_img_multiformats;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ use Psl\Option;
|
||||||
use WP_Error;
|
use WP_Error;
|
||||||
use WP_Term;
|
use WP_Term;
|
||||||
|
|
||||||
|
use function get_post_meta;
|
||||||
|
use function get_the_terms;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function Psl\Option\none;
|
use function Psl\Option\none;
|
||||||
use function Psl\Option\some;
|
use function Psl\Option\some;
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,17 @@ namespace HaikuAtelier\WP;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
|
use function filemtime;
|
||||||
|
use function get_template_directory;
|
||||||
|
use function get_template_directory_uri;
|
||||||
use function is_bool;
|
use function is_bool;
|
||||||
|
use function wp_enqueue_script_module;
|
||||||
|
use function wp_enqueue_style;
|
||||||
|
|
||||||
final readonly class Resource {
|
final readonly class Resource {
|
||||||
|
/**
|
||||||
|
* @throws Exception Lève une `Exception` s'il est impossible d'obtenir les attributs du fichier au chemin passé en paramètre.
|
||||||
|
*/
|
||||||
public static function enqueue_script_module_file(string $path, string $id): void {
|
public static function enqueue_script_module_file(string $path, string $id): void {
|
||||||
$file_uri = get_template_directory_uri() . $path;
|
$file_uri = get_template_directory_uri() . $path;
|
||||||
|
|
||||||
|
|
@ -16,7 +24,7 @@ final readonly class Resource {
|
||||||
$file_mtime = filemtime($file_path);
|
$file_mtime = filemtime($file_path);
|
||||||
|
|
||||||
if (is_bool($file_mtime)) {
|
if (is_bool($file_mtime)) {
|
||||||
throw new Exception("Could not get modification time of file: {$file_uri} ");
|
throw new Exception("Impossible de récupérer la date de modification du fichier : {$file_uri}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$version = (string) $file_mtime;
|
$version = (string) $file_mtime;
|
||||||
|
|
@ -29,6 +37,9 @@ final readonly class Resource {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception Lève une `Exception` s'il est impossible d'obtenir les attributs du fichier au chemin passé en paramètre.
|
||||||
|
*/
|
||||||
public static function enqueue_style_file(string $path, string $handle): void {
|
public static function enqueue_style_file(string $path, string $handle): void {
|
||||||
$file_uri = get_template_directory_uri() . $path;
|
$file_uri = get_template_directory_uri() . $path;
|
||||||
|
|
||||||
|
|
@ -36,7 +47,7 @@ final readonly class Resource {
|
||||||
$file_mtime = filemtime($file_path);
|
$file_mtime = filemtime($file_path);
|
||||||
|
|
||||||
if (is_bool($file_mtime)) {
|
if (is_bool($file_mtime)) {
|
||||||
throw new Exception("Could not get modification time of file: {$file_uri} ");
|
throw new Exception("Impossible de récupérer la date de modification du fichier : {$file_uri}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$ver = (string) $file_mtime;
|
$ver = (string) $file_mtime;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ button {
|
||||||
|
|
||||||
&.bouton-retour-haut {
|
&.bouton-retour-haut {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
z-index: 500;
|
||||||
right: var(--espace-xl);
|
right: var(--espace-xl);
|
||||||
bottom: calc(var(--espace-l) + var(--pied-de-page-hauteur));
|
bottom: calc(var(--espace-l) + var(--pied-de-page-hauteur));
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
|
|
@ -80,7 +81,6 @@ button {
|
||||||
background: var(--couleur-fond);
|
background: var(--couleur-fond);
|
||||||
box-shadow: initial;
|
box-shadow: initial;
|
||||||
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
|
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
|
||||||
z-index: 500;
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Option, pipe } from "effect";
|
import { Option, pipe } from "effect";
|
||||||
import { Array as EffectArray } from "effect";
|
import { Array as FxArray } from "effect";
|
||||||
import { NonEmptyReadonlyArray } from "effect/Array";
|
import { NonEmptyReadonlyArray } from "effect/Array";
|
||||||
import { getOptionOrThrowWithError } from "./utils";
|
import { getOptionOrThrowWithError } from "./utils";
|
||||||
|
|
||||||
|
|
@ -27,8 +27,8 @@ export const getAllSelectorFromParent =
|
||||||
pipe(
|
pipe(
|
||||||
parent.querySelectorAll<E>(selector),
|
parent.querySelectorAll<E>(selector),
|
||||||
// Convertis NodeListOf en Array.
|
// Convertis NodeListOf en Array.
|
||||||
Array.from<E>,
|
(xs: NodeListOf<E>) => Array.from<E>(xs),
|
||||||
(xs: Array<E>) => Option.liftPredicate(EffectArray.isNonEmptyReadonlyArray)(xs),
|
(xs: Array<E>) => Option.liftPredicate(FxArray.isReadonlyArrayNonEmpty)(xs),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getAllSelectorFromDocument = <E extends Element = Element>(
|
export const getAllSelectorFromDocument = <E extends Element = Element>(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { pipe, Option } from "effect";
|
import { Option, pipe } from "effect";
|
||||||
|
|
||||||
export const getOptionOrThrowWithError =
|
export const getOptionOrThrowWithError =
|
||||||
(message: string) =>
|
(message: string) =>
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,18 @@
|
||||||
import { ATTRIBUT_CHARGEMENT } from "../constantes/dom";
|
import { ATTRIBUT_CHARGEMENT } from "../constantes/dom";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
interface AnimationCycleTexte {
|
type AnimationCycleTexte = {
|
||||||
callback: () => void;
|
callback: () => void;
|
||||||
etapes: Array<string>;
|
etapes: Array<string>;
|
||||||
index: number;
|
index: number;
|
||||||
interval: NodeJS.Timeout;
|
interval: NodeJS.Timeout;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ParametresAnimationCycleTexte {
|
type ParametresAnimationCycleTexte = {
|
||||||
attribut: string;
|
attribut: string;
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
etapes: Array<string>;
|
etapes: Array<string>;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Créer le nécessaire pour une animation s'exécutant jusqu'à ce que un interval soit manuellement arrêté. L'animation
|
* Créer le nécessaire pour une animation s'exécutant jusqu'à ce que un interval soit manuellement arrêté. L'animation
|
||||||
|
|
@ -39,7 +39,7 @@ const lanceAnimationCycleTexte = (args: ParametresAnimationCycleTexte): Animatio
|
||||||
},
|
},
|
||||||
etapes: args.etapes,
|
etapes: args.etapes,
|
||||||
index: 0,
|
index: 0,
|
||||||
interval: setInterval(() => {}, 2147483647),
|
interval: setInterval(() => {}, 2_147_483_647),
|
||||||
};
|
};
|
||||||
|
|
||||||
return animation;
|
return animation;
|
||||||
|
|
|
||||||
|
|
@ -123,8 +123,12 @@ export const reporteEtLeveErreur = <E extends Error>(erreur: E): never => {
|
||||||
export const reporteEtJournaliseErreur = <E extends Error>(erreur: E): void => {
|
export const reporteEtJournaliseErreur = <E extends Error>(erreur: E): void => {
|
||||||
reporteErreur(erreur);
|
reporteErreur(erreur);
|
||||||
console.error(erreur);
|
console.error(erreur);
|
||||||
if (erreur instanceof ValiError) console.error(erreur.issues);
|
if (erreur instanceof ValiError) {
|
||||||
if (erreur instanceof ErreurAdresseInvalide) console.error(erreur.problemes);
|
console.error(erreur.issues);
|
||||||
|
}
|
||||||
|
if (erreur instanceof ErreurAdresseInvalide) {
|
||||||
|
console.error(erreur.problemes);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reporteEtRetourneErreur = <E extends Error>(erreur: E): E => {
|
export const reporteEtRetourneErreur = <E extends Error>(erreur: E): E => {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ export const CODE_PROMO_MAJ_EVENT = new CustomEvent(CODE_PROMO_MAJ, {});
|
||||||
|
|
||||||
// Interfaces
|
// Interfaces
|
||||||
|
|
||||||
export interface UpdatedShippingRatesEvent extends Event {
|
export type UpdatedShippingRatesEvent = {
|
||||||
detail: { refresh_methods: boolean; shipping_rates: ReadonlyArray<WCStoreShippingRateShippingRate> };
|
detail: { refresh_methods: boolean; shipping_rates: ReadonlyArray<WCStoreShippingRateShippingRate> };
|
||||||
}
|
} & Event;
|
||||||
export interface UpdatedTotalsEvent extends Event {
|
export type UpdatedTotalsEvent = {
|
||||||
detail: { totals: WCStoreCartTotals };
|
detail: { totals: WCStoreCartTotals };
|
||||||
}
|
} & Event;
|
||||||
|
|
||||||
// Méthodes
|
// Méthodes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { pipe } from "@mobily/ts-belt";
|
import { pipe } from "@mobily/ts-belt";
|
||||||
import { Either } from "purify-ts";
|
import { Either } from "purify-ts";
|
||||||
import { parse, type ValiError } from "valibot";
|
import { parse } from "valibot";
|
||||||
|
import type { ValiError } from "valibot";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
MessageMajBoutonPanier,
|
MessageMajBoutonPanier,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type ArgumentsPostBackendWC = {
|
||||||
route: string;
|
route: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// fetch
|
// Fetch
|
||||||
|
|
||||||
export const getBackend = (args: ArgumentsGetBackendWC): Promise<Response> =>
|
export const getBackend = (args: ArgumentsGetBackendWC): Promise<Response> =>
|
||||||
fetch(args.route, {
|
fetch(args.route, {
|
||||||
|
|
@ -120,19 +120,16 @@ export const safeFetch = (f: Promise<Response>): EitherAsync<DOMException | Type
|
||||||
EitherAsync<DOMException | TypeError, Response>(async () => await f);
|
EitherAsync<DOMException | TypeError, Response>(async () => await f);
|
||||||
|
|
||||||
// Réponses Simplifiées
|
// Réponses Simplifiées
|
||||||
export const newPartialResponse = async (reponse: Response): Promise<SimplifiedResponse> => {
|
export const newPartialResponse = async (reponse: Response): Promise<SimplifiedResponse> => ({
|
||||||
return {
|
|
||||||
body: await reponse.json(),
|
body: await reponse.json(),
|
||||||
status: reponse.status,
|
status: reponse.status,
|
||||||
};
|
});
|
||||||
};
|
|
||||||
|
|
||||||
export const traiteErreursBackendWooCommerce = (rs: SimplifiedResponse): HttpCodeErrors => {
|
export const traiteErreursBackendWooCommerce = (rs: SimplifiedResponse): HttpCodeErrors =>
|
||||||
return match(rs)
|
match(rs)
|
||||||
.with({ status: 400 }, () => new BadRequestError())
|
.with({ status: 400 }, () => new BadRequestError())
|
||||||
.with({ status: 401 }, () => new UnauthorizedError())
|
.with({ status: 401 }, () => new UnauthorizedError())
|
||||||
.with({ status: 403 }, () => new ForbiddenError())
|
.with({ status: 403 }, () => new ForbiddenError())
|
||||||
.with({ status: 404 }, () => new NotFoundError())
|
.with({ status: 404 }, () => new NotFoundError())
|
||||||
.with({ status: 500 }, () => new ServerError())
|
.with({ status: 500 }, () => new ServerError())
|
||||||
.otherwise((rs) => new Error(String(rs.status)));
|
.otherwise((rs) => new Error(String(rs.status)));
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ export const WCStoreBillingAddressSchema = v.object({
|
||||||
city: v.string(),
|
city: v.string(),
|
||||||
company: v.string(),
|
company: v.string(),
|
||||||
country: v.string(),
|
country: v.string(),
|
||||||
// email: v.optional(v.pipe(v.string(), v.email())),
|
// Email: v.optional(v.pipe(v.string(), v.email())),
|
||||||
email: v.string(),
|
email: v.string(),
|
||||||
first_name: v.string(),
|
first_name: v.string(),
|
||||||
last_name: v.string(),
|
last_name: v.string(),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ import type { GenericSchema, InferOutput, ValiError } from "valibot";
|
||||||
import { Either, Maybe } from "purify-ts";
|
import { Either, Maybe } from "purify-ts";
|
||||||
|
|
||||||
import { safeJsonParse } from "./dom.ts";
|
import { safeJsonParse } from "./dom.ts";
|
||||||
import { ErreurEntreeInexistante, type NonExistingKeyError } from "./erreurs.ts";
|
import { ErreurEntreeInexistante } from "./erreurs.ts";
|
||||||
|
import type { NonExistingKeyError } from "./erreurs.ts";
|
||||||
import { safeSchemaParse, safeSchemaParseCurried } from "./validation.ts";
|
import { safeSchemaParse, safeSchemaParseCurried } from "./validation.ts";
|
||||||
|
|
||||||
export type GetSessionStorage<S extends GenericSchema> = Either<ErreursGetSessionStorage<S>, InferOutput<S>>;
|
export type GetSessionStorage<S extends GenericSchema> = Either<ErreursGetSessionStorage<S>, InferOutput<S>>;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export type FetchErrors = DOMException | Error | TypeError;
|
||||||
|
|
||||||
export type HttpCodeErrors = BadRequestError | Error | ForbiddenError | NotFoundError | ServerError | UnauthorizedError;
|
export type HttpCodeErrors = BadRequestError | Error | ForbiddenError | NotFoundError | ServerError | UnauthorizedError;
|
||||||
|
|
||||||
export interface SimplifiedResponse {
|
export type SimplifiedResponse = {
|
||||||
body: unknown;
|
body: unknown;
|
||||||
status: number;
|
status: number;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { D } from "@mobily/ts-belt";
|
import { D } from "@mobily/ts-belt";
|
||||||
import { type Either, Maybe } from "purify-ts";
|
import { Maybe } from "purify-ts";
|
||||||
|
import type { Either } from "purify-ts";
|
||||||
|
|
||||||
import { CleNonTrouveError } from "./erreurs";
|
import { CleNonTrouveError } from "./erreurs";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Either } from "purify-ts";
|
import { Either } from "purify-ts";
|
||||||
import { type GenericSchema, type InferOutput, parse, type ValiError } from "valibot";
|
import { parse } from "valibot";
|
||||||
|
import type { GenericSchema, InferOutput, ValiError } from "valibot";
|
||||||
|
|
||||||
export const safeSchemaParse = <Schema extends GenericSchema>(
|
export const safeSchemaParse = <Schema extends GenericSchema>(
|
||||||
valeur: unknown,
|
valeur: unknown,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import { map as dictMap, values as dictValues } from "@mobily/ts-belt/Dict";
|
||||||
import { trim as stringTrim } from "@mobily/ts-belt/String";
|
import { trim as stringTrim } from "@mobily/ts-belt/String";
|
||||||
import { EitherAsync, Maybe } from "purify-ts";
|
import { EitherAsync, Maybe } from "purify-ts";
|
||||||
import { match, P } from "ts-pattern";
|
import { match, P } from "ts-pattern";
|
||||||
import { type AnySchema, ValiError } from "valibot";
|
import { ValiError } from "valibot";
|
||||||
|
import type { AnySchema } from "valibot";
|
||||||
|
|
||||||
import type { WCStoreBillingAddress, WCStoreShippingAddress } from "../lib/types/api/adresses";
|
import type { WCStoreBillingAddress, WCStoreShippingAddress } from "../lib/types/api/adresses";
|
||||||
import type { WCStoreCart, WCStoreShippingRate, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
import type { WCStoreCart, WCStoreShippingRate, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
||||||
|
|
@ -43,10 +44,10 @@ import { safeSchemaParse } from "../lib/validation";
|
||||||
import { E } from "./scripts-page-panier-elements";
|
import { E } from "./scripts-page-panier-elements";
|
||||||
import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
|
import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
|
||||||
|
|
||||||
interface Addresses {
|
type Addresses = {
|
||||||
billing_address: WCStoreBillingAddress;
|
billing_address: WCStoreBillingAddress;
|
||||||
shipping_address: WCStoreShippingAddress;
|
shipping_address: WCStoreShippingAddress;
|
||||||
}
|
};
|
||||||
|
|
||||||
// @ts-expect-error -- États injectés par le modèle PHP
|
// @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
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
|
||||||
|
|
@ -65,8 +66,7 @@ export const initCartFormEventEmitters = (): void => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAddressesFromForm = (formFields: Record<string, string>, areAddressesMerged: boolean): Addresses => {
|
export const getAddressesFromForm = (formFields: Record<string, string>, areAddressesMerged: boolean): Addresses => ({
|
||||||
return {
|
|
||||||
billing_address: {
|
billing_address: {
|
||||||
address_1: formFields["facturation-adresse"] ?? formFields["livraison-adresse"] ?? "",
|
address_1: formFields["facturation-adresse"] ?? formFields["livraison-adresse"] ?? "",
|
||||||
address_2: "",
|
address_2: "",
|
||||||
|
|
@ -92,8 +92,7 @@ export const getAddressesFromForm = (formFields: Record<string, string>, areAddr
|
||||||
postcode: formFields["livraison-code-postal"] ?? "",
|
postcode: formFields["livraison-code-postal"] ?? "",
|
||||||
state: formFields["livraison-region-etat"] ?? "",
|
state: formFields["livraison-region-etat"] ?? "",
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
};
|
|
||||||
|
|
||||||
export const initShippingCalculationButton = (): void => {
|
export const initShippingCalculationButton = (): void => {
|
||||||
// Déclenche au clic sur le Bouton de soumission du Formulaire la requête pour le calcul des frais de livraison
|
// Déclenche au clic sur le Bouton de soumission du Formulaire la requête pour le calcul des frais de livraison
|
||||||
|
|
@ -119,11 +118,11 @@ export const initShippingCalculationButton = (): void => {
|
||||||
void EitherAsync.liftEither(safeSchemaParse(formArgs, WCStoreCartUpdateCustomerArgsSchema))
|
void EitherAsync.liftEither(safeSchemaParse(formArgs, WCStoreCartUpdateCustomerArgsSchema))
|
||||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||||
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
|
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
|
||||||
.chain((args: WCStoreCartUpdateCustomerArgs) => {
|
.chain((args: WCStoreCartUpdateCustomerArgs) =>
|
||||||
return safeFetch(postBackend(ROUTE_API_MAJ_CLIENT, JSON.stringify(args), false));
|
safeFetch(postBackend(ROUTE_API_MAJ_CLIENT, JSON.stringify(args), false)),
|
||||||
})
|
)
|
||||||
.chain((rs: Response) => {
|
.chain((rs: Response) =>
|
||||||
return EitherAsync<ErreurAdresseInvalide | HttpCodeErrors, unknown>(
|
EitherAsync<ErreurAdresseInvalide | HttpCodeErrors, unknown>(
|
||||||
async ({ throwE }): Promise<unknown> =>
|
async ({ throwE }): Promise<unknown> =>
|
||||||
match(await newPartialResponse(rs))
|
match(await newPartialResponse(rs))
|
||||||
.with({ status: 200 }, (rs): unknown => rs.body)
|
.with({ status: 200 }, (rs): unknown => rs.body)
|
||||||
|
|
@ -135,8 +134,8 @@ export const initShippingCalculationButton = (): void => {
|
||||||
(rs): never => throwE(new ErreurAdresseInvalide(rs.body.data.params)),
|
(rs): never => throwE(new ErreurAdresseInvalide(rs.body.data.params)),
|
||||||
)
|
)
|
||||||
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs))),
|
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs))),
|
||||||
);
|
),
|
||||||
})
|
)
|
||||||
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCStoreCartSchema)))
|
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCStoreCartSchema)))
|
||||||
.ifRight((cart: WCStoreCart): void => {
|
.ifRight((cart: WCStoreCart): void => {
|
||||||
/** La méthode de livraison sélectionnée dans le SessionStorage */
|
/** La méthode de livraison sélectionnée dans le SessionStorage */
|
||||||
|
|
@ -299,9 +298,9 @@ export const initOrderCreationButton = (): void => {
|
||||||
void EitherAsync.liftEither(safeSchemaParse(formArgs, WCV3OrdersArgsSchema))
|
void EitherAsync.liftEither(safeSchemaParse(formArgs, WCV3OrdersArgsSchema))
|
||||||
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
// Désactive le Bouton pour empêcher des requêtes concurrentes
|
||||||
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
|
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
|
||||||
.chain((args: WCV3OrdersArgs) => {
|
.chain((args: WCV3OrdersArgs) =>
|
||||||
return safeFetch(postBackend(ROUTE_API_NOUVELLE_COMMANDES, JSON.stringify(args), true));
|
safeFetch(postBackend(ROUTE_API_NOUVELLE_COMMANDES, JSON.stringify(args), true)),
|
||||||
})
|
)
|
||||||
.chain((rs: Response) =>
|
.chain((rs: Response) =>
|
||||||
EitherAsync<HttpCodeErrors, unknown>(
|
EitherAsync<HttpCodeErrors, unknown>(
|
||||||
async ({ throwE }): Promise<unknown> =>
|
async ({ throwE }): Promise<unknown> =>
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ export const initialiseElementsCodePromo = (): void => {
|
||||||
);
|
);
|
||||||
|
|
||||||
window.dispatchEvent(CODE_PROMO_MAJ_EVENT);
|
window.dispatchEvent(CODE_PROMO_MAJ_EVENT);
|
||||||
// emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
|
// EmetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
|
||||||
})
|
})
|
||||||
.ifLeft((erreur) => {
|
.ifLeft((erreur) => {
|
||||||
// Rétablis le texte d'origine
|
// Rétablis le texte d'origine
|
||||||
|
|
@ -194,7 +194,9 @@ export const initialiseElementsCodePromo = (): void => {
|
||||||
)
|
)
|
||||||
.chain((reponse: Response) =>
|
.chain((reponse: Response) =>
|
||||||
EitherAsync<ServerError, unknown>(async ({ throwE }) => {
|
EitherAsync<ServerError, unknown>(async ({ throwE }) => {
|
||||||
if (estReponse500(reponse)) throwE(new ServerError("500 server Error"));
|
if (estReponse500(reponse)) {
|
||||||
|
throwE(new ServerError("500 server Error"));
|
||||||
|
}
|
||||||
return await reponse.json();
|
return await reponse.json();
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
|
import { forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
|
||||||
import { html, render, type TemplateResult } from "lit-html";
|
import { html, render } from "lit-html";
|
||||||
|
import type { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import type { WCStoreCartTotals, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
import type { WCStoreCartTotals, WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
|
||||||
import type { WCStoreShippingRateShippingRates } from "../lib/types/api/couts-livraison";
|
import type { WCStoreShippingRateShippingRates } from "../lib/types/api/couts-livraison";
|
||||||
|
|
@ -66,8 +67,9 @@ export const generateShippingRatesHTML = (
|
||||||
getDOMElementsWithSelector(container)("div[data-methode-initiale]").ifRight(arrayForEach((div) => div.remove()));
|
getDOMElementsWithSelector(container)("div[data-methode-initiale]").ifRight(arrayForEach((div) => div.remove()));
|
||||||
|
|
||||||
const selectedShippingRate: string = shippingRates.find((sr) => sr.selected)?.method_id ?? "";
|
const selectedShippingRate: string = shippingRates.find((sr) => sr.selected)?.method_id ?? "";
|
||||||
const shippingRatesHTML: ReadonlyArray<TemplateResult> = arrayMap(shippingRates, (methode) => {
|
const shippingRatesHTML: ReadonlyArray<TemplateResult> = arrayMap(
|
||||||
return html` <div>
|
shippingRates,
|
||||||
|
(methode) => html` <div>
|
||||||
<input
|
<input
|
||||||
id="methode-livraison-${methode.method_id}"
|
id="methode-livraison-${methode.method_id}"
|
||||||
name="choix-methode-livraison"
|
name="choix-methode-livraison"
|
||||||
|
|
@ -78,8 +80,8 @@ export const generateShippingRatesHTML = (
|
||||||
<label for="methode-livraison-${methode.method_id}"
|
<label for="methode-livraison-${methode.method_id}"
|
||||||
>${methode.name} (${formateEnEuros(methode.price)})</label
|
>${methode.name} (${formateEnEuros(methode.price)})</label
|
||||||
>
|
>
|
||||||
</div>`;
|
</div>`,
|
||||||
});
|
);
|
||||||
|
|
||||||
// Ajoute les nouveaux Produits dans le DOM
|
// Ajoute les nouveaux Produits dans le DOM
|
||||||
container.removeAttribute(ATTRIBUT_HIDDEN);
|
container.removeAttribute(ATTRIBUT_HIDDEN);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import { pipe } from "@mobily/ts-belt";
|
||||||
import { forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
|
import { forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
|
||||||
import { EitherAsync, Maybe } from "purify-ts";
|
import { EitherAsync, Maybe } from "purify-ts";
|
||||||
import { match, P } from "ts-pattern";
|
import { match, P } from "ts-pattern";
|
||||||
import { type AnySchema, ValiError } from "valibot";
|
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";
|
||||||
import type { WCStoreCartRemoveItemArgs } from "../lib/types/api/cart-remove-item";
|
import type { WCStoreCartRemoveItemArgs } from "../lib/types/api/cart-remove-item";
|
||||||
|
|
|
||||||
|
|
@ -59,9 +59,13 @@ const initialiseObservationFenetre = (): void => {
|
||||||
// Met à jour la valeur du défilement vertical dans la page
|
// Met à jour la valeur du défilement vertical dans la page
|
||||||
defilementY = majDefilementY();
|
defilementY = majDefilementY();
|
||||||
// Vérifie que le Ratio soit le bon
|
// Vérifie que le Ratio soit le bon
|
||||||
if (ratioActuel < RATIO_MINIMUM_PAGE_PAR_FENETRE) return;
|
if (ratioActuel < RATIO_MINIMUM_PAGE_PAR_FENETRE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Attend la prochaine étape
|
// Attend la prochaine étape
|
||||||
if (etapePlanifiee) return;
|
if (etapePlanifiee) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
etapePlanifiee = true;
|
etapePlanifiee = true;
|
||||||
requestAnimationFrame((): void =>
|
requestAnimationFrame((): void =>
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,16 @@ document.addEventListener("DOMContentLoaded", (): void => {
|
||||||
|
|
||||||
// Créé un nouvel Observer pour la première et dernière entrée.
|
// Créé un nouvel Observer pour la première et dernière entrée.
|
||||||
EffectArray.forEach(firstAndLastEntries, (menuEntry, _index) => {
|
EffectArray.forEach(firstAndLastEntries, (menuEntry, _index) => {
|
||||||
if (Predicate.isUndefined(menuEntry)) return;
|
if (Predicate.isUndefined(menuEntry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
new IntersectionObserver(
|
new IntersectionObserver(
|
||||||
EffectArray.forEach((intersectionEntry) => {
|
EffectArray.forEach((intersectionEntry) => {
|
||||||
// Ne déclenche rien si le scroll n'est pas horizontal
|
// Ne déclenche rien si le scroll n'est pas horizontal
|
||||||
if (intersectionEntry.boundingClientRect.top <= 0) return;
|
if (intersectionEntry.boundingClientRect.top <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Match.value([intersectionEntry.isIntersecting]).pipe(
|
Match.value([intersectionEntry.isIntersecting]).pipe(
|
||||||
Match.when([true, 0], () => productsCategoriesMenu.removeAttribute("data-entrees-presentes-debut")),
|
Match.when([true, 0], () => productsCategoriesMenu.removeAttribute("data-entrees-presentes-debut")),
|
||||||
|
|
@ -32,7 +36,7 @@ document.addEventListener("DOMContentLoaded", (): void => {
|
||||||
Match.orElse(() => {}),
|
Match.orElse(() => {}),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
{ root: null, threshold: 0.9 },
|
{ root: undefined, threshold: 0.9 },
|
||||||
).observe(menuEntry);
|
).observe(menuEntry);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ const initGestionAnimation = (): void => {
|
||||||
A.at(E.IMAGES_STORYTELLING, 0),
|
A.at(E.IMAGES_STORYTELLING, 0),
|
||||||
O.tap((img) => {
|
O.tap((img) => {
|
||||||
const options: IntersectionObserverInit = {
|
const options: IntersectionObserverInit = {
|
||||||
root: null,
|
root: undefined,
|
||||||
rootMargin: "0px",
|
rootMargin: "0px",
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -92,13 +92,13 @@ const initialisePageBoutique = (): void => {
|
||||||
)
|
)
|
||||||
// 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<APIFetchErrors, unknown>(async ({ throwE }) => {
|
EitherAsync<APIFetchErrors, unknown>(async ({ throwE }) =>
|
||||||
return 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 Server Error")))
|
.with({ status: 400 }, () => throwE(new BadRequestError("400 Server Error")))
|
||||||
.with({ status: 200 }, (r) => r.body)
|
.with({ status: 200 }, (r) => r.body)
|
||||||
.run();
|
.run(),
|
||||||
}),
|
),
|
||||||
)
|
)
|
||||||
// 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, WCV3ProductsSchema)))
|
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCV3ProductsSchema)))
|
||||||
|
|
@ -139,12 +139,12 @@ const initialisePageBoutique = (): void => {
|
||||||
</figure>
|
</figure>
|
||||||
</article>
|
</article>
|
||||||
`,
|
`,
|
||||||
tap((article) => fragment.appendChild(article)),
|
tap((article) => fragment.append(article)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ajoute les nouveaux Produits dans le DOM
|
// Ajoute les nouveaux Produits dans le DOM
|
||||||
E.GRILLE_PRODUITS.appendChild(fragment);
|
E.GRILLE_PRODUITS.append(fragment);
|
||||||
E.GRILLE_PRODUITS.setAttribute(ATTRIBUT_PAGE, String(nouveauNumeroPage));
|
E.GRILLE_PRODUITS.setAttribute(ATTRIBUT_PAGE, String(nouveauNumeroPage));
|
||||||
|
|
||||||
E.BOUTON_PLUS_DE_PRODUITS.textContent = "Show more";
|
E.BOUTON_PLUS_DE_PRODUITS.textContent = "Show more";
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import {
|
||||||
} from "./constantes/dom.ts";
|
} from "./constantes/dom.ts";
|
||||||
import { NOM_CANAL_BOUTON_PANIER, NOM_CANAL_CONTENU_PANIER } from "./constantes/messages.ts";
|
import { NOM_CANAL_BOUTON_PANIER, NOM_CANAL_CONTENU_PANIER } from "./constantes/messages.ts";
|
||||||
import { getDOMElementsWithSelector, recupereElementAvecSelecteur, recupereElementOuLeve } from "./lib/dom.ts";
|
import { getDOMElementsWithSelector, recupereElementAvecSelecteur, recupereElementOuLeve } from "./lib/dom.ts";
|
||||||
import { type CleNonTrouveError, reporteErreur } from "./lib/erreurs.ts";
|
import { reporteErreur } from "./lib/erreurs.ts";
|
||||||
|
import type { CleNonTrouveError } from "./lib/erreurs.ts";
|
||||||
import { valideMessageMajBoutonPanier, valideMessageMajContenuPanier } from "./lib/messages.ts";
|
import { valideMessageMajBoutonPanier, valideMessageMajContenuPanier } from "./lib/messages.ts";
|
||||||
import { arrondisADeuxDecimales, diviseParCent, formateEnEuros, inverseNombre } from "./lib/nombres.ts";
|
import { arrondisADeuxDecimales, diviseParCent, formateEnEuros, inverseNombre } from "./lib/nombres.ts";
|
||||||
import { propEither } from "./lib/utils.ts";
|
import { propEither } from "./lib/utils.ts";
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import { tap as optionTap } from "@mobily/ts-belt/Option";
|
||||||
import { pipe as epipe } from "effect";
|
import { pipe as epipe } from "effect";
|
||||||
import { EitherAsync, Maybe } from "purify-ts";
|
import { EitherAsync, Maybe } from "purify-ts";
|
||||||
import { match, P } from "ts-pattern";
|
import { match, P } from "ts-pattern";
|
||||||
import { type AnySchema, ValiError } from "valibot";
|
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";
|
||||||
import type { WCStoreCartAddItemArgs, WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.ts";
|
import type { WCStoreCartAddItemArgs, WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.ts";
|
||||||
|
|
@ -79,8 +80,12 @@ const gereAccordeonDetailsProduit = (): void => {
|
||||||
const idContenu: null | string = bouton.getAttribute(ATTRIBUT_ARIA_CONTROLS);
|
const idContenu: null | string = bouton.getAttribute(ATTRIBUT_ARIA_CONTROLS);
|
||||||
const sectionCorrespondante: HTMLDivElement | undefined = E.CONTENUS_ACCORDEON[index];
|
const sectionCorrespondante: HTMLDivElement | undefined = E.CONTENUS_ACCORDEON[index];
|
||||||
|
|
||||||
if (!idContenu) throw new Error("Le lien ne dispose pas d'ID !");
|
if (!idContenu) {
|
||||||
if (!sectionCorrespondante) throw new Error("Le lien ne dispose pas de section correspondante !");
|
throw new Error("Le lien ne dispose pas d'ID !");
|
||||||
|
}
|
||||||
|
if (!sectionCorrespondante) {
|
||||||
|
throw new Error("Le lien ne dispose pas de section correspondante !");
|
||||||
|
}
|
||||||
|
|
||||||
contenus.set(idContenu, [bouton, sectionCorrespondante]);
|
contenus.set(idContenu, [bouton, sectionCorrespondante]);
|
||||||
|
|
||||||
|
|
@ -93,7 +98,9 @@ const gereAccordeonDetailsProduit = (): void => {
|
||||||
pipe(contenus.values(), Array.from<EnsembleLienContenu>, deplieToutesSections);
|
pipe(contenus.values(), Array.from<EnsembleLienContenu>, deplieToutesSections);
|
||||||
|
|
||||||
// Ne fais rien de plus si l'onglet sélectionné était le courant
|
// Ne fais rien de plus si l'onglet sélectionné était le courant
|
||||||
if (estAncienContenuDeplie) return;
|
if (estAncienContenuDeplie) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Ouvre le nouvel onglet sélectionné
|
// Ouvre le nouvel onglet sélectionné
|
||||||
bouton.setAttribute(ATTRIBUT_ARIA_EXPANDED, "true");
|
bouton.setAttribute(ATTRIBUT_ARIA_EXPANDED, "true");
|
||||||
|
|
@ -130,14 +137,14 @@ const getAttributesFromDom = (): ReadonlyArray<WCStoreCartAddItemArgsItems> => {
|
||||||
document.querySelectorAll<HTMLSelectElement>(".selecteur-produit select"),
|
document.querySelectorAll<HTMLSelectElement>(".selecteur-produit select"),
|
||||||
Array.from<HTMLSelectElement>,
|
Array.from<HTMLSelectElement>,
|
||||||
);
|
);
|
||||||
if (selectElements.length === 0) return [];
|
if (selectElements.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const attributes = selectElements.map((select: HTMLSelectElement) => {
|
const attributes = selectElements.map((select: HTMLSelectElement) => ({
|
||||||
return {
|
|
||||||
attribute: select.id,
|
attribute: select.id,
|
||||||
value: select.value,
|
value: select.value,
|
||||||
} satisfies WCStoreCartAddItemArgsItems;
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
};
|
};
|
||||||
|
|
@ -176,9 +183,9 @@ const ajouteProduitAuPanier = (event: MouseEvent): void => {
|
||||||
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),
|
||||||
// id: ETATS_PAGE.idProduit,
|
// Id: ETATS_PAGE.idProduit,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
// variation: getAttributeValuesFromDom(),
|
// Variation: getAttributeValuesFromDom(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Réalise la Requête et traite sa Réponse
|
// Réalise la Requête et traite sa Réponse
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
interface ImportMeta {
|
type ImportMeta = {
|
||||||
readonly env: ImportMetaEnv;
|
readonly env: ImportMetaEnv;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ImportMetaEnv {}
|
type ImportMetaEnv = {};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace HaikuAtelier;
|
namespace HaikuAtelier;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use HaikuAtelier\Data\Product;
|
use HaikuAtelier\Data\Product;
|
||||||
use HaikuAtelier\WP\Resource;
|
use HaikuAtelier\WP\Resource;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
|
@ -16,38 +15,36 @@ use Timber\Timber;
|
||||||
use WC_Product;
|
use WC_Product;
|
||||||
use WP_Term;
|
use WP_Term;
|
||||||
|
|
||||||
require_once __DIR__ . '/src/inc/TraitementInformations.php';
|
use function add_action;
|
||||||
|
use function assert;
|
||||||
|
use function get_queried_object;
|
||||||
|
use function is_array;
|
||||||
|
use function wc_get_products;
|
||||||
|
|
||||||
$context = Timber::context();
|
$context = Timber::context();
|
||||||
$templates = ['boutique.twig'];
|
$templates = ['boutique.twig'];
|
||||||
|
|
||||||
/** @var WP_Term */
|
/** @var WP_Term La Catégorie affichée. */
|
||||||
$current_term = get_queried_object();
|
$current_term = get_queried_object();
|
||||||
$category_slug = $current_term->slug;
|
|
||||||
|
|
||||||
/** @var list<WC_Product> $raw_products Les informations brutes des Produits. */
|
/** @var list<Product> Les Produits de la Catégorie affichée. */
|
||||||
$raw_products = wc_get_products([
|
$products = wc_get_products([
|
||||||
'category' => [$category_slug],
|
'category' => [$current_term->slug],
|
||||||
'limit' => 12,
|
'limit' => 12,
|
||||||
'order' => 'DESC',
|
'order' => 'DESC',
|
||||||
'orderby' => 'date',
|
'orderby' => 'date',
|
||||||
'status' => 'publish',
|
'status' => 'publish',
|
||||||
]);
|
])
|
||||||
|
|> function (/** @var list<WC_Product>|stdClass */ mixed $products): array {
|
||||||
|
assert(is_array($products), 'Les Produits de la Catégorie doivent être un tableau.');
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
|
|> (static fn(/** @var list<WC_Product> */ array $products): array => Arr::map($products, Product::new(...)));
|
||||||
|
|
||||||
/** @var list<Product> */
|
|
||||||
$products = Arr::map($raw_products, Product::new(...));
|
|
||||||
$context['products'] = $products;
|
$context['products'] = $products;
|
||||||
|
$context['category_id'] = $current_term->term_id;
|
||||||
|
|
||||||
/** @var string */
|
add_action('wp_enqueue_scripts', function (): void {
|
||||||
$products_category_id = array_shift($raw_products)?->get_category_ids()[0] ?? '';
|
|
||||||
$context['products_category_id'] = $products_category_id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Charge les scripts et styles de la page.
|
|
||||||
*
|
|
||||||
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
|
|
||||||
*/
|
|
||||||
function load_page_resources(): void {
|
|
||||||
Resource::enqueue_style_file(
|
Resource::enqueue_style_file(
|
||||||
handle: 'haiku-atelier-2024-styles-page-boutique',
|
handle: 'haiku-atelier-2024-styles-page-boutique',
|
||||||
path: '/assets/css/pages/page-boutique.css',
|
path: '/assets/css/pages/page-boutique.css',
|
||||||
|
|
@ -60,9 +57,7 @@ function load_page_resources(): void {
|
||||||
id: 'haiku-atelier-2024-scripts-menu-categories',
|
id: 'haiku-atelier-2024-scripts-menu-categories',
|
||||||
path: '/assets/js/scripts-menu-categories.js',
|
path: '/assets/js/scripts-menu-categories.js',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
|
||||||
|
|
||||||
// Rendu
|
// Rendu
|
||||||
Timber::render(
|
Timber::render(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<div
|
<div
|
||||||
class="grille-produits"
|
class="grille-produits"
|
||||||
data-page="1"
|
data-page="1"
|
||||||
{% if products_category_id %}data-id-categorie-produits="{{ products_category_id }}"{% endif %}
|
{% if category_id %}data-id-categorie-produits="{{ category_id }}"{% endif %}
|
||||||
>
|
>
|
||||||
{% if products|length > 0 %}
|
{% if products|length > 0 %}
|
||||||
{% for product in products %}
|
{% for product in products %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue