Compare commits

...

1 commit

Author SHA1 Message Date
e01cd343bc fonc: travail en cours sur scripts page Produit 2026-04-13 17:43:27 +02:00
30 changed files with 1110 additions and 994 deletions

View file

@ -7,257 +7,253 @@ use PhpCsFixer\Finder;
use PhpCsFixer\Runner; use PhpCsFixer\Runner;
$finder = new Finder()->in(__DIR__)->exclude([ $finder = new Finder()->in(__DIR__)->exclude([
'vendor', 'vendor',
'web/vendor', 'web/vendor',
'web/wp', 'web/wp',
'web/app/languages', 'web/app/languages',
'web/app/plugins', 'web/app/plugins',
'web/app/mu-plugins', 'web/app/mu-plugins',
]); ]);
return new Config() return new Config()
->setRiskyAllowed(true) ->setRiskyAllowed(true)
->setRules([ ->setRules([
'array_syntax' => ['syntax' => 'short'], 'array_syntax' => ['syntax' => 'short'],
'assign_null_coalescing_to_coalesce_equal' => true, 'assign_null_coalescing_to_coalesce_equal' => true,
'attribute_empty_parentheses' => ['use_parentheses' => true], 'attribute_empty_parentheses' => ['use_parentheses' => true],
'blank_line_after_namespace' => true, 'blank_line_after_namespace' => true,
'blank_lines_before_namespace' => ['min_line_breaks' => 1, 'max_line_breaks' => 2], 'blank_lines_before_namespace' => ['min_line_breaks' => 1, 'max_line_breaks' => 2],
'cast_spaces' => true, 'cast_spaces' => true,
'class_attributes_separation' => ['elements' => [ 'class_attributes_separation' => ['elements' => [
'case' => 'none', 'case' => 'none',
'const' => 'none', 'const' => 'none',
'method' => 'one', 'method' => 'one',
'property' => 'one', 'property' => 'one',
'trait_import' => 'none', 'trait_import' => 'none',
]], ]],
'class_reference_name_casing' => true, 'class_reference_name_casing' => true,
'clean_namespace' => true, 'clean_namespace' => true,
'combine_consecutive_issets' => true, 'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true, 'combine_consecutive_unsets' => true,
'combine_nested_dirname' => true, 'combine_nested_dirname' => true,
'comment_to_phpdoc' => true, 'comment_to_phpdoc' => true,
'constant_case' => true, 'constant_case' => true,
'date_time_immutable' => true, 'date_time_immutable' => true,
'declare_equal_normalize' => true, 'declare_equal_normalize' => true,
'declare_parentheses' => true, 'declare_parentheses' => true,
'declare_strict_types' => true, 'declare_strict_types' => true,
'dir_constant' => true, 'dir_constant' => true,
'echo_tag_syntax' => true, 'echo_tag_syntax' => true,
'encoding' => true, 'encoding' => true,
'ereg_to_preg' => true, 'ereg_to_preg' => true,
'error_suppression' => true, 'error_suppression' => true,
'explicit_indirect_variable' => true, 'explicit_indirect_variable' => true,
'explicit_string_variable' => true, 'explicit_string_variable' => true,
'final_class' => true, 'final_class' => true,
'final_internal_class' => true, 'final_internal_class' => true,
'full_opening_tag' => true, 'full_opening_tag' => true,
'fully_qualified_strict_types' => ['import_symbols' => true], 'fully_qualified_strict_types' => ['import_symbols' => true],
'function_to_constant' => true, 'function_to_constant' => true,
'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true], 'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true],
'heredoc_to_nowdoc' => true, 'heredoc_to_nowdoc' => true,
'integer_literal_case' => true, 'integer_literal_case' => true,
'lambda_not_used_import' => true, 'lambda_not_used_import' => true,
'list_syntax' => true, 'list_syntax' => true,
'logical_operators' => true, 'logical_operators' => true,
'long_to_shorthand_operator' => true, 'long_to_shorthand_operator' => true,
'lowercase_cast' => true, 'lowercase_cast' => true,
'lowercase_keywords' => true, 'lowercase_keywords' => true,
'lowercase_static_reference' => true, 'lowercase_static_reference' => true,
'magic_constant_casing' => true, 'magic_constant_casing' => true,
'magic_method_casing' => true, 'magic_method_casing' => true,
'mb_str_functions' => true, 'mb_str_functions' => true,
'modernize_strpos' => ['modernize_stripos' => true], 'modernize_strpos' => ['modernize_stripos' => true],
'modernize_types_casting' => true, 'modernize_types_casting' => true,
'modifier_keywords' => true, 'modifier_keywords' => true,
'multiline_comment_opening_closing' => true, 'multiline_comment_opening_closing' => true,
'native_constant_invocation' => true, 'native_constant_invocation' => true,
'native_function_casing' => true, 'native_function_casing' => true,
'native_function_invocation' => [ 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced', 'strict' => true],
'include' => ['@compiler_optimized'], 'native_type_declaration_casing' => true,
'scope' => 'namespaced', 'new_expression_parentheses' => true,
'strict' => true, 'no_alias_functions' => ['sets' => ['@all']],
], 'no_alias_language_construct_call' => true,
'native_type_declaration_casing' => true, 'no_alternative_syntax' => true,
'new_expression_parentheses' => true, 'no_binary_string' => true,
'no_alias_functions' => ['sets' => ['@all']], 'no_closing_tag' => true,
'no_alias_language_construct_call' => true, 'no_empty_comment' => true,
'no_alternative_syntax' => true, 'no_homoglyph_names' => true,
'no_binary_string' => true, 'no_leading_import_slash' => true,
'no_closing_tag' => true, 'no_mixed_echo_print' => ['use' => 'echo'],
'no_empty_comment' => true, 'no_multiline_whitespace_around_double_arrow' => true,
'no_homoglyph_names' => true, 'no_multiple_statements_per_line' => true,
'no_leading_import_slash' => true, 'no_null_property_initialization' => true,
'no_mixed_echo_print' => ['use' => 'echo'], 'no_php4_constructor' => true,
'no_multiline_whitespace_around_double_arrow' => true, 'no_short_bool_cast' => true,
'no_multiple_statements_per_line' => true, 'no_trailing_comma_in_singleline' => true,
'no_null_property_initialization' => true, 'no_trailing_whitespace_in_comment' => true,
'no_php4_constructor' => true, 'no_unneeded_braces' => ['namespaces' => true],
'no_short_bool_cast' => true, 'no_unneeded_control_parentheses' => ['statements' => [
'no_trailing_comma_in_singleline' => true, 'break',
'no_trailing_whitespace_in_comment' => true, 'clone',
'no_unneeded_braces' => ['namespaces' => true], 'continue',
'no_unneeded_control_parentheses' => ['statements' => [ 'echo_print',
'break', 'negative_instanceof',
'clone', 'others',
'continue', 'return',
'echo_print', 'switch_case',
'negative_instanceof', 'yield',
'others', 'yield_from',
'return', ]],
'switch_case', 'no_unneeded_final_method' => true,
'yield', 'no_unneeded_import_alias' => true,
'yield_from', 'no_unreachable_default_argument_value' => true,
]], 'no_unset_cast' => true,
'no_unneeded_final_method' => true, 'no_unset_on_property' => true,
'no_unneeded_import_alias' => true, 'no_unused_imports' => true,
'no_unreachable_default_argument_value' => true, 'no_useless_concat_operator' => true,
'no_unset_cast' => true, 'no_useless_nullsafe_operator' => true,
'no_unset_on_property' => true, 'no_useless_printf' => true,
'no_unused_imports' => true, 'no_useless_return' => true,
'no_useless_concat_operator' => true, 'no_useless_sprintf' => true,
'no_useless_nullsafe_operator' => true, 'no_whitespace_before_comma_in_array' => ['after_heredoc' => true],
'no_useless_printf' => true, 'non_printable_character' => true,
'no_useless_return' => true, 'normalize_index_brace' => true,
'no_useless_sprintf' => true, 'nullable_type_declaration' => ['syntax' => 'union'],
'no_whitespace_before_comma_in_array' => ['after_heredoc' => true], 'nullable_type_declaration_for_default_null_value' => true,
'non_printable_character' => true, 'numeric_literal_separator' => ['override_existing' => true, 'strategy' => 'use_separator'],
'normalize_index_brace' => true, 'ordered_attributes' => true,
'nullable_type_declaration' => ['syntax' => 'union'], 'ordered_class_elements' => ['case_sensitive' => false, 'sort_algorithm' => 'alpha'],
'nullable_type_declaration_for_default_null_value' => true, 'ordered_imports' => ['case_sensitive' => true],
'numeric_literal_separator' => ['override_existing' => true, 'strategy' => 'use_separator'], 'ordered_interfaces' => true,
'ordered_attributes' => true, 'ordered_traits' => true,
'ordered_class_elements' => ['case_sensitive' => false, 'sort_algorithm' => 'alpha'], 'ordered_types' => ['null_adjustment' => 'always_last'],
'ordered_imports' => ['case_sensitive' => true], 'phpdoc_readonly_class_comment_to_keyword' => true,
'ordered_interfaces' => true, 'phpdoc_to_param_type' => true,
'ordered_traits' => true, 'phpdoc_to_property_type' => true,
'ordered_types' => ['null_adjustment' => 'always_last'], 'phpdoc_to_return_type' => true,
'phpdoc_readonly_class_comment_to_keyword' => true, 'pow_to_exponentiation' => true,
'phpdoc_to_param_type' => true, 'protected_to_private' => true,
'phpdoc_to_property_type' => true, 'psr_autoloading' => true,
'phpdoc_to_return_type' => true, 'random_api_migration' => ['replacements' => [
'pow_to_exponentiation' => true, 'getrandmax' => 'mt_getrandmax',
'protected_to_private' => true, 'rand' => 'mt_rand',
'psr_autoloading' => true, 'srand' => 'mt_srand',
'random_api_migration' => ['replacements' => [ ]],
'getrandmax' => 'mt_getrandmax', 'return_assignment' => true,
'rand' => 'mt_rand', 'self_accessor' => true,
'srand' => 'mt_srand', 'self_static_accessor' => true,
]], 'set_type_to_cast' => true,
'return_assignment' => true, 'short_scalar_cast' => true,
'self_accessor' => true, 'simple_to_complex_string_variable' => true,
'self_static_accessor' => true, 'simplified_null_return' => true,
'set_type_to_cast' => true, 'single_class_element_per_statement' => true,
'short_scalar_cast' => true, 'single_import_per_statement' => true,
'simple_to_complex_string_variable' => true, 'single_line_after_imports' => true,
'simplified_null_return' => true, 'single_line_comment_spacing' => true,
'single_class_element_per_statement' => true, 'single_line_comment_style' => true,
'single_import_per_statement' => true, 'single_line_empty_body' => true,
'single_line_after_imports' => true, 'single_trait_insert_per_statement' => true,
'single_line_comment_spacing' => true, 'standardize_not_equals' => true,
'single_line_comment_style' => true, 'static_lambda' => true,
'single_line_empty_body' => true, 'strict_comparison' => true,
'single_trait_insert_per_statement' => true, 'strict_param' => true,
'standardize_not_equals' => true, 'string_implicit_backslashes' => true,
'static_lambda' => true, 'string_length_to_empty' => true,
'strict_comparison' => true, 'switch_continue_to_break' => true,
'strict_param' => true, 'ternary_to_null_coalescing' => true,
'string_implicit_backslashes' => true, 'trim_array_spaces' => true,
'string_length_to_empty' => true, 'use_arrow_functions' => true,
'switch_continue_to_break' => true, 'void_return' => true,
'ternary_to_null_coalescing' => true, 'whitespace_after_comma_in_array' => ['ensure_single_space' => true],
'trim_array_spaces' => true, // ---
'use_arrow_functions' => true, // Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one.
'void_return' => true, 'align_multiline_comment' => ['comment_type' => 'all_multiline'],
'whitespace_after_comma_in_array' => ['ensure_single_space' => true], // There should not be blank lines between docblock and the documented element.
// --- 'no_blank_lines_after_phpdoc' => true,
// Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one. // There should not be empty PHPDoc blocks.
'align_multiline_comment' => ['comment_type' => 'all_multiline'], 'no_empty_phpdoc' => true,
// There should not be blank lines between docblock and the documented element. // Removes @param, @return and @var tags that don't provide any useful information.
'no_blank_lines_after_phpdoc' => true, 'no_superfluous_phpdoc_tags' => [
// There should not be empty PHPDoc blocks. 'allow_hidden_params' => false,
'no_empty_phpdoc' => true, 'allow_mixed' => false,
// Removes @param, @return and @var tags that don't provide any useful information. 'allow_unused_params' => false,
'no_superfluous_phpdoc_tags' => [ ],
'allow_hidden_params' => false, // PHPDoc should contain @param for all params.
'allow_mixed' => false, 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false],
'allow_unused_params' => false, // All items of the given PHPDoc tags must be either left-aligned or (by default) aligned vertically.
], 'phpdoc_align' => true,
// PHPDoc should contain @param for all params. // PHPDoc annotation descriptions should not be a sentence.
'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], 'phpdoc_annotation_without_dot' => true,
// All items of the given PHPDoc tags must be either left-aligned or (by default) aligned vertically. // PHPDoc array<T> type must be used instead of T[].
'phpdoc_align' => true, 'phpdoc_array_type' => true,
// PHPDoc annotation descriptions should not be a sentence. // Docblocks should have the same indentation as the documented subject.
'phpdoc_annotation_without_dot' => true, 'phpdoc_indent' => true,
// PHPDoc array<T> type must be used instead of T[]. // Fixes PHPDoc inline tags.
'phpdoc_array_type' => true, 'phpdoc_inline_tag_normalizer' => true,
// Docblocks should have the same indentation as the documented subject. // Changes doc blocks from single to multi line, or reversed. Works for class constants, properties and methods only.
'phpdoc_indent' => true, 'phpdoc_line_span' => ['const' => 'single', 'method' => 'multi', 'property' => 'single'],
// Fixes PHPDoc inline tags. // PHPDoc list type must be used instead of array without a key.
'phpdoc_inline_tag_normalizer' => true, 'phpdoc_list_type' => false,
// Changes doc blocks from single to multi line, or reversed. Works for class constants, properties and methods only. // @access annotations must be removed from PHPDoc.
'phpdoc_line_span' => ['const' => 'single', 'method' => 'multi', 'property' => 'single'], 'phpdoc_no_access' => true,
// PHPDoc list type must be used instead of array without a key. // No alias PHPDoc tags should be used.
'phpdoc_list_type' => false, 'phpdoc_no_alias_tag' => true,
// @access annotations must be removed from PHPDoc. // @return void and @return null annotations must be removed from PHPDoc.
'phpdoc_no_access' => true, 'phpdoc_no_empty_return' => false,
// No alias PHPDoc tags should be used. // @package and @subpackage annotations must be removed from PHPDoc.
'phpdoc_no_alias_tag' => true, 'phpdoc_no_package' => true,
// @return void and @return null annotations must be removed from PHPDoc. // Classy that does not inherit must not have @inheritdoc tags.
'phpdoc_no_empty_return' => false, 'phpdoc_no_useless_inheritdoc' => true,
// @package and @subpackage annotations must be removed from PHPDoc. // Annotations in PHPDoc should be ordered in defined sequence.
'phpdoc_no_package' => true, 'phpdoc_order' => true,
// Classy that does not inherit must not have @inheritdoc tags. // Order PHPDoc tags by value.
'phpdoc_no_useless_inheritdoc' => true, 'phpdoc_order_by_value' => true,
// Annotations in PHPDoc should be ordered in defined sequence. // Orders all @param annotations in DocBlocks according to method signature.
'phpdoc_order' => true, 'phpdoc_param_order' => true,
// Order PHPDoc tags by value. // The type of @return annotations of methods returning a reference to itself must the configured one.
'phpdoc_order_by_value' => true, 'phpdoc_return_self_reference' => true,
// Orders all @param annotations in DocBlocks according to method signature. // Scalar types should always be written in the same form. int not integer, bool not boolean, float not real or double.
'phpdoc_param_order' => true, 'phpdoc_scalar' => ['types' => [
// The type of @return annotations of methods returning a reference to itself must the configured one. 'boolean',
'phpdoc_return_self_reference' => true, 'callback',
// Scalar types should always be written in the same form. int not integer, bool not boolean, float not real or double. 'double',
'phpdoc_scalar' => ['types' => [ 'integer',
'boolean', 'never-return',
'callback', 'never-returns',
'double', 'no-return',
'integer', 'real',
'never-return', 'str',
'never-returns', ]],
'no-return', // Annotations in PHPDoc should be grouped together so that annotations of the same type immediately follow each other. Annotations of a different type are separated by a single blank line.
'real', 'phpdoc_separation' => [
'str', 'groups' => [
]], ['Annotation', 'NamedArgumentConstructor', 'Target'],
// Annotations in PHPDoc should be grouped together so that annotations of the same type immediately follow each other. Annotations of a different type are separated by a single blank line. ['author', 'copyright', 'license'],
'phpdoc_separation' => [ ['category', 'package', 'subpackage'],
'groups' => [ ['property', 'property-read', 'property-write'],
['Annotation', 'NamedArgumentConstructor', 'Target'], ['deprecated', 'link', 'see', 'since'],
['author', 'copyright', 'license'], ],
['category', 'package', 'subpackage'], 'skip_unlisted_annotations' => false,
['property', 'property-read', 'property-write'], ],
['deprecated', 'link', 'see', 'since'], // Single line @var PHPDoc should have proper spacing.
], 'phpdoc_single_line_var_spacing' => true,
'skip_unlisted_annotations' => false, // PHPDoc summary should end in either a full stop, exclamation mark, or question mark.
], 'phpdoc_summary' => true,
// Single line @var PHPDoc should have proper spacing. // Docblocks should only be used on structural elements.
'phpdoc_single_line_var_spacing' => true, 'phpdoc_to_comment' => false,
// PHPDoc summary should end in either a full stop, exclamation mark, or question mark. // PHPDoc should start and end with content, excluding the very first and last line of the docblocks.
'phpdoc_summary' => true, 'phpdoc_trim' => true,
// Docblocks should only be used on structural elements. // Removes extra blank lines after summary and after description in PHPDoc.
'phpdoc_to_comment' => false, 'phpdoc_trim_consecutive_blank_line_separation' => true,
// PHPDoc should start and end with content, excluding the very first and last line of the docblocks. // The correct case must be used for standard PHP types in PHPDoc.
'phpdoc_trim' => true, 'phpdoc_types' => true,
// Removes extra blank lines after summary and after description in PHPDoc. // Sorts PHPDoc types.
'phpdoc_trim_consecutive_blank_line_separation' => true, 'phpdoc_types_order' => ['null_adjustment' => 'always_last'],
// The correct case must be used for standard PHP types in PHPDoc. // @var and @type annotations must have type and name in the correct order.
'phpdoc_types' => true, 'phpdoc_var_annotation_correct_order' => true,
// Sorts PHPDoc types. // @var and @type annotations of classy properties should not contain the name.
'phpdoc_types_order' => ['null_adjustment' => 'always_last'], 'phpdoc_var_without_name' => true,
// @var and @type annotations must have type and name in the correct order. ])
'phpdoc_var_annotation_correct_order' => true, ->setFinder($finder)
// @var and @type annotations of classy properties should not contain the name. ->setParallelConfig(Runner\Parallel\ParallelConfigFactory::detect());
'phpdoc_var_without_name' => true,
])
->setFinder($finder)
->setParallelConfig(Runner\Parallel\ParallelConfigFactory::detect());

View file

@ -8,7 +8,7 @@
"@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.5", "a11y-dialog": "^8.1.5",
"effect": "^4.0.0-beta.46", "effect": "^4.0.0-beta.48",
"lit-html": "^3.3.2", "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",
@ -34,9 +34,9 @@
"eslint-plugin-perfectionist": "^5.8.0", "eslint-plugin-perfectionist": "^5.8.0",
"eslint-plugin-sonarjs": "^4.0.2", "eslint-plugin-sonarjs": "^4.0.2",
"fdir": "^6.5.0", "fdir": "^6.5.0",
"globals": "^17.4.0", "globals": "^17.5.0",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"knip": "^6.3.1", "knip": "^6.4.1",
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"lightningcss-cli": "^1.32.0", "lightningcss-cli": "^1.32.0",
"oxlint": "^1.59.0", "oxlint": "^1.59.0",
@ -46,7 +46,7 @@
"prettier-plugin-pkg": "^0.22.1", "prettier-plugin-pkg": "^0.22.1",
"prettier-plugin-sh": "^0.18.1", "prettier-plugin-sh": "^0.18.1",
"sass-embedded": "^1.99.0", "sass-embedded": "^1.99.0",
"stylelint": "^17.6.0", "stylelint": "^17.7.0",
"stylelint-config-clean-order": "^8.0.1", "stylelint-config-clean-order": "^8.0.1",
"stylelint-config-sass-guidelines": "^13.0.0", "stylelint-config-sass-guidelines": "^13.0.0",
"stylelint-config-standard-scss": "^17.0.0", "stylelint-config-standard-scss": "^17.0.0",
@ -796,7 +796,7 @@
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"effect": ["effect@4.0.0-beta.46", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-3f6gXvvUMtEueCRY0tU76Vq2Pej1SAwwE+s0Owd5nD53yS5n4RZhUA1rlCGFuSbQFA225pGy8vO72+lpvu7u5A=="], "effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
"electron-to-chromium": ["electron-to-chromium@1.5.334", "", {}, "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog=="], "electron-to-chromium": ["electron-to-chromium@1.5.334", "", {}, "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog=="],
@ -930,7 +930,7 @@
"global-prefix": ["global-prefix@3.0.0", "", { "dependencies": { "ini": "^1.3.5", "kind-of": "^6.0.2", "which": "^1.3.1" } }, "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg=="], "global-prefix": ["global-prefix@3.0.0", "", { "dependencies": { "ini": "^1.3.5", "kind-of": "^6.0.2", "which": "^1.3.1" } }, "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg=="],
"globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="], "globals": ["globals@17.5.0", "", {}, "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g=="],
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
@ -1076,7 +1076,7 @@
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
"knip": ["knip@6.3.1", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "get-tsconfig": "4.13.7", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-parser": "^0.121.0", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-22kLJloVcOVOAudCxlFOC0ICAMme7dKsS7pVTEnrmyKGpswb8ieznvAiSKUeFVDJhb01ect6dkDc1Ha1g1sPpg=="], "knip": ["knip@6.4.1", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "get-tsconfig": "4.13.7", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-parser": "^0.121.0", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-Ry+ywmDFSZvKp/jx7LxMgsZWRTs931alV84e60lh0Stf6kSRYqSIUTkviyyDFRcSO3yY1Kpbi83OirN+4lA2Xw=="],
"known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="],
@ -1484,7 +1484,7 @@
"style-search": ["style-search@0.1.0", "", {}, "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg=="], "style-search": ["style-search@0.1.0", "", {}, "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg=="],
"stylelint": ["stylelint@17.6.0", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-syntax-patches-for-csstree": "^1.1.1", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0", "@csstools/selector-resolve-nested": "^4.0.0", "@csstools/selector-specificity": "^6.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.1", "css-functions-list": "^3.3.3", "css-tree": "^3.2.1", "debug": "^4.4.3", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^11.1.2", "global-modules": "^2.0.0", "globby": "^16.1.1", "globjoin": "^0.1.4", "html-tags": "^5.1.0", "ignore": "^7.0.5", "import-meta-resolve": "^4.2.0", "is-plain-object": "^5.0.0", "mathml-tag-names": "^4.0.0", "meow": "^14.1.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.5.8", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", "string-width": "^8.2.0", "supports-hyperlinks": "^4.4.0", "svg-tags": "^1.0.0", "table": "^6.9.0", "write-file-atomic": "^7.0.1" }, "bin": { "stylelint": "bin/stylelint.mjs" } }, "sha512-tokrsMIVAR9vAQ/q3UVEr7S0dGXCi7zkCezPRnS2kqPUulvUh5Vgfwngrk4EoAoW7wnrThqTdnTFN5Ra7CaxIg=="], "stylelint": ["stylelint@17.7.0", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-syntax-patches-for-csstree": "^1.1.2", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0", "@csstools/selector-resolve-nested": "^4.0.0", "@csstools/selector-specificity": "^6.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.1", "css-functions-list": "^3.3.3", "css-tree": "^3.2.1", "debug": "^4.4.3", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^11.1.2", "global-modules": "^2.0.0", "globby": "^16.2.0", "globjoin": "^0.1.4", "html-tags": "^5.1.0", "ignore": "^7.0.5", "import-meta-resolve": "^4.2.0", "is-plain-object": "^5.0.0", "mathml-tag-names": "^4.0.0", "meow": "^14.1.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.5.8", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", "string-width": "^8.2.0", "supports-hyperlinks": "^4.4.0", "svg-tags": "^1.0.0", "table": "^6.9.0", "write-file-atomic": "^7.0.1" }, "bin": { "stylelint": "bin/stylelint.mjs" } }, "sha512-n/+4RheCRl+cecGnF+S/Adz59iCRaK9BVznJYB+a7GOksfwNzjiOPnYv17pTO0HgRse9IiqbMtekGNhOb2tVYQ=="],
"stylelint-config-clean-order": ["stylelint-config-clean-order@8.0.1", "", { "peerDependencies": { "stylelint": ">=16", "stylelint-order": ">=6" } }, "sha512-zKjp7BiINXRZOG9m0fE/6UKoM6clPekL+LoAiHMCiQU2hgirKL5G0mKc5Z0ygIhQXfb1+DTRDM0mu6Ecdv4q8g=="], "stylelint-config-clean-order": ["stylelint-config-clean-order@8.0.1", "", { "peerDependencies": { "stylelint": ">=16", "stylelint-order": ">=6" } }, "sha512-zKjp7BiINXRZOG9m0fE/6UKoM6clPekL+LoAiHMCiQU2hgirKL5G0mKc5Z0ygIhQXfb1+DTRDM0mu6Ecdv4q8g=="],
@ -1532,7 +1532,7 @@
"to-valid-identifier": ["to-valid-identifier@1.0.0", "", { "dependencies": { "@sindresorhus/base62": "^1.0.0", "reserved-identifiers": "^1.0.0" } }, "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw=="], "to-valid-identifier": ["to-valid-identifier@1.0.0", "", { "dependencies": { "@sindresorhus/base62": "^1.0.0", "reserved-identifiers": "^1.0.0" } }, "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw=="],
"toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="],
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
@ -1632,6 +1632,10 @@
"@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/globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
"@gcch/configuration-oxlint/globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
"@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=="],
@ -1656,6 +1660,8 @@
"eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], "eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"eslint-plugin-sonarjs/globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
"eslint-plugin-unicorn/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], "eslint-plugin-unicorn/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],

118
composer.lock generated
View file

@ -3629,15 +3629,15 @@
}, },
{ {
"name": "wpackagist-plugin/query-monitor", "name": "wpackagist-plugin/query-monitor",
"version": "4.0.5", "version": "4.0.6",
"source": { "source": {
"type": "svn", "type": "svn",
"url": "https://plugins.svn.wordpress.org/query-monitor/", "url": "https://plugins.svn.wordpress.org/query-monitor/",
"reference": "tags/4.0.5" "reference": "tags/4.0.6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://downloads.wordpress.org/plugin/query-monitor.4.0.5.zip" "url": "https://downloads.wordpress.org/plugin/query-monitor.4.0.6.zip"
}, },
"require": { "require": {
"composer/installers": "^1.0 || ^2.0" "composer/installers": "^1.0 || ^2.0"
@ -4077,6 +4077,75 @@
], ],
"time": "2024-05-06T16:37:16+00:00" "time": "2024-05-06T16:37:16+00:00"
}, },
{
"name": "ergebnis/agent-detector",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/ergebnis/agent-detector.git",
"reference": "5b654a9f1ff8a5d2ce6a57568df5ae8801c87f64"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/5b654a9f1ff8a5d2ce6a57568df5ae8801c87f64",
"reference": "5b654a9f1ff8a5d2ce6a57568df5ae8801c87f64",
"shasum": ""
},
"require": {
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.50.0",
"ergebnis/license": "^2.7.0",
"ergebnis/php-cs-fixer-config": "^6.60.2",
"ergebnis/phpstan-rules": "^2.13.1",
"ergebnis/phpunit-slow-test-detector": "^2.24.0",
"ergebnis/rector-rules": "^1.16.0",
"fakerphp/faker": "^1.24.1",
"infection/infection": "^0.26.6",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.46",
"phpstan/phpstan-deprecation-rules": "^2.0.4",
"phpstan/phpstan-phpunit": "^2.0.16",
"phpstan/phpstan-strict-rules": "^2.0.10",
"phpunit/phpunit": "^9.6.34",
"rector/rector": "^2.4.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.0-dev"
},
"composer-normalize": {
"indent-size": 2,
"indent-style": "space"
}
},
"autoload": {
"psr-4": {
"Ergebnis\\AgentDetector\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andreas Möller",
"email": "am@localheinz.com",
"homepage": "https://localheinz.com"
}
],
"description": "Provides a detector for detecting the presence of an agent.",
"homepage": "https://github.com/ergebnis/agent-detector",
"support": {
"issues": "https://github.com/ergebnis/agent-detector/issues",
"security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md",
"source": "https://github.com/ergebnis/agent-detector"
},
"time": "2026-04-10T13:45:13+00:00"
},
{ {
"name": "evenement/evenement", "name": "evenement/evenement",
"version": "v3.0.2", "version": "v3.0.2",
@ -4187,22 +4256,23 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.94.2", "version": "v3.95.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "7787ceff91365ba7d623ec410b8f429cdebb4f63" "reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7787ceff91365ba7d623ec410b8f429cdebb4f63", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a9727678fbd12997f1d9de8f4a37824ed9df1065",
"reference": "7787ceff91365ba7d623ec410b8f429cdebb4f63", "reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"clue/ndjson-react": "^1.3", "clue/ndjson-react": "^1.3",
"composer/semver": "^3.4", "composer/semver": "^3.4",
"composer/xdebug-handler": "^3.0.5", "composer/xdebug-handler": "^3.0.5",
"ergebnis/agent-detector": "^1.1.1",
"ext-filter": "*", "ext-filter": "*",
"ext-hash": "*", "ext-hash": "*",
"ext-json": "*", "ext-json": "*",
@ -4227,18 +4297,18 @@
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
}, },
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7.1", "facile-it/paraunit": "^1.3.1 || ^2.8.0",
"infection/infection": "^0.32.3", "infection/infection": "^0.32.6",
"justinrainbow/json-schema": "^6.6.4", "justinrainbow/json-schema": "^6.8.0",
"keradus/cli-executor": "^2.3", "keradus/cli-executor": "^2.3",
"mikey179/vfsstream": "^1.6.12", "mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.9.1", "php-coveralls/php-coveralls": "^2.9.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.7", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.7", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8",
"phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.51", "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55",
"symfony/polyfill-php85": "^1.33", "symfony/polyfill-php85": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.4", "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.8",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.1" "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.8"
}, },
"suggest": { "suggest": {
"ext-dom": "For handling output formats in XML", "ext-dom": "For handling output formats in XML",
@ -4279,7 +4349,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.2" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.1"
}, },
"funding": [ "funding": [
{ {
@ -4287,7 +4357,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2026-02-20T16:13:53+00:00" "time": "2026-04-12T17:00:09+00:00"
}, },
{ {
"name": "php-standard-library/phpstan-extension", "name": "php-standard-library/phpstan-extension",
@ -7411,16 +7481,16 @@
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",
"version": "2.2.0", "version": "2.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/webmozarts/assert.git", "url": "https://github.com/webmozarts/assert.git",
"reference": "1b99650e7ffcad232624a260bc7fbdec2ffc407c" "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/1b99650e7ffcad232624a260bc7fbdec2ffc407c", "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4",
"reference": "1b99650e7ffcad232624a260bc7fbdec2ffc407c", "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -7467,9 +7537,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/webmozarts/assert/issues", "issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/2.2.0" "source": "https://github.com/webmozarts/assert/tree/2.3.0"
}, },
"time": "2026-04-09T16:54:47+00:00" "time": "2026-04-11T10:33:05+00:00"
} }
], ],
"aliases": [], "aliases": [],
@ -7484,5 +7554,5 @@
"php": ">=8.5" "php": ">=8.5"
}, },
"platform-dev": {}, "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.9.0"
} }

View file

@ -37,16 +37,16 @@ $webroot_dir = $root_dir . '/web';
* .env.local will override .env if it exists * .env.local will override .env if it exists
*/ */
if (file_exists($root_dir . '/.env')) { if (file_exists($root_dir . '/.env')) {
$env_files = file_exists($root_dir . '/.env.local') ? ['.env', '.env.local'] : ['.env']; $env_files = file_exists($root_dir . '/.env.local') ? ['.env', '.env.local'] : ['.env'];
$dotenv = Dotenv\Dotenv::createImmutable($root_dir, $env_files, false); $dotenv = Dotenv\Dotenv::createImmutable($root_dir, $env_files, false);
$dotenv->load(); $dotenv->load();
$dotenv->required(['WP_HOME', 'WP_SITEURL']); $dotenv->required(['WP_HOME', 'WP_SITEURL']);
if (!env('DATABASE_URL')) { if (!env('DATABASE_URL')) {
$dotenv->required(['DB_NAME', 'DB_USER', 'DB_PASSWORD']); $dotenv->required(['DB_NAME', 'DB_USER', 'DB_PASSWORD']);
} }
} }
/* /*
@ -57,7 +57,7 @@ define('WP_ENV', env('WP_ENV') ?: 'production');
// Infer WP_ENVIRONMENT_TYPE based on WP_ENV // Infer WP_ENVIRONMENT_TYPE based on WP_ENV
if (!env('WP_ENVIRONMENT_TYPE') && in_array(WP_ENV, ['production', 'staging', 'development', 'local'], true)) { if (!env('WP_ENVIRONMENT_TYPE') && in_array(WP_ENV, ['production', 'staging', 'development', 'local'], true)) {
Config::define('WP_ENVIRONMENT_TYPE', WP_ENV); Config::define('WP_ENVIRONMENT_TYPE', WP_ENV);
} }
// URLs // URLs
@ -71,7 +71,7 @@ Config::define('WP_CONTENT_URL', Config::get('WP_HOME') . Config::get('CONTENT_D
// DB settings // DB settings
if (env('DB_SSL')) { if (env('DB_SSL')) {
Config::define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL); Config::define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL);
} }
Config::define('DB_NAME', env('DB_NAME')); Config::define('DB_NAME', env('DB_NAME'));
@ -83,12 +83,12 @@ Config::define('DB_COLLATE', '');
$table_prefix = env('DB_PREFIX') ?: 'wp_'; $table_prefix = env('DB_PREFIX') ?: 'wp_';
if (env('DATABASE_URL')) { if (env('DATABASE_URL')) {
$dsn = (object) parse_url(env('DATABASE_URL')); $dsn = (object) parse_url(env('DATABASE_URL'));
Config::define('DB_NAME', mb_substr($dsn->path, 1)); Config::define('DB_NAME', mb_substr($dsn->path, 1));
Config::define('DB_USER', $dsn->user); Config::define('DB_USER', $dsn->user);
Config::define('DB_PASSWORD', $dsn->pass ?? null); Config::define('DB_PASSWORD', $dsn->pass ?? null);
Config::define('DB_HOST', isset($dsn->port) ? "{$dsn->host}:{$dsn->port}" : $dsn->host); Config::define('DB_HOST', isset($dsn->port) ? "{$dsn->host}:{$dsn->port}" : $dsn->host);
} }
// Authentication Unique Keys and Salts // Authentication Unique Keys and Salts
@ -129,18 +129,18 @@ Config::define('WP_PLUGIN_DIR', Config::get('WP_CONTENT_DIR') . '/plugins');
* See https://codex.wordpress.org/Function_Reference/is_ssl#Notes * See https://codex.wordpress.org/Function_Reference/is_ssl#Notes
*/ */
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO']) { if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO']) {
$_SERVER['HTTPS'] = 'on'; $_SERVER['HTTPS'] = 'on';
} }
$env_config = __DIR__ . '/environments/' . WP_ENV . '.php'; $env_config = __DIR__ . '/environments/' . WP_ENV . '.php';
if (file_exists($env_config)) { if (file_exists($env_config)) {
include_once $env_config; include_once $env_config;
} }
Config::apply(); Config::apply();
// Bootstrap WordPress // Bootstrap WordPress
if (!defined('ABSPATH')) { if (!defined('ABSPATH')) {
define('ABSPATH', $webroot_dir . '/wp/'); define('ABSPATH', $webroot_dir . '/wp/');
} }

View file

@ -14,7 +14,7 @@
"@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.5", "a11y-dialog": "^8.1.5",
"effect": "^4.0.0-beta.46", "effect": "^4.0.0-beta.48",
"lit-html": "^3.3.2", "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",
@ -40,9 +40,9 @@
"eslint-plugin-perfectionist": "^5.8.0", "eslint-plugin-perfectionist": "^5.8.0",
"eslint-plugin-sonarjs": "^4.0.2", "eslint-plugin-sonarjs": "^4.0.2",
"fdir": "^6.5.0", "fdir": "^6.5.0",
"globals": "^17.4.0", "globals": "^17.5.0",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"knip": "^6.3.1", "knip": "^6.4.1",
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"lightningcss-cli": "^1.32.0", "lightningcss-cli": "^1.32.0",
"oxlint": "^1.59.0", "oxlint": "^1.59.0",
@ -52,7 +52,7 @@
"prettier-plugin-pkg": "^0.22.1", "prettier-plugin-pkg": "^0.22.1",
"prettier-plugin-sh": "^0.18.1", "prettier-plugin-sh": "^0.18.1",
"sass-embedded": "^1.99.0", "sass-embedded": "^1.99.0",
"stylelint": "^17.6.0", "stylelint": "^17.7.0",
"stylelint-config-clean-order": "^8.0.1", "stylelint-config-clean-order": "^8.0.1",
"stylelint-config-sass-guidelines": "^13.0.0", "stylelint-config-sass-guidelines": "^13.0.0",
"stylelint-config-standard-scss": "^17.0.0", "stylelint-config-standard-scss": "^17.0.0",

View file

@ -14,32 +14,32 @@ global $wpdb;
$wp_postmeta = "{$wpdb->prefix}postmeta"; $wp_postmeta = "{$wpdb->prefix}postmeta";
try { try {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Error Handling // Error Handling
$sql = "UPDATE {$wp_postmeta} SET meta_value = REPLACE(meta_value,'-scaled.jpg','.jpg') WHERE meta_key='_wp_attached_file' AND meta_value LIKE '%-scaled.jpg%'"; $sql = "UPDATE {$wp_postmeta} SET meta_value = REPLACE(meta_value,'-scaled.jpg','.jpg') WHERE meta_key='_wp_attached_file' AND meta_value LIKE '%-scaled.jpg%'";
$result = $pdo->exec($sql); $result = $pdo->exec($sql);
print_r($result); print_r($result);
} catch (PDOException $e) { } catch (PDOException $e) {
print_r($e->getMessage()); print_r($e->getMessage());
} }
// replace _wp_attachment_metadata meta_key. // replace _wp_attachment_metadata meta_key.
$image_metas = []; $image_metas = [];
try { try {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Error Handling // Error Handling
$sql = "SELECT * FROM {$wp_postmeta} WHERE meta_value LIKE '%-scaled.jpg%' AND meta_key='_wp_attachment_metadata'"; $sql = "SELECT * FROM {$wp_postmeta} WHERE meta_value LIKE '%-scaled.jpg%' AND meta_key='_wp_attachment_metadata'";
$statement = $pdo->query($sql); $statement = $pdo->query($sql);
$image_metas = $statement->fetchAll(); $image_metas = $statement->fetchAll();
foreach ($image_metas as $meta) { foreach ($image_metas as $meta) {
$meta_value = unserialize($meta['meta_value']); $meta_value = unserialize($meta['meta_value']);
$file = $meta_value['file']; $file = $meta_value['file'];
$meta_value['file'] = str_replace('-scaled.jpg', '.jpg', $file); $meta_value['file'] = str_replace('-scaled.jpg', '.jpg', $file);
update_post_meta($meta['post_id'], $meta['meta_key'], $meta_value); update_post_meta($meta['post_id'], $meta['meta_key'], $meta_value);
$result = get_post_meta($meta['post_id'], $meta['meta_key']); $result = get_post_meta($meta['post_id'], $meta['meta_key']);
print_r($result); print_r($result);
} }
} catch (PDOException $e) { } catch (PDOException $e) {
print_r($e->getMessage()); print_r($e->getMessage());
} }

View file

@ -22,12 +22,11 @@ $templates = ['404.twig'];
* *
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger * @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 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-modele-simple.css',
path: '/assets/css/pages/page-modele-simple.css', );
);
} }
add_action('wp_enqueue_scripts', load_page_resources(...)); add_action('wp_enqueue_scripts', load_page_resources(...));

View file

@ -17,14 +17,14 @@ $context = Timber::context();
$templates = ['accueil.twig']; $templates = ['accueil.twig'];
add_action('wp_enqueue_scripts', function (): void { add_action('wp_enqueue_scripts', function (): 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',
); );
Resource::enqueue_script_module_file( Resource::enqueue_script_module_file(
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',
); );
}); });
Timber::render(data: $context, filenames: $templates); Timber::render(data: $context, filenames: $templates);

View file

@ -24,20 +24,20 @@ $templates = ['a-propos.twig'];
$image_dimensions = getimagesize(filename: get_template_directory() . '/assets/img/about/haikuabout.png'); $image_dimensions = getimagesize(filename: get_template_directory() . '/assets/img/about/haikuabout.png');
if (is_bool($image_dimensions)) { if (is_bool($image_dimensions)) {
throw new Exception("Impossible d'obtenir les dimensions de l'image principale de la page."); throw new Exception("Impossible d'obtenir les dimensions de l'image principale de la page.");
} }
$context['image_dimensions'] = $image_dimensions; $context['image_dimensions'] = $image_dimensions;
add_action('wp_enqueue_scripts', function (): void { add_action('wp_enqueue_scripts', function (): 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',
); );
Resource::enqueue_script_module_file( Resource::enqueue_script_module_file(
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',
); );
}); });
Timber::render(data: $context, filenames: $templates); Timber::render(data: $context, filenames: $templates);

View file

@ -22,22 +22,20 @@ use WC_Session_Handler;
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
// TODO: Appliquer le bon calcul pour les montants vs. percentages // TODO: Appliquer le bon calcul pour les montants vs. percentages
function get_discount_amount(WC_Coupon $coupon) function get_discount_amount(WC_Coupon $coupon) {
{ if ($coupon->get_discount_type() === 'fixed_cart') {
if ($coupon->get_discount_type() === 'fixed_cart') { return $coupon->get_amount() * 100;
return $coupon->get_amount() * 100; } else {
} else { return $coupon->get_amount();
return $coupon->get_amount(); }
}
} }
function get_discount_duration(WC_Coupon $coupon): string function get_discount_duration(WC_Coupon $coupon): string {
{ if ($coupon->get_discount_type() === 'fixed_cart') {
if ($coupon->get_discount_type() === 'fixed_cart') { return 'once';
return 'once'; } else {
} else { return 'forever';
return 'forever'; }
}
} }
// Récupère les informations nécessaires // Récupère les informations nécessaires
@ -46,37 +44,37 @@ $session_wc = WC()->session;
/** @var array<string,string> $urls URLs utilisables pour rediriger l'Utilisateur. */ /** @var array<string,string> $urls URLs utilisables pour rediriger l'Utilisateur. */
$urls = [ $urls = [
'accueil' => get_page_link(get_page_by_path('home')), 'accueil' => get_page_link(get_page_by_path('home')),
'succes_commande' => get_page_link(get_page_by_path('successful-order')), 'succes_commande' => get_page_link(get_page_by_path('successful-order')),
'echec_commande' => get_page_link(get_page_by_path('failed-order')), 'echec_commande' => get_page_link(get_page_by_path('failed-order')),
]; ];
// Redirige à la page d'accueil si le Panier est vide // Redirige à la page d'accueil si le Panier est vide
if (WC()->cart->is_empty()) { if (WC()->cart->is_empty()) {
header('Location: ' . $urls['accueil']); header('Location: ' . $urls['accueil']);
return; return;
} }
// Vérifie que les paramètres d'URLs nécessaires soient présents // Vérifie que les paramètres d'URLs nécessaires soient présents
/** @var string $order_id */ /** @var string $order_id */
$order_id = $_GET['order_id']; $order_id = $_GET['order_id'];
if (!$order_id) { if (!$order_id) {
$reponse = ['succes' => false, 'status' => 'order_key is missing']; $reponse = ['succes' => false, 'status' => 'order_key is missing'];
echo json_encode($reponse); echo json_encode($reponse);
http_response_code(400); http_response_code(400);
return; return;
} }
/** @var string $order_key */ /** @var string $order_key */
$order_key = $_GET['order_key']; $order_key = $_GET['order_key'];
if (!$order_key) { if (!$order_key) {
$reponse = ['succes' => false, 'status' => 'order_key is missing']; $reponse = ['succes' => false, 'status' => 'order_key is missing'];
echo json_encode($reponse); echo json_encode($reponse);
http_response_code(400); http_response_code(400);
return; return;
} }
// Récupère le Panier et l'Email du Client // Récupère le Panier et l'Email du Client
@ -88,29 +86,29 @@ $email_client = WC()->session->get('customer')['email'];
/** @var list<Product> $articles */ /** @var list<Product> $articles */
$articles = collect($panier->get_cart()) $articles = collect($panier->get_cart())
->map(static function ($article_panier) { ->map(static function ($article_panier) {
$titre_produit = match ('variable' === $article_panier['data']?->get_type()) { $titre_produit = match ('variable' === $article_panier['data']?->get_type()) {
true => $article_panier['data']?->get_title() true => $article_panier['data']?->get_title()
. ' (' . ' ('
. explode(': ', (string) $article_panier['data']?->get_attribute_summary())[1] . explode(': ', (string) $article_panier['data']?->get_attribute_summary())[1]
. ')', . ')',
false => $article_panier['data']?->get_title(), false => $article_panier['data']?->get_title(),
}; };
return [ return [
'price_data' => [ 'price_data' => [
'currency' => 'EUR', 'currency' => 'EUR',
'product_data' => [ 'product_data' => [
'name' => $titre_produit, 'name' => $titre_produit,
'images' => [wp_get_attachment_image_url($article_panier['data']?->get_image_id())], 'images' => [wp_get_attachment_image_url($article_panier['data']?->get_image_id())],
], ],
'unit_amount' => $article_panier['data']?->get_price() * 100, 'unit_amount' => $article_panier['data']?->get_price() * 100,
], ],
'quantity' => $article_panier['quantity'], 'quantity' => $article_panier['quantity'],
]; ];
}) })
->values() ->values()
->toArray(); ->toArray();
// Récupère la Commande et la Méthode de Livraison // Récupère la Commande et la Méthode de Livraison
/** @var WC_Order $commande */ /** @var WC_Order $commande */
@ -120,7 +118,7 @@ $methode_livraison = ['nom' => $commande->get_shipping_method(), 'cout' => $comm
// Le nom de la méthode de livraison ne peut être une chaîne vide. // Le nom de la méthode de livraison ne peut être une chaîne vide.
if (empty($methode_livraison['nom'])) { if (empty($methode_livraison['nom'])) {
$methode_livraison['nom'] = 'Free'; $methode_livraison['nom'] = 'Free';
} }
// Sélectionne la clé API Stripe // Sélectionne la clé API Stripe
@ -129,39 +127,39 @@ Stripe::setApiKey(Config::get('STRIPE_API_SECRET'));
// Met à jour les Codes promos // Met à jour les Codes promos
$coupons_stripe = collect(Coupon::all()->data); $coupons_stripe = collect(Coupon::all()->data);
$coupons_wc = collect(WC()->cart->get_coupons()) $coupons_wc = collect(WC()->cart->get_coupons())
->map(static fn(WC_Coupon $coupon): array => [ ->map(static fn(WC_Coupon $coupon): array => [
'currency' => 'EUR', 'currency' => 'EUR',
'duration' => get_discount_duration($coupon), 'duration' => get_discount_duration($coupon),
'fixed_cart' === $coupon->get_discount_type() ? 'amount_off' : 'percent_off' => get_discount_amount($coupon), 'fixed_cart' === $coupon->get_discount_type() ? 'amount_off' : 'percent_off' => get_discount_amount($coupon),
'id' => $coupon->get_code(), 'id' => $coupon->get_code(),
'name' => $coupon->get_code(), 'name' => $coupon->get_code(),
]) ])
->each(static function (array $item) use ($coupons_stripe): void { ->each(static function (array $item) use ($coupons_stripe): void {
// Si le code promo n'existe pas, le créer // Si le code promo n'existe pas, le créer
if (!$coupons_stripe->contains('name', $item['name'])) { if (!$coupons_stripe->contains('name', $item['name'])) {
Coupon::create($item); Coupon::create($item);
} }
}); });
$reductions_stripe = $coupons_wc $reductions_stripe = $coupons_wc
->map(static fn($coupon): array => ['coupon' => $coupon['name']]) ->map(static fn($coupon): array => ['coupon' => $coupon['name']])
->values() ->values()
->toArray(); ->toArray();
/** @var Session $session_checkout_stripe */ /** @var Session $session_checkout_stripe */
$session_checkout_stripe = Session::create([ $session_checkout_stripe = Session::create([
'cancel_url' => $urls['echec_commande'], 'cancel_url' => $urls['echec_commande'],
'customer_email' => $email_client, 'customer_email' => $email_client,
'discounts' => $reductions_stripe, 'discounts' => $reductions_stripe,
'line_items' => $articles, 'line_items' => $articles,
'mode' => 'payment', 'mode' => 'payment',
'success_url' => $urls['succes_commande'] . '?session_id={CHECKOUT_SESSION_ID}', 'success_url' => $urls['succes_commande'] . '?session_id={CHECKOUT_SESSION_ID}',
'metadata' => ['order_id' => $order_id, 'order_key' => $order_key], 'metadata' => ['order_id' => $order_id, 'order_key' => $order_key],
'shipping_options' => [['shipping_rate_data' => [ 'shipping_options' => [['shipping_rate_data' => [
'display_name' => $methode_livraison['nom'], 'display_name' => $methode_livraison['nom'],
'fixed_amount' => ['amount' => $methode_livraison['cout'], 'currency' => 'EUR'], 'fixed_amount' => ['amount' => $methode_livraison['cout'], 'currency' => 'EUR'],
'tax_behavior' => 'inclusive', 'tax_behavior' => 'inclusive',
'type' => 'fixed_amount', 'type' => 'fixed_amount',
]]], ]]],
], ['idempotency_key' => Uuid::v4()]); ], ['idempotency_key' => Uuid::v4()]);
// echo json_encode($session_checkout_stripe); // echo json_encode($session_checkout_stripe);
header('HTTP/1.1 303 See Other'); header('HTTP/1.1 303 See Other');

View file

@ -17,10 +17,10 @@ $context = Timber::context();
$templates = ['echec-commande.twig']; $templates = ['echec-commande.twig'];
add_action('wp_enqueue_scripts', function (): void { add_action('wp_enqueue_scripts', function (): 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',
); );
}); });
// Rendu // Rendu

View file

@ -17,10 +17,10 @@ $context = Timber::context();
$templates = ['cgv.twig']; $templates = ['cgv.twig'];
add_action('wp_enqueue_scripts', function (): void { add_action('wp_enqueue_scripts', function (): 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',
); );
}); });
// Rendu // Rendu

View file

@ -9,39 +9,36 @@ declare(strict_types=1);
use Carbon_Fields\Container; use Carbon_Fields\Container;
use Carbon_Fields\Field; use Carbon_Fields\Field;
function cree_champs_personnalises_produit(): void function cree_champs_personnalises_produit(): void {
{ Container::make('post_meta', "Product's Details")
Container::make('post_meta', "Product's Details") ->where('post_type', '=', 'product')
->where('post_type', '=', 'product') ->add_fields([
->add_fields([ // Galerie des photos Produit
// Galerie des photos Produit Field::make('media_gallery', 'photos_colonne_gauche', __('Left Column Photos'))
Field::make('media_gallery', 'photos_colonne_gauche', __('Left Column Photos')) ->set_type(['image'])
->set_type(['image']) ->set_duplicates_allowed(false),
->set_duplicates_allowed(false), // Galerie des photos portées
// Galerie des photos portées Field::make('media_gallery', 'photos_colonne_droite', __('Right Column Photos'))
Field::make('media_gallery', 'photos_colonne_droite', __('Right Column Photos')) ->set_type(['image'])
->set_type(['image']) ->set_duplicates_allowed(false),
->set_duplicates_allowed(false), // Texte des détails du Produit
// Texte des détails du Produit Field::make('rich_text', 'haiku_details_produit', __("Product's Details")),
Field::make('rich_text', 'haiku_details_produit', __("Product's Details")),
]);
}
function cree_champ_personnalise_commande($order): void
{
woocommerce_wp_text_input([
'id' => 'tracking_number',
'label' => 'Tracking Number:',
'value' => $order->get_meta('tracking_number'),
'wrapper_class' => 'form-field-wide',
]); ]);
} }
function maj_champ_personnalise_commande($order_id): void function cree_champ_personnalise_commande($order): void {
{ woocommerce_wp_text_input([
$order = wc_get_order($order_id); 'id' => 'tracking_number',
$order->update_meta_data('tracking_number', wc_clean($_POST['tracking_number'])); 'label' => 'Tracking Number:',
$order->save(); 'value' => $order->get_meta('tracking_number'),
'wrapper_class' => 'form-field-wide',
]);
}
function maj_champ_personnalise_commande($order_id): void {
$order = wc_get_order($order_id);
$order->update_meta_data('tracking_number', wc_clean($_POST['tracking_number']));
$order->save();
} }
add_action('carbon_fields_register_fields', 'cree_champs_personnalises_produit'); add_action('carbon_fields_register_fields', 'cree_champs_personnalises_produit');

View file

@ -8,74 +8,69 @@
declare(strict_types=1); declare(strict_types=1);
function enregistre_controle_personnalise_tinymce(): void function enregistre_controle_personnalise_tinymce(): void {
{ /**
* TinyMCE Custom Control.
*
* @author Anthony Hortin <http://maddisondesigns.com>
* @license http://www.gnu.org/licenses/gpl-2.0.html
*
* @see https://github.com/maddisondesigns
*/
final class ControlesPersonnalises extends WP_Customize_Control {
/** The type of control being rendered. */
public $type = 'editeur_tinymce';
/** /**
* TinyMCE Custom Control. * Enqueue our scripts and styles.
*
* @author Anthony Hortin <http://maddisondesigns.com>
* @license http://www.gnu.org/licenses/gpl-2.0.html
*
* @see https://github.com/maddisondesigns
*/ */
final class ControlesPersonnalises extends WP_Customize_Control public function enqueue(): void {
{ wp_enqueue_script(
/** The type of control being rendered. */ handle: 'controle-personnalise-tinymce',
public $type = 'editeur_tinymce'; src: get_template_directory_uri() . '/assets/vendor/controle-personnalise-tinymce.js',
deps: ['jquery'],
ver: '1.3',
args: true,
);
wp_enqueue_editor();
}
/** /**
* Enqueue our scripts and styles. * Render the control in the customizer.
*/ */
public function enqueue(): void public function render_content(): void { ?>
{
wp_enqueue_script(
handle: 'controle-personnalise-tinymce',
src: get_template_directory_uri() . '/assets/vendor/controle-personnalise-tinymce.js',
deps: ['jquery'],
ver: '1.3',
args: true,
);
wp_enqueue_editor();
}
/**
* Render the control in the customizer.
*/
public function render_content(): void
{ ?>
<div class="tinymce-control"> <div class="tinymce-control">
<span class="customize-control-title"><?php echo esc_html($this->label); ?></span> <span class="customize-control-title"><?php echo esc_html($this->label); ?></span>
<?php if (!empty($this->description)) { ?> <?php if (!empty($this->description)) { ?>
<span class="customize-control-description"><?php echo esc_html($this->description); ?></span> <span class="customize-control-description"><?php echo esc_html($this->description); ?></span>
<?php } ?> <?php } ?>
<textarea id="<?php echo <textarea id="<?php echo
esc_attr($this->id) esc_attr($this->id)
; ?>" class="customize-control-tinymce-editor" <?php $this->link(); ?>><?php echo ; ?>" class="customize-control-tinymce-editor" <?php $this->link(); ?>><?php echo
esc_html($this->value()) esc_html($this->value())
; ?></textarea> ; ?></textarea>
</div> </div>
<?php } <?php }
/** /**
* Pass our TinyMCE toolbar string to JavaScript. * Pass our TinyMCE toolbar string to JavaScript.
*/ */
public function to_json(): void public function to_json(): void {
{ parent::to_json();
parent::to_json();
$this->json['skyrockettinymcetoolbar1'] = isset($this->input_attrs['toolbar1']) $this->json['skyrockettinymcetoolbar1'] = isset($this->input_attrs['toolbar1'])
? esc_attr($this->input_attrs['toolbar1']) ? esc_attr($this->input_attrs['toolbar1'])
: 'bold italic bullist numlist alignleft aligncenter alignright link'; : 'bold italic bullist numlist alignleft aligncenter alignright link';
$this->json['skyrockettinymcetoolbar2'] = isset($this->input_attrs['toolbar2']) $this->json['skyrockettinymcetoolbar2'] = isset($this->input_attrs['toolbar2'])
? esc_attr($this->input_attrs['toolbar2']) ? esc_attr($this->input_attrs['toolbar2'])
: ''; : '';
$this->json['skyrocketmediabuttons'] = isset($this->input_attrs['mediaButtons']) $this->json['skyrocketmediabuttons'] = isset($this->input_attrs['mediaButtons'])
&& $this->input_attrs['mediaButtons'] === true && $this->input_attrs['mediaButtons'] === true
? true ? true
: false; : false;
}
} }
}
} }
add_action('customize_register', 'enregistre_controle_personnalise_tinymce'); add_action('customize_register', 'enregistre_controle_personnalise_tinymce');

View file

@ -11,116 +11,113 @@ use function is_float;
use function is_int; use function is_int;
use function is_string; use function is_string;
final readonly class Cart final readonly class Cart {
{ public function __construct() {}
public function __construct() {}
/** La valeur par défaut d'une donnée invalide du Panier. */ /** La valeur par défaut d'une donnée invalide du Panier. */
private const string DEFAULT_VALUE = '0.00'; private const string DEFAULT_VALUE = '0.00';
/** /**
* Retourne la liste des pays acceptés pour la livraison. * Retourne la liste des pays acceptés pour la livraison.
* *
* @return array<int,string> * @return array<int,string>
*/ */
public static function get_allowed_countries(): array public static function get_allowed_countries(): array {
{ return [
return [ 'AD',
'AD', 'AL',
'AL', 'AM',
'AM', 'AR',
'AR', 'AT',
'AT', 'AU',
'AU', 'BA',
'BA', 'BE',
'BE', 'BG',
'BG', 'BR',
'BR', 'CA',
'CA', 'CH',
'CH', 'CL',
'CL', 'CR',
'CR', 'CU',
'CU', 'CY',
'CY', 'CZ',
'CZ', 'DE',
'DE', 'DK',
'DK', 'DZ',
'DZ', 'EE',
'EE', 'EG',
'EG', 'ES',
'ES', 'FI',
'FI', 'FR',
'FR', 'GF',
'GF', 'GP',
'GP', 'GR',
'GR', 'HR',
'HR', 'HU',
'HU', 'IE',
'IE', 'IS',
'IS', 'IT',
'IT', 'JP',
'JP', 'KR',
'KR', 'LB',
'LB', 'LI',
'LI', 'LT',
'LT', 'LU',
'LU', 'LV',
'LV', 'MA',
'MA', 'MD',
'MD', 'ME',
'ME', 'MF',
'MF', 'MQ',
'MQ', 'MT',
'MT', 'MX',
'MX', 'NC',
'NC', 'NL',
'NL', 'NO',
'NO', 'NZ',
'NZ', 'PF',
'PF', 'PL',
'PL', 'PM',
'PM', 'PS',
'PS', 'PT',
'PT', 'RE',
'RE', 'RO',
'RO', 'SE',
'SE', 'SI',
'SI', 'SK',
'SK', 'SM',
'SM', 'TN',
'TN', 'TR',
'TR', 'TW',
'TW', 'US',
'US', 'YT',
'YT', 'ZA',
'ZA', ];
]; }
public static function parse_cart_value(int|float|string|bool $cart_value): string {
if (is_int($cart_value) || is_float($cart_value)) {
return self::format_number($cart_value);
} }
public static function parse_cart_value(int|float|string|bool $cart_value): string if (is_string($cart_value)) {
{ $number = Number::parseInt($cart_value);
if (is_int($cart_value) || is_float($cart_value)) { $number = is_bool($number) ? 0 : $number;
return self::format_number($cart_value);
}
if (is_string($cart_value)) { return self::format_number($number);
$number = Number::parseInt($cart_value);
$number = is_bool($number) ? 0 : $number;
return self::format_number($number);
}
return '0.00';
} }
private static function format_number(int|float $number): string return '0.00';
{ }
$formatted_number = Number::format(
number: $number, private static function format_number(int|float $number): string {
// precision et max_precision sont mutuellement exclusifs. $formatted_number = Number::format(
precision: 2, number: $number,
locale: 'fr', // precision et max_precision sont mutuellement exclusifs.
); precision: 2,
return is_bool($formatted_number) ? self::DEFAULT_VALUE : $formatted_number; locale: 'fr',
} );
return is_bool($formatted_number) ? self::DEFAULT_VALUE : $formatted_number;
}
} }

View file

@ -6,34 +6,32 @@ namespace HaikuAtelier\Data;
use WC_Product; use WC_Product;
final readonly class ProductVariation final readonly class ProductVariation {
{ /**
/** * @param int $id L'ID de la Variation
* @param int $id L'ID de la Variation * @param string $price Le prix de la Variation
* @param string $price Le prix de la Variation * @param list<ProductVariationAttribute> $attributes Les attributs appliqués à la Variation
* @param list<ProductVariationAttribute> $attributes Les attributs appliqués à la Variation */
*/ private function __construct(
private function __construct( public int $id,
public int $id, public string $price,
public string $price, public array $attributes,
public array $attributes, ) {}
) {}
/** /**
* Créé une nouvelle instance de `ProductVariation` à partir d'un `WC_Product`. * Créé une nouvelle instance de `ProductVariation` à partir d'un `WC_Product`.
*/ */
public static function new(WC_Product $product): self public static function new(WC_Product $product): self {
{ $id = $product->get_id();
$id = $product->get_id(); $price = $product->get_price();
$price = $product->get_price(); /** @var list<ProductVariationAttribute> */
/** @var list<ProductVariationAttribute> */ $attributes = array_map(
$attributes = array_map( /** @phpstan-ignore argument.type (Impossible à satisfaire) */
/** @phpstan-ignore argument.type (Impossible à satisfaire) */ static fn(string $key, string $value) => new ProductVariationAttribute($key, $value),
static fn(string $key, string $value) => new ProductVariationAttribute($key, $value), array_keys($product->get_attributes()),
array_keys($product->get_attributes()), array_values($product->get_attributes()),
array_values($product->get_attributes()), );
);
return new self($id, $price, $attributes); return new self($id, $price, $attributes);
} }
} }

View file

@ -4,14 +4,13 @@ declare(strict_types=1);
namespace HaikuAtelier\Data; namespace HaikuAtelier\Data;
final readonly class ProductVariationAttribute final readonly class ProductVariationAttribute {
{ /**
/** * @param string $attribute Le slug de l'Attribut
* @param string $attribute Le slug de l'Attribut * @param string $value Le slug de la valeur de l'Attribut
* @param string $value Le slug de la valeur de l'Attribut */
*/ public function __construct(
public function __construct( public string $attribute,
public string $attribute, public string $value,
public string $value, ) {}
) {}
} }

View file

@ -9,9 +9,8 @@ declare(strict_types=1);
namespace HaikuAtelier; 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 {
{ remove_filter('the_content', 'wpautop');
remove_filter('the_content', 'wpautop');
} }
/** /**
@ -21,17 +20,16 @@ function desactive_wpautop(): void
* *
* @return array<string, bool> le même tableau avec des configurations en plus * @return array<string, bool> le même tableau avec des configurations en plus
*/ */
function desactive_transformation_contenu_tinymce(array $configuration): array function desactive_transformation_contenu_tinymce(array $configuration): array {
{ // Ne supprime pas les retours à la ligne
// Ne supprime pas les retours à la ligne $configuration['remove_linebreaks'] = false;
$configuration['remove_linebreaks'] = false; // Convertis les caractères de retours à la ligne en <br>
// Convertis les caractères de retours à la ligne en <br> $configuration['convert_newlines_to_brs'] = true;
$configuration['convert_newlines_to_brs'] = true; // Supprime les <br> redondants
// Supprime les <br> redondants $configuration['remove_redundant_brs'] = false;
$configuration['remove_redundant_brs'] = false;
// Retourne $configuration à WordPress // Retourne $configuration à WordPress
return $configuration; return $configuration;
} }
/** /**
@ -41,22 +39,19 @@ function desactive_transformation_contenu_tinymce(array $configuration): array
* *
* @return array<string, string> le même tableau avec SVG en plus * @return array<string, string> le même tableau avec SVG en plus
*/ */
function autorise_import_svg_mediatheque(array $file_types): array 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 [...$file_types, ...$new_filetypes]; return [...$file_types, ...$new_filetypes];
} }
function retire_motifs_blocs_gutenberg(): void function retire_motifs_blocs_gutenberg(): void {
{ remove_theme_support('core-block-patterns');
remove_theme_support('core-block-patterns');
} }
function retire_styles_core_block(): void function retire_styles_core_block(): void {
{ wp_dequeue_style('core-block-supports');
wp_dequeue_style('core-block-supports');
} }
// Désactive les appels à l'API de la mise à jour des traductions // Désactive les appels à l'API de la mise à jour des traductions

View file

@ -14,33 +14,32 @@ use function register_taxonomy;
/** /**
* Enregistre la Taxonomie « Collection ». * Enregistre la Taxonomie « Collection ».
*/ */
function enregistre_taxonomie_collection(): void function enregistre_taxonomie_collection(): void {
{ $labels = [
$labels = [ 'add_new_item' => __('Add New Collection'),
'add_new_item' => __('Add New Collection'), 'all_items' => __('All Collections'),
'all_items' => __('All Collections'), 'edit_item' => __('Edit Collection'),
'edit_item' => __('Edit Collection'), 'menu_name' => __('Collections'),
'menu_name' => __('Collections'), 'name' => __('Collections'),
'name' => __('Collections'), 'new_item_name' => __('New Collection Name'),
'new_item_name' => __('New Collection Name'), 'search_items' => __('Search Collections'),
'search_items' => __('Search Collections'), 'singular_name' => __('Collection'),
'singular_name' => __('Collection'), 'update_item' => __('Update Collection'),
'update_item' => __('Update Collection'), ];
]; $args = [
$args = [ 'description' => __('An ensemble of pieces thematically or chronologically grouped together.'),
'description' => __('An ensemble of pieces thematically or chronologically grouped together.'), 'hierarchical' => false,
'hierarchical' => false, 'labels' => $labels,
'labels' => $labels, 'publicly_queryable' => false,
'publicly_queryable' => false, 'query_var' => true,
'query_var' => true, 'rewrite' => ['slug' => 'collection'],
'rewrite' => ['slug' => 'collection'], 'show_admin_column' => true,
'show_admin_column' => true, 'show_in_menu' => true,
'show_in_menu' => true, 'show_in_quick_edit' => true,
'show_in_quick_edit' => true, 'show_ui' => true,
'show_ui' => true, ];
];
register_taxonomy('collection', ['product'], $args); register_taxonomy('collection', ['product'], $args);
} }
add_action('init', enregistre_taxonomie_collection(...)); add_action('init', enregistre_taxonomie_collection(...));

View file

@ -14,50 +14,46 @@ use function is_array;
use function Psl\Option\none; use function Psl\Option\none;
use function Psl\Option\some; use function Psl\Option\some;
final readonly class Post final readonly class Post {
{ /**
/** * @return Option\Option<mixed>
* @return Option\Option<mixed> */
*/ public static function get_post_meta(int $post_id, string $key): Option\Option {
public static function get_post_meta(int $post_id, string $key): Option\Option /** @var false|mixed|string */
{ $value = get_post_meta($post_id, $key, true);
/** @var false|mixed|string */
$value = get_post_meta($post_id, $key, true);
if ($value === false) { if ($value === false) {
return none(); return none();
}
return some($value);
} }
/** return some($value);
* @return Option\Option<array<mixed>> }
*/
public static function get_post_meta_array(int $post_id, string $key): Option\Option
{
/** @var array<mixed>|false */
$value = get_post_meta($post_id, $key, false);
if (is_array($value)) { /**
return some($value); * @return Option\Option<array<mixed>>
} */
public static function get_post_meta_array(int $post_id, string $key): Option\Option {
/** @var array<mixed>|false */
$value = get_post_meta($post_id, $key, false);
return none(); if (is_array($value)) {
return some($value);
} }
/** return none();
* @return Option\Option<array<mixed>> }
*/
public static function get_terms(int $post_id, string $taxonomy_name): Option\Option
{
/** @var false|list<WP_Term>|WP_Error */
$terms = get_the_terms($post_id, $taxonomy_name);
if (is_array($terms)) { /**
return some($terms); * @return Option\Option<array<mixed>>
} */
public static function get_terms(int $post_id, string $taxonomy_name): Option\Option {
/** @var false|list<WP_Term>|WP_Error */
$terms = get_the_terms($post_id, $taxonomy_name);
return none(); if (is_array($terms)) {
return some($terms);
} }
return none();
}
} }

View file

@ -0,0 +1,10 @@
import { Schema } from "effect";
import { ProductAttribute } from "./product.ts";
class AddProductToCart extends Schema.Class<AddProductToCart>("AddProductToCart")({
id: Schema.Int,
quantity: Schema.Int.check(Schema.isGreaterThan(0)),
variation: Schema.Array(ProductAttribute),
}) {}
export { AddProductToCart };

View file

@ -0,0 +1,20 @@
// oxlint-disable no-magic-numbers -- Pas besoin ici.
import { Schema } from "effect";
class ProductAttribute extends Schema.Class<ProductAttribute>("ProductAttribute")({
/** L'identifiant _(slug)_ de l'Attribut. */
attribute: Schema.String,
/** La valeur de l'attribut. */
value: Schema.String,
}) {}
class ProductVariation extends Schema.Class<ProductVariation>("ProductVariation")({
/** Les Attributs présents pour cette Variation. */
attributes: Schema.Array(ProductAttribute),
/** L'identifiant numérique unique de la Variation. */
id: Schema.Int.check(Schema.isGreaterThan(0)),
/** Le prix de la Variation. */
price: Schema.NonEmptyString,
}) {}
export { ProductAttribute, ProductVariation };

View file

@ -0,0 +1,13 @@
import { Console, Layer, ManagedRuntime, pipe } from "effect";
import ProductPageDOM from "./service-dom.ts";
import ProductPageElements from "./service-elements.ts";
const ProductPageRuntime = ManagedRuntime.make(
pipe(
ProductPageDOM.layer,
Layer.provide(ProductPageElements.layer),
Layer.tapError(error => Console.error("ProductPageRuntime", "Impossible de créer le Layer :", error)),
),
);
export default ProductPageRuntime;

View file

@ -6,97 +6,28 @@ import {
Effect, Effect,
HashMap, HashMap,
Layer, Layer,
ManagedRuntime,
Option, Option,
pipe, pipe,
Ref,
Schema,
SchemaIssue,
Stream, Stream,
} from "effect"; } from "effect";
import type { NonEmptyReadonlyArray } from "effect/Array";
import type { NoSuchElementError } from "effect/Cause"; import type { NoSuchElementError } from "effect/Cause";
import { getAllSelectorFromDocument, getFirstSelectorFromDocument } from "../scripts-effect/lib/dom.ts"; import { AddProductToCart } from "../../scripts-effect/schemas/api.ts";
import { ProductAttribute, ProductVariation } from "../../scripts-effect/schemas/product.ts";
import { import {
ATTRIBUT_ARIA_CONTROLS, ATTRIBUT_ARIA_CONTROLS,
ATTRIBUT_ARIA_EXPANDED, ATTRIBUT_ARIA_EXPANDED,
ATTRIBUT_DESACTIVE, ATTRIBUT_DESACTIVE,
ATTRIBUT_HIDDEN, ATTRIBUT_HIDDEN,
DOM_BOUTON_AJOUT_PANIER, } from "../constantes/dom.ts";
DOM_BOUTONS_ACCORDEON, import ProductPageElements from "./service-elements.ts";
DOM_CONTENUS_ACCORDEON, import type { DetailEnsemble } from "./types.d.ts";
DOM_PRIX_PRODUIT,
} from "./constantes/dom.ts";
import type { WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.d.ts";
/** Représente un ensemble bouton-contenu d'une Section dans la description du Produit. */
type DetailEnsemble = {
button: HTMLButtonElement;
content: HTMLDivElement;
};
class ProductPageElements extends Context.Service<
ProductPageElements,
{
AddToCartButton: HTMLButtonElement;
Details: HashMap.HashMap<string, DetailEnsemble>;
DetailsButtons: NonEmptyReadonlyArray<HTMLButtonElement>;
DetailsContents: NonEmptyReadonlyArray<HTMLDivElement>;
ProductPrice: HTMLParagraphElement;
ProductRawJson: HTMLScriptElement;
VariationChoiceForm: HTMLFormElement;
VariationSelectors: ReadonlyArray<HTMLSelectElement>;
}
>()("haikuatelier.fr/Produit/ProductPageElements") {
static readonly layer = Layer.effect(
ProductPageElements,
Effect.gen(function*() {
const AddToCartButton = yield* getFirstSelectorFromDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER);
const DetailsButtons = yield* getAllSelectorFromDocument<HTMLButtonElement>(DOM_BOUTONS_ACCORDEON);
const DetailsContents = yield* getAllSelectorFromDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON);
const ProductPrice = yield* getFirstSelectorFromDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT);
const ProductRawJson = yield* getFirstSelectorFromDocument<HTMLScriptElement>("#product-json");
const VariationChoiceForm = yield* getFirstSelectorFromDocument<HTMLFormElement>("#variation-choice");
const VariationSelectors = yield* pipe(
getAllSelectorFromDocument<HTMLSelectElement>(".selecteur-produit select"),
Option.orElseSome(() => FxArray.empty<HTMLSelectElement>()),
);
const Details = yield* pipe(
DetailsButtons,
FxArray.map(
(button: HTMLButtonElement, index: number): Effect.Effect<[string, DetailEnsemble], NoSuchElementError> =>
Effect.gen(function*() {
const contentId = yield* Option.fromNullishOr(button.getAttribute(ATTRIBUT_ARIA_CONTROLS));
const content = yield* FxArray.get(DetailsContents, index);
return [contentId, { button, content } satisfies DetailEnsemble];
}),
),
Effect.all,
Effect.map(HashMap.fromIterable<string, DetailEnsemble>),
);
return ProductPageElements.of({
AddToCartButton,
Details,
DetailsButtons,
DetailsContents,
ProductPrice,
ProductRawJson,
VariationChoiceForm,
VariationSelectors,
});
}),
);
}
class ProductPageDOM extends Context.Service< class ProductPageDOM extends Context.Service<
ProductPageDOM, ProductPageDOM,
{ {
initPriceUpdatesOnVariationChange: () => Effect.Effect<void>;
onVariationChangeHandler: () => Effect.Effect<void>;
/**
* Récupère les Attributs du Produit depuis les Elements au sein du DOM.
*/
getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>>;
/** /**
* Initialise l'état initial du Bouton d'ajout au Panier. * Initialise l'état initial du Bouton d'ajout au Panier.
*/ */
@ -110,26 +41,30 @@ class ProductPageDOM extends Context.Service<
*/ */
initDetailInteractions: () => Effect.Effect<void, NoSuchElementError>; initDetailInteractions: () => Effect.Effect<void, NoSuchElementError>;
/** /**
* Met à jour l'état des Sections de la Description du Produit. * Initialise la mise à jour du Prix affiché en fonction du choix de la Varation de Produit.
*/ */
onDetailButtonClickHandler: (evt: Event) => Effect.Effect<void, NoSuchElementError>; initPriceUpdatesOnVariationChange: () => Effect.Effect<void, NoSuchElementError | string>;
/**
* Met à jour l'état du Bouton d'ajout au Panier.
*/
onFormChangeHandler: (evt: Event) => Effect.Effect<void>;
/** /**
* Replie toutes les sections de la description du Produit. * Replie toutes les sections de la description du Produit.
*/ */
toggleAllDetails: () => Effect.Effect<void>; initAddToCartButtonClicks: () => unknown;
ProductVariations: ReadonlyArray<ProductVariation>;
CurrentVariation: Ref.Ref<Option.Option<ProductVariation>>;
} }
>()("haikuatelier.fr/Produit/ProductPageDOM") { >()("haikuatelier.fr/Produit/ProductPageDOM") {
static readonly layer = Layer.effect( static readonly layer = Layer.effect(
ProductPageDOM, ProductPageDOM,
Effect.gen(function*() { Effect.gen(function*() {
const { AddToCartButton, Details, ProductPrice, DetailsButtons, ProductRawJson, VariationChoiceForm, VariationSelectors } = const {
yield* ProductPageElements; AddToCartButton,
Details,
const onFormChangeHandler = Effect.fnUntraced(function*(evt: Event) { DetailsButtons,
ProductPrice,
ProductRawJson,
VariationChoiceForm,
VariationSelectors,
} = yield* ProductPageElements;
const onFormChangeHandler = Effect.fn("onFormChangeHandler")(function*(evt: Event): Effect.fn.Return<void> {
// La cible ne peut qu'être un Formulaire. // La cible ne peut qu'être un Formulaire.
const target: HTMLFormElement = evt.target as HTMLFormElement; const target: HTMLFormElement = evt.target as HTMLFormElement;
const isClickAllowed = target.checkValidity() === false; const isClickAllowed = target.checkValidity() === false;
@ -152,7 +87,9 @@ class ProductPageDOM extends Context.Service<
); );
}); });
const onDetailButtonClickHandler = Effect.fnUntraced(function*(evt: Event) { const onDetailButtonClickHandler = Effect.fn("onDetailButtonClickHandler")(function*(
evt: Event,
): Effect.fn.Return<void, NoSuchElementError> {
// Empêche la pollution de l'historique de navigation // Empêche la pollution de l'historique de navigation
evt.preventDefault(); evt.preventDefault();
@ -183,13 +120,67 @@ class ProductPageDOM extends Context.Service<
return yield* Effect.void; return yield* Effect.void;
}); });
const getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>> = () => const ProductVariations: ReadonlyArray<ProductVariation> = yield* pipe(
Effect.sync(() => JSON.parse(ProductRawJson.textContent)?.variations,
json => Schema.decodeUnknownEffect(Schema.Array(ProductVariation))(json, { onExcessProperty: "ignore" }),
Effect.mapError(error => SchemaIssue.makeFormatterStandardSchemaV1()(error.issue)),
Effect.tapCause(Console.error),
);
const getChosenProductAttributesFromDOM = Effect.fn("getChosenProductAttributesFromDOM")(function*() {
return yield* pipe(
FxArray.map(VariationSelectors, (select: HTMLSelectElement) => ({ FxArray.map(VariationSelectors, (select: HTMLSelectElement) => ({
attribute: select.id, attribute: select.id,
value: select.value, value: select.value,
})) })),
variations => Schema.decodeEffect(Schema.Array(ProductAttribute))(variations),
Effect.mapError(error => SchemaIssue.makeFormatterDefault()(error.issue)),
Effect.tapCause(Console.error),
); );
});
const CurrentVariation = yield* Ref.make(Option.none<ProductVariation>());
const onVariationChangeHandler = Effect.fn("onVariationChangeHandler")(function*(): Effect.fn.Return<
void,
NoSuchElementError | string
> {
yield* Console.debug("onVariationChangeHandler");
// Ne fais rien si le Formulaire n'est pas valide.
if (VariationChoiceForm.checkValidity() === false) {
yield* Console.debug("onVariationChangeHandler", "Le formulaire est invalide.");
return yield* Effect.void;
}
const equivalence = Schema.toEquivalence(Schema.Array(ProductAttribute));
const chosenProductAttributes = yield* getChosenProductAttributesFromDOM();
const chosenVariation: ProductVariation = yield* FxArray.findFirst(
ProductVariations,
(variation: ProductVariation) => equivalence(variation.attributes, chosenProductAttributes),
);
// Met à jour la valeur de la Variation choisie dans le Service.
yield* Ref.set(Option.some(chosenVariation))(CurrentVariation);
const newPrice = chosenVariation.price;
ProductPrice.textContent = `${newPrice}`;
return yield* Effect.void;
}, Effect.tapCause(Console.error));
const onAddToCartButtonHandler = Effect.fn("onAddToCartButtonHandler")(function*() {
const chosenVariation = yield* Ref.getUnsafe(CurrentVariation);
const productDetails = yield* Schema.decodeEffect(AddProductToCart)(
{
id: chosenVariation.id,
quantity: 1,
variation: chosenVariation.attributes,
},
{ errors: "all" },
);
console.debug(productDetails);
});
const initAddToCartButtonInitialState = Effect.fn("initAddToCartButtonInitialState")(function*() { const initAddToCartButtonInitialState = Effect.fn("initAddToCartButtonInitialState")(function*() {
/** Est-ce que le Produit affiché est en stock ? */ /** Est-ce que le Produit affiché est en stock ? */
@ -216,31 +207,23 @@ class ProductPageDOM extends Context.Service<
); );
}); });
const initPriceUpdatesOnVariationChange = Effect.fn("initPriceUpdatesOnVariationChange")(function*(){ const initAddToCartButtonClicks = Effect.fn("initAddToCartButtonClicks")(function*() {
return yield* pipe( return yield* pipe(
Stream.fromEventListener(VariationChoiceForm, "change"), Stream.fromEventListener(AddToCartButton, "click"),
Stream.tap(onVariationChangeHandler), Stream.tap(onAddToCartButtonHandler),
Stream.runDrain, Stream.runDrain,
) );
}); });
const onVariationChangeHandler = Effect.fn("onVariationChangeHandler")(function*(){ const initPriceUpdatesOnVariationChange = Effect.fn("initPriceUpdatesOnVariationChange")(
if (VariationChoiceForm.checkValidity() === false) { function*(): Effect.fn.Return<void, NoSuchElementError | string> {
return yield* Effect.void; return yield* pipe(
} Stream.fromEventListener(VariationChoiceForm, "change"),
Stream.tap(onVariationChangeHandler),
const variations = JSON.parse(ProductRawJson.textContent)?.variations as ReadonlyArray<unknown>; Stream.runDrain,
const chosenAttributes = yield* getProductAttributesFromDOM(); );
},
const equivalence = FxArray.makeEquivalence<{attribute: string,value: string}>((a,b) => { );
return a.attribute === b.attribute && a.value === b.value;
});
const chosenVariation = yield* FxArray.findFirst(variations, variation => equivalence(variation.attributes, chosenAttributes));
const newPrice = chosenVariation.price;
ProductPrice.textContent = `${newPrice}`;
return yield* Effect.void;
});
const initDetailInteractions = Effect.fn("initDetailInteractions")(function*() { const initDetailInteractions = Effect.fn("initDetailInteractions")(function*() {
return yield* pipe( return yield* pipe(
@ -256,26 +239,16 @@ class ProductPageDOM extends Context.Service<
}); });
return ProductPageDOM.of({ return ProductPageDOM.of({
getProductAttributesFromDOM, CurrentVariation,
ProductVariations,
initAddToCartButtonClicks,
initAddToCartButtonInitialState, initAddToCartButtonInitialState,
initAddToCartButtonUpdates, initAddToCartButtonUpdates,
initDetailInteractions, initDetailInteractions,
initPriceUpdatesOnVariationChange, initPriceUpdatesOnVariationChange,
onDetailButtonClickHandler,
onFormChangeHandler,
onVariationChangeHandler,
toggleAllDetails,
}); });
}), }),
); );
} }
const ProductPageRuntime = ManagedRuntime.make( export default ProductPageDOM;
pipe(
ProductPageDOM.layer,
Layer.provide(ProductPageElements.layer),
Layer.tapError(error => Console.error("ManagedRuntime", "Impossible de créer le Layer :", error.name)),
),
);
export { type DetailEnsemble, ProductPageDOM, ProductPageElements, ProductPageRuntime };

View file

@ -0,0 +1,70 @@
import { Array as FxArray, Context, Effect, HashMap, Layer, Option, pipe } from "effect";
import type { NonEmptyReadonlyArray } from "effect/Array";
import type { NoSuchElementError } from "effect/Cause";
import { getAllSelectorFromDocument, getFirstSelectorFromDocument } from "../../scripts-effect/lib/dom.ts";
import {
ATTRIBUT_ARIA_CONTROLS,
DOM_BOUTON_AJOUT_PANIER,
DOM_BOUTONS_ACCORDEON,
DOM_CONTENUS_ACCORDEON,
DOM_PRIX_PRODUIT,
} from "../constantes/dom.ts";
import type { DetailEnsemble } from "./types.d.ts";
class ProductPageElements extends Context.Service<
ProductPageElements,
{
AddToCartButton: HTMLButtonElement;
Details: HashMap.HashMap<string, DetailEnsemble>;
DetailsButtons: NonEmptyReadonlyArray<HTMLButtonElement>;
DetailsContents: NonEmptyReadonlyArray<HTMLDivElement>;
ProductPrice: HTMLParagraphElement;
ProductRawJson: HTMLScriptElement;
VariationChoiceForm: HTMLFormElement;
VariationSelectors: ReadonlyArray<HTMLSelectElement>;
}
>()("haikuatelier.fr/Produit/ProductPageElements") {
static readonly layer = Layer.effect(
ProductPageElements,
Effect.gen(function*() {
const AddToCartButton = yield* getFirstSelectorFromDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER);
const DetailsButtons = yield* getAllSelectorFromDocument<HTMLButtonElement>(DOM_BOUTONS_ACCORDEON);
const DetailsContents = yield* getAllSelectorFromDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON);
const ProductPrice = yield* getFirstSelectorFromDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT);
const ProductRawJson = yield* getFirstSelectorFromDocument<HTMLScriptElement>("#product-json");
const VariationChoiceForm = yield* getFirstSelectorFromDocument<HTMLFormElement>("#variation-choice");
const VariationSelectors = yield* pipe(
getAllSelectorFromDocument<HTMLSelectElement>(".selecteur-produit select"),
Option.orElseSome(() => FxArray.empty<HTMLSelectElement>()),
);
const Details = yield* pipe(
DetailsButtons,
FxArray.map(
(button: HTMLButtonElement, index: number): Effect.Effect<[string, DetailEnsemble], NoSuchElementError> =>
Effect.gen(function*() {
const contentId = yield* Option.fromNullishOr(button.getAttribute(ATTRIBUT_ARIA_CONTROLS));
const content = yield* FxArray.get(DetailsContents, index);
return [contentId, { button, content } satisfies DetailEnsemble];
}),
),
Effect.all,
Effect.map(HashMap.fromIterable<string, DetailEnsemble>),
);
return ProductPageElements.of({
AddToCartButton,
Details,
DetailsButtons,
DetailsContents,
ProductPrice,
ProductRawJson,
VariationChoiceForm,
VariationSelectors,
});
}),
);
}
export default ProductPageElements;

View file

@ -0,0 +1,6 @@
/** Représente un ensemble bouton-contenu d'une Section dans la description du Produit. */
type DetailEnsemble = {
button: HTMLButtonElement;
content: HTMLDivElement;
};
export { DetailEnsemble };

View file

@ -1,10 +1,8 @@
// Scripts pour la Page Produit // Scripts pour la Page Produit
import { pipe } from "@mobily/ts-belt"; import { Console, Effect } from "effect";
import { Console, Effect, pipe as epipe } from "effect"; import ProductPageRuntime from "./page-produit/runtime.ts";
import ProductPageDOM from "./page-produit/service-dom.ts";
import { ProductPageRuntime } from "./scripts-page-produit-service.ts";
/** États utiles pour les scripts de la page. */ /** États utiles pour les scripts de la page. */
type EtatsPage = { type EtatsPage = {
@ -17,31 +15,6 @@ type EtatsPage = {
// @ts-expect-error -- États injectés par le modèle PHP // @ts-expect-error -- États injectés par le modèle PHP
const ETATS_PAGE: EtatsPage = _etats; const ETATS_PAGE: EtatsPage = _etats;
const areArraysEqual = <T>(array1: Array<T>, array2: Array<T>): boolean => {
if (array1 !== array2) {
const a1 = JSON.stringify(array1.toSorted());
const a2 = JSON.stringify(array2.toSorted());
return a1 === a2;
}
return true;
};
const updatePriceOnAttributeChange = (): void => {
E.VARIATION_CHOICE_FORM.addEventListener("change", (): void => {
if (E.VARIATION_CHOICE_FORM.checkValidity() === false) {
return;
}
const productVariations: Array<unknown> = epipe(E.PRODUCT_JSON.textContent, JSON.parse)?.variations;
const chosenAttributes = getAttributesFromDom();
const chosenVariation = productVariations.find(v => areArraysEqual(v.attributes, chosenAttributes));
const newPrice: string = chosenVariation.price;
E.PRIX_PRODUIT.textContent = `${newPrice}`;
});
};
// const ajouteProduitAuPanier = (event: MouseEvent): void => { // const ajouteProduitAuPanier = (event: MouseEvent): void => {
// event.preventDefault(); // event.preventDefault();
// console.debug("getAttributeValuesFromDom", getAttributesFromDom()); // console.debug("getAttributeValuesFromDom", getAttributesFromDom());
@ -131,8 +104,24 @@ const updatePriceOnAttributeChange = (): void => {
// }; // };
document.addEventListener("DOMContentLoaded", (): void => { document.addEventListener("DOMContentLoaded", (): void => {
ProductPageRuntime.runFork(pipe(initAddToCartButton(), Effect.tapCause(Console.error))); console.debug("oups");
ProductPageRuntime.runFork(pipe(initAddToCartInteractionUpdates(), Effect.tapCause(Console.error))); Effect.gen(function*() {
ProductPageRuntime.runFork(pipe(initDetailInteractions(), Effect.tapCause(Console.error))); const DOM = yield* ProductPageDOM;
updatePriceOnAttributeChange(); console.debug("oups");
const effects = Effect.all(
[
DOM.initAddToCartButtonInitialState(),
DOM.initAddToCartButtonUpdates(),
DOM.initDetailInteractions(),
DOM.initPriceUpdatesOnVariationChange(),
DOM.initAddToCartButtonClicks(),
],
{
concurrency: "unbounded",
},
);
yield* effects.pipe(Effect.tapCause(Console.error));
}).pipe(ProductPageRuntime.runFork);
}); });

View file

@ -12,7 +12,7 @@ use Illuminate\Support\Str;
use Timber\Timber; use Timber\Timber;
if (!defined('ABSPATH')) { if (!defined('ABSPATH')) {
exit(); exit();
} }
// Initialise Timber // Initialise Timber
@ -30,13 +30,13 @@ $commande = $order;
$date = new Carbon($commande->get_date_created()); $date = new Carbon($commande->get_date_created());
$email = [ $email = [
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()], 'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
'livraison' => [ 'livraison' => [
'transporteur' => Str::of($commande->get_shipping_method())->replace(' (Free)', ''), 'transporteur' => Str::of($commande->get_shipping_method())->replace(' (Free)', ''),
'numero_suivi' => blank($commande->get_meta('tracking_number')) 'numero_suivi' => blank($commande->get_meta('tracking_number'))
? 'UNKNOWN_TRACKING_NUMBER' ? 'UNKNOWN_TRACKING_NUMBER'
: $commande->get_meta('tracking_number'), : $commande->get_meta('tracking_number'),
], ],
]; ];
$context['commande'] = $email; $context['commande'] = $email;

View file

@ -13,7 +13,7 @@ use Illuminate\Support\Str;
use Timber\Timber; use Timber\Timber;
if (!defined('ABSPATH')) { if (!defined('ABSPATH')) {
exit(); exit();
} }
// Initialise Timber // Initialise Timber
@ -31,49 +31,44 @@ $commande = $order;
$date = new Carbon($commande->get_date_created()); $date = new Carbon($commande->get_date_created());
$email = [ $email = [
'adresses' => [ 'adresses' => ['facturation' => $commande->get_address('billing'), 'livraison' => $commande->get_address('shipping')],
'facturation' => $commande->get_address('billing'), 'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
'livraison' => $commande->get_address('shipping'), 'livraison' => [
], 'methode' => $commande->get_shipping_method(),
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()], 'numero_suivi' => $commande->get_meta('tracking_number'),
'livraison' => [ ],
'methode' => $commande->get_shipping_method(), 'paiement' => ['methode' => ''],
'numero_suivi' => $commande->get_meta('tracking_number'), 'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
], $produit = $article->get_product();
'paiement' => ['methode' => ''],
'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
$produit = $article->get_product();
if (is_bool($produit) || $produit === null) { if (is_bool($produit) || $produit === null) {
return []; return [];
} }
return [ return [
// Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide // Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide
'attribut' => $produit->is_type('variable') 'attribut' => $produit->is_type('variable')
? collect($produit->get_attributes()) ? collect($produit->get_attributes())
->mapWithKeys(static fn($_atr, $cle): array => [ ->mapWithKeys(static fn($_atr, $cle): array => [
'nom' => Str::lower(wc_attribute_label($cle, $produit)), 'nom' => Str::lower(wc_attribute_label($cle, $produit)),
'valeur' => $produit->get_attribute($cle), 'valeur' => $produit->get_attribute($cle),
]) ])
->toArray() ->toArray()
: [], : [],
'lien' => $produit->get_permalink(), 'lien' => $produit->get_permalink(),
'nom' => $produit->get_title(), 'nom' => $produit->get_title(),
'prix_total' => $article->get_total(), 'prix_total' => $article->get_total(),
'quantite' => $article->get_quantity(), 'quantite' => $article->get_quantity(),
]; ];
}), }),
'totaux' => [ 'totaux' => [
'sous_total_livraison' => '0' === $commande->get_shipping_total() 'sous_total_livraison' => '0' === $commande->get_shipping_total() ? 'Free' : $commande->get_shipping_total() . '€',
? 'Free' 'sous_total_produits' => $commande->get_subtotal() . '€',
: $commande->get_shipping_total() . '€', 'sous_total_reduction' => '0.00' === $commande->get_discount_total()
'sous_total_produits' => $commande->get_subtotal() . '€', ? '0'
'sous_total_reduction' => '0.00' === $commande->get_discount_total() : Number::format((float) $commande->get_discount_total(), maxPrecision: 2) . '€',
? '0' 'total' => Number::format((float) $commande->get_total(), maxPrecision: 2) . '€',
: Number::format((float) $commande->get_discount_total(), maxPrecision: 2) . '€', ],
'total' => Number::format((float) $commande->get_total(), maxPrecision: 2) . '€',
],
]; ];
// Transforme les codes de pays en noms de pays // Transforme les codes de pays en noms de pays
$email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()]; $email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()];

View file

@ -13,7 +13,7 @@ use Illuminate\Support\Str;
use Timber\Timber; use Timber\Timber;
if (!defined('ABSPATH')) { if (!defined('ABSPATH')) {
exit(); exit();
} }
// Initialise Timber // Initialise Timber
@ -31,45 +31,40 @@ $commande = $order;
$date = new Carbon($commande->get_date_created()); $date = new Carbon($commande->get_date_created());
$email = [ $email = [
'adresses' => [ 'adresses' => ['facturation' => $commande->get_address('billing'), 'livraison' => $commande->get_address('shipping')],
'facturation' => $commande->get_address('billing'), 'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
'livraison' => $commande->get_address('shipping'), 'paiement' => ['methode' => ''],
], 'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()], $produit = $article->get_product();
'paiement' => ['methode' => ''],
'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
$produit = $article->get_product();
if (is_bool($produit) || $produit === null) { if (is_bool($produit) || $produit === null) {
return []; return [];
} }
return [ return [
// Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide // Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide
'attribut' => $article->is_type('variable') 'attribut' => $article->is_type('variable')
? collect($produit->get_attributes()) ? collect($produit->get_attributes())
->mapWithKeys(static fn($_atr, $cle): array => [ ->mapWithKeys(static fn($_atr, $cle): array => [
'nom' => Str::lower(wc_attribute_label($cle, $produit)), 'nom' => Str::lower(wc_attribute_label($cle, $produit)),
'valeur' => $produit->get_attribute($cle), 'valeur' => $produit->get_attribute($cle),
]) ])
->toArray() ->toArray()
: [], : [],
'lien' => $produit->get_permalink(), 'lien' => $produit->get_permalink(),
'nom' => $produit->get_title(), 'nom' => $produit->get_title(),
'prix_total' => $article->get_total(), 'prix_total' => $article->get_total(),
'quantite' => $article->get_quantity(), 'quantite' => $article->get_quantity(),
]; ];
}), }),
'totaux' => [ 'totaux' => [
'sous_total_livraison' => '0' === $commande->get_shipping_total() 'sous_total_livraison' => '0' === $commande->get_shipping_total() ? 'Free' : $commande->get_shipping_total() . '€',
? 'Free' 'sous_total_produits' => $commande->get_subtotal() . '€',
: $commande->get_shipping_total() . '€', 'sous_total_reduction' => '0.00' === $commande->get_discount_total()
'sous_total_produits' => $commande->get_subtotal() . '€', ? '0'
'sous_total_reduction' => '0.00' === $commande->get_discount_total() : Number::format((float) $commande->get_discount_total(), maxPrecision: 2) . '€',
? '0' 'total' => Number::format((float) $commande->get_total(), maxPrecision: 2) . '€',
: Number::format((float) $commande->get_discount_total(), maxPrecision: 2) . '€', ],
'total' => Number::format((float) $commande->get_total(), maxPrecision: 2) . '€',
],
]; ];
// Transforme les codes de pays en noms de pays // Transforme les codes de pays en noms de pays
$email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()]; $email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()];